Skip to content

Commit

Permalink
New KeyOperation, KeyOperationPolicy and builder concepts (#814)
Browse files Browse the repository at this point in the history
* Added new KeyOperation and KeyOperationBuilder concepts
Changed Jwk#getOperations and JwkBuilder#operations from methods that accepted and returned Strings to use new KeyOperation instances
Added Jwks.OP#builder() method to create a KeyOperationBuilder

* Changed Jwks.OP#WRAP to WRAP_KEY and UNWRAP to UNWRAP_KEY to match RFC names

* Added new KeyOperationPolicy and KeyOperationPolicyBuilder concepts
Added Jwks.OP#policy() builder method to create a KeyOperationPolicyBuilder
Added JwkBuilder#operationPolicy and JwkParserBuilder#operationPolicy methods for configuring custom KeyOperationPolicy instances during JWK building and parsing, respectively.
  • Loading branch information
lhazlewood authored Sep 8, 2023
1 parent a6792d9 commit 847ad13
Show file tree
Hide file tree
Showing 36 changed files with 1,612 additions and 180 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package io.jsonwebtoken.security;

import java.security.Key;
import java.util.Set;
import java.util.Collection;

/**
* A {@link JwkBuilder} that builds asymmetric (public or private) JWKs.
Expand Down Expand Up @@ -69,7 +69,7 @@ public interface AsymmetricJwkBuilder<K extends Key, J extends AsymmetricJwk<K>,
*
* <p>Per
* <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">JWK RFC 7517, Section 4.3, last paragraph</a>,
* the {@code use} (Public Key Use) and {@link #operations(Set) key_ops (Key Operations)} members
* the <code>use (Public Key Use)</code> and {@link #operations(Collection) key_ops (Key Operations)} members
* <em>SHOULD NOT</em> be used together; however, if both are used, the information they convey <em>MUST</em> be
* consistent. Applications should specify which of these members they use, if either is to be used by the
* application.</p>
Expand Down
71 changes: 11 additions & 60 deletions api/src/main/java/io/jsonwebtoken/security/Jwk.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,69 +95,15 @@ public interface Jwk<K extends Key> extends Identifiable, Map<String, Object> {
String getAlgorithm();

/**
* Returns the JWK
* <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">{@code key_ops} (Key Operations)
* parameter</a> values or {@code null} if not present. Any values within the returned {@code Set} are
* CaSe-SeNsItIvE.
*
* <p>The JWK specification <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">defines</a> the
* following values:</p>
*
* <table>
* <caption>JWK Key Operations</caption>
* <thead>
* <tr>
* <th>Value</th>
* <th>Operation</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td><b>{@code sign}</b></td>
* <td>compute digital signatures or MAC</td>
* </tr>
* <tr>
* <td><b>{@code verify}</b></td>
* <td>verify digital signatures or MAC</td>
* </tr>
* <tr>
* <td><b>{@code encrypt}</b></td>
* <td>encrypt content</td>
* </tr>
* <tr>
* <td><b>{@code decrypt}</b></td>
* <td>decrypt content and validate decryption, if applicable</td>
* </tr>
* <tr>
* <td><b>{@code wrapKey}</b></td>
* <td>encrypt key</td>
* </tr>
* <tr>
* <td><b>{@code unwrapKey}</b></td>
* <td>decrypt key and validate decryption, if applicable</td>
* </tr>
* <tr>
* <td><b>{@code deriveKey}</b></td>
* <td>derive key</td>
* </tr>
* <tr>
* <td><b>{@code deriveBits}</b></td>
* <td>derive bits not to be used as a key</td>
* </tr>
* </tbody>
* </table>
*
* <p>Other values <em>MAY</em> be used. For best interoperability with other applications however, it is
* recommended to use only the values above.</p>
*
* <p>Multiple unrelated key operations <em>SHOULD NOT</em> be specified for a key because of the potential
* vulnerabilities associated with using the same key with multiple algorithms. Thus, the combinations
* {@code sign} with {@code verify}, {@code encrypt} with {@code decrypt}, and {@code wrapKey} with
* {@code unwrapKey} are permitted, but other combinations <em>SHOULD NOT</em> be used.</p>
* Returns the JWK <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">{@code key_ops}
* (Key Operations) parameter</a> values or {@code null} if not present. All JWK standard Key Operations are
* available via the {@link Jwks.OP} registry, but other (custom) values <em>MAY</em> be present in the returned
* set.
*
* @return the JWK {@code key_ops} value or {@code null} if not present.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3"><code>key_ops</code>(Key Operations) Parameter</a>
*/
Set<String> getOperations();
Set<KeyOperation> getOperations();

/**
* Returns the required JWK
Expand Down Expand Up @@ -188,6 +134,11 @@ public interface Jwk<K extends Key> extends Identifiable, Map<String, Object> {
* <td><b>{@code oct}</b></td>
* <td>Octet sequence (used to represent symmetric keys)</td>
* </tr>
* <tr>
* <td><b>{@code OKP}</b></td>
* <td><a href="https://www.rfc-editor.org/rfc/rfc8037#section-2">Octet Key Pair</a> (used to represent Edwards
* Elliptic Curve keys)</td>
* </tr>
* </tbody>
* </table>
*
Expand Down
154 changes: 95 additions & 59 deletions api/src/main/java/io/jsonwebtoken/security/JwkBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import io.jsonwebtoken.lang.MapMutator;

import java.security.Key;
import java.util.Set;
import java.util.Collection;

/**
* A {@link SecurityBuilder} that produces a JWK. A JWK is an immutable set of name/value pairs that represent a
Expand Down Expand Up @@ -103,6 +103,47 @@ public interface JwkBuilder<K extends Key, J extends Jwk<K>, T extends JwkBuilde
*/
T idFromThumbprint(HashAlgorithm alg);

/**
* Specifies an operation for which the key may be used by adding it to the
* JWK <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">{@code key_ops} (Key Operations)
* Parameter</a> values. This method may be called multiple times.
*
* <p>The {@code key_ops} (key operations) parameter identifies the operation(s) for which the key is
* intended to be used. The {@code key_ops} parameter is intended for use cases in which public,
* private, or symmetric keys may be present.</p>
*
* <p><b>Security Vulnerability Notice</b></p>
*
* <p>Multiple unrelated key operations <em>SHOULD NOT</em> be specified for a key because of the potential
* vulnerabilities associated with using the same key with multiple algorithms. Thus, the combinations
* {@link Jwks.OP#SIGN sign} with {@link Jwks.OP#VERIFY verify},
* {@link Jwks.OP#ENCRYPT encrypt} with {@link Jwks.OP#DECRYPT decrypt}, and
* {@link Jwks.OP#WRAP_KEY wrapKey} with {@link Jwks.OP#UNWRAP_KEY unwrapKey} are permitted, but other combinations
* <em>SHOULD NOT</em> be used. This is enforced by the builder's key operation
* {@link #operationPolicy(KeyOperationPolicy) policy}.</p>
*
* <p><b>Standard {@code KeyOperation}s and Overrides</b></p>
*
* <p>All RFC-standard JWK Key Operations in the {@link Jwks.OP} registry are supported via the builder's default
* operations {@link #operationPolicy(KeyOperationPolicy) policy}, but other (custom) values
* <em>MAY</em> be specified (for example, using a {@link Jwks.OP#builder()}).</p>
*
* <p>If the {@code JwkBuilder} is being used to rebuild or parse an existing JWK however, any custom operations
* should be enabled for the {@code JwkBuilder} by {@link #operationPolicy(KeyOperationPolicy) specifying}
* an operations policy that includes the custom values (e.g. via
* {@link Jwks.OP#policy()}.{@link KeyOperationPolicyBuilder#add(KeyOperation) add(customKeyOperation)}).</p>
*
* <p>For best interoperability with other applications however, it is recommended to use only the {@link Jwks.OP}
* constants.</p>
*
* @param operation the value to add to the JWK {@code key_ops} value set
* @return the builder for method chaining.
* @throws IllegalArgumentException if {@code op} is {@code null} or if the operation is not permitted
* by the operations {@link #operationPolicy(KeyOperationPolicy) policy}.
* @see Jwks.OP
*/
T operation(KeyOperation operation) throws IllegalArgumentException;

/**
* Sets the JWK <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">{@code key_ops}
* (Key Operations) Parameter</a> values.
Expand All @@ -111,68 +152,63 @@ public interface JwkBuilder<K extends Key, J extends Jwk<K>, T extends JwkBuilde
* intended to be used. The {@code key_ops} parameter is intended for use cases in which public,
* private, or symmetric keys may be present.</p>
*
* <p>The JWK specification <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">defines</a> the
* following values:</p>
*
* <table>
* <caption>JWK Key Operations</caption>
* <thead>
* <tr>
* <th>Value</th>
* <th>Operation</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td><b>{@code sign}</b></td>
* <td>compute digital signatures or MAC</td>
* </tr>
* <tr>
* <td><b>{@code verify}</b></td>
* <td>verify digital signatures or MAC</td>
* </tr>
* <tr>
* <td><b>{@code encrypt}</b></td>
* <td>encrypt content</td>
* </tr>
* <tr>
* <td><b>{@code decrypt}</b></td>
* <td>decrypt content and validate decryption, if applicable</td>
* </tr>
* <tr>
* <td><b>{@code wrapKey}</b></td>
* <td>encrypt key</td>
* </tr>
* <tr>
* <td><b>{@code unwrapKey}</b></td>
* <td>decrypt key and validate decryption, if applicable</td>
* </tr>
* <tr>
* <td><b>{@code deriveKey}</b></td>
* <td>derive key</td>
* </tr>
* <tr>
* <td><b>{@code deriveBits}</b></td>
* <td>derive bits not to be used as a key</td>
* </tr>
* </tbody>
* </table>
*
* <p>(Note that {@code key_ops} values intentionally match the {@code KeyUsage} values defined in the
* <a href="https://www.w3.org/TR/WebCryptoAPI/">Web Cryptography API</a> specification.)</p>
*
* <p>Other values <em>MAY</em> be used. For best interoperability with other applications however, it is
* recommended to use only the values above. Each value is a CaSe-SeNsItIvE string. Use of the
* {@code key_ops} member is <em>OPTIONAL</em>, unless the application requires its presence.</p>
* <p><b>Security Vulnerability Notice</b></p>
*
* <p>Multiple unrelated key operations <em>SHOULD NOT</em> be specified for a key because of the potential
* vulnerabilities associated with using the same key with multiple algorithms. Thus, the combinations
* {@code sign} with {@code verify}, {@code encrypt} with {@code decrypt}, and {@code wrapKey} with
* {@code unwrapKey} are permitted, but other combinations <em>SHOULD NOT</em> be used.</p>
* {@link Jwks.OP#SIGN sign} with {@link Jwks.OP#VERIFY verify},
* {@link Jwks.OP#ENCRYPT encrypt} with {@link Jwks.OP#DECRYPT decrypt}, and
* {@link Jwks.OP#WRAP_KEY wrapKey} with {@link Jwks.OP#UNWRAP_KEY unwrapKey} are permitted, but other combinations
* <em>SHOULD NOT</em> be used. This is enforced by the builder's default
* operation {@link #operationPolicy(KeyOperationPolicy) policy}.</p>
*
* <p><b>Standard {@code KeyOperation}s and Overrides</b></p>
*
* <p>All RFC-standard JWK Key Operations in the {@link Jwks.OP} registry are supported via the builder's default
* operations {@link #operationPolicy(KeyOperationPolicy) policy}, but other (custom) values
* <em>MAY</em> be specified (for example, using a {@link Jwks.OP#builder()}).</p>
*
* <p>If the {@code JwkBuilder} is being used to rebuild or parse an existing JWK however, any custom operations
* should be enabled for the {@code JwkBuilder} by {@link #operationPolicy(KeyOperationPolicy) specifying}
* an operations policy that includes the custom values (e.g. via
* {@link Jwks.OP#policy()}.{@link KeyOperationPolicyBuilder#add(KeyOperation) add(customKeyOperation)}).</p>
*
* <p>For best interoperability with other applications however, it is recommended to use only the {@link Jwks.OP}
* constants.</p>
*
* @param ops the JWK {@code key_ops} value set, or {@code null} if not present.
* @return the builder for method chaining.
* @throws IllegalArgumentException {@code ops} is {@code null} or empty, or if any of the operations are not
* permitted by the operations {@link #operationPolicy(KeyOperationPolicy) policy}.
* @see Jwks.OP
*/
T operations(Collection<KeyOperation> ops) throws IllegalArgumentException;

/**
* Sets the builder's {@link KeyOperationPolicy} that determines which key
* {@link #operations(Collection) operations} may be assigned to the JWK. Unless overridden by this method, the
* builder uses the default RFC-recommended policy where:
* <ul>
* <li>All {@link Jwks.OP RFC-standard key operations} are supported.</li>
* <li>Multiple unrelated operations may not be assigned to the JWK per the
* <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">RFC 7517, Section 4.3</a> recommendation:
* <blockquote><pre>
* Multiple unrelated key operations SHOULD NOT be specified for a key
* because of the potential vulnerabilities associated with using the
* same key with multiple algorithms.
* </pre></blockquote></li>
* </ul>
*
* <p>If you wish to enable a different policy, perhaps to support additional custom {@code KeyOperation} values,
* one can be created by using the {@link Jwks.OP#policy()} builder, or by implementing the
* {@link KeyOperationPolicy} interface directly.</p>
*
* @param ops the JWK {@code key_ops} value set.
* @param policy the policy to apply during JWK construction
* @return the builder for method chaining.
* @throws IllegalArgumentException if {@code ops} is {@code null} or empty.
* @throws IllegalArgumentException if the specified policy is null, or the policy's
* {@link KeyOperationPolicy#getOperations() operations} collection is null or
* empty.
* @see Jwks.OP#policy()
*/
T operations(Set<String> ops) throws IllegalArgumentException;
T operationPolicy(KeyOperationPolicy policy) throws IllegalArgumentException;
}
24 changes: 24 additions & 0 deletions api/src/main/java/io/jsonwebtoken/security/JwkParserBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,28 @@ public interface JwkParserBuilder extends Builder<JwkParser> {
*/
JwkParserBuilder deserializeJsonWith(Deserializer<Map<String, ?>> deserializer);

/**
* Sets the parser's key operation policy that determines which {@link KeyOperation}s may be assigned to parsed
* JWKs. Unless overridden by this method, the parser uses the default RFC-recommended policy where:
* <ul>
* <li>All {@link Jwks.OP RFC-standard key operations} are supported.</li>
* <li>Multiple unrelated operations may <b>not</b> be assigned to the JWK per the
* <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3">RFC 7517, Section 4.3</a> recommendation:
* <blockquote><pre>
* Multiple unrelated key operations SHOULD NOT be specified for a key
* because of the potential vulnerabilities associated with using the
* same key with multiple algorithms.
* </pre></blockquote></li>
* </ul>
*
* <p>If you wish to enable a different policy, perhaps to support additional custom {@code KeyOperation} values,
* one can be created by using the {@link Jwks.OP#policy()} builder, or by implementing the
* {@link KeyOperationPolicy} interface directly.</p>
*
* @param policy the policy to use to determine which {@link KeyOperation}s may be assigned to parsed JWKs.
* @return the builder for method chaining.
* @throws IllegalArgumentException if {@code policy} is null
*/
JwkParserBuilder operationPolicy(KeyOperationPolicy policy) throws IllegalArgumentException;

}
Loading

0 comments on commit 847ad13

Please sign in to comment.