mirror of https://github.com/jwtk/jjwt.git
Refactoring and testing cont'd
This commit is contained in:
parent
f95b024d90
commit
bfdaa754ac
|
@ -40,13 +40,13 @@ import java.util.Map;
|
|||
public interface Header<T extends Header<T>> extends Map<String,Object> {
|
||||
|
||||
/** JWT {@code Type} (typ) value: <code>"JWT"</code> */
|
||||
public static final String JWT_TYPE = "JWT";
|
||||
String JWT_TYPE = "JWT";
|
||||
|
||||
/** JWT {@code Type} header parameter name: <code>"typ"</code> */
|
||||
public static final String TYPE = "typ";
|
||||
String TYPE = "typ";
|
||||
|
||||
/** JWT {@code Content Type} header parameter name: <code>"cty"</code> */
|
||||
public static final String CONTENT_TYPE = "cty";
|
||||
String CONTENT_TYPE = "cty";
|
||||
|
||||
/**
|
||||
* JWT {@code Algorithm} header parameter name: <code>"alg"</code>.
|
||||
|
@ -54,15 +54,15 @@ public interface Header<T extends Header<T>> extends Map<String,Object> {
|
|||
* @see <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1">JWS Algorithm Header</a>
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7516#section-4.1.1">JWE Algorithm Header</a>
|
||||
*/
|
||||
public static final String ALGORITHM = "alg";
|
||||
String ALGORITHM = "alg";
|
||||
|
||||
/** JWT {@code Compression Algorithm} header parameter name: <code>"zip"</code> */
|
||||
public static final String COMPRESSION_ALGORITHM = "zip";
|
||||
String COMPRESSION_ALGORITHM = "zip";
|
||||
|
||||
/** JJWT legacy/deprecated compression algorithm header parameter name: <code>"calg"</code>
|
||||
* @deprecated use {@link #COMPRESSION_ALGORITHM} instead. */
|
||||
@Deprecated
|
||||
public static final String DEPRECATED_COMPRESSION_ALGORITHM = "calg";
|
||||
String DEPRECATED_COMPRESSION_ALGORITHM = "calg";
|
||||
|
||||
/**
|
||||
* Returns the <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-5.1">
|
||||
|
@ -123,13 +123,15 @@ public interface Header<T extends Header<T>> extends Map<String,Object> {
|
|||
* <li>If the JWT is a Signed JWT (a JWS), the <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1">
|
||||
* <code>alg</code></a> (Algorithm) header parameter identifies the cryptographic algorithm used to secure the
|
||||
* JWS. Consider using
|
||||
* {@link io.jsonwebtoken.security.SignatureAlgorithms#forName(String) SignatureAlgorithms.forName} to
|
||||
* convert this string value to a type-safe enum instance.</li>
|
||||
* {@link io.jsonwebtoken.security.SignatureAlgorithms#findById(String) SignatureAlgorithms.findById} to
|
||||
* convert this string value to a type-safe SignatureAlgorithm instance.</li>
|
||||
* <li>If the JWT is an Encrypted JWT (a JWE), the
|
||||
* <a href="https://tools.ietf.org/html/rfc7516#section-4.1.1"><code>alg</code></a> (Algorithm) header parameter
|
||||
* identifies the cryptographic key management algorithm used to encrypt or determine the value of the Content
|
||||
* Encryption Key (CEK). The encrypted content is not usable if the <code>alg</code> value does not represent a
|
||||
* supported algorithm, or if the recipient does not have a key that can be used with that algorithm</li>
|
||||
* supported algorithm, or if the recipient does not have a key that can be used with that algorithm. Consider
|
||||
* using {@link io.jsonwebtoken.security.KeyAlgorithms#findById(String) KeyAlgorithms.findById} to convert this
|
||||
* string value to a type-safe KeyAlgorithm instance.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the {@code alg} header value or {@code null} if not present. This will always be
|
||||
|
@ -144,14 +146,12 @@ public interface Header<T extends Header<T>> extends Map<String,Object> {
|
|||
* <ul>
|
||||
* <li>If the JWT is a Signed JWT (a JWS), the <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1">
|
||||
* <code>alg</code></a> (Algorithm) header parameter identifies the cryptographic algorithm used to secure the
|
||||
* JWS. Consider using
|
||||
* {@link io.jsonwebtoken.security.SignatureAlgorithms#forName(String) SignatureAlgorithms.forName} to
|
||||
* convert this string value to a type-safe enum instance.</li>
|
||||
* JWS.</li>
|
||||
* <li>If the JWT is an Encrypted JWT (a JWE), the
|
||||
* <a href="https://tools.ietf.org/html/rfc7516#section-4.1.1"><code>alg</code></a> (Algorithm) header parameter
|
||||
* identifies the cryptographic key management algorithm used to encrypt or determine the value of the Content
|
||||
* Encryption Key (CEK). The encrypted content is not usable if the <code>alg</code> value does not represent a
|
||||
* supported algorithm, or if the recipient does not have a key that can be used with that algorithm</li>
|
||||
* supported algorithm, or if the recipient does not have a key that can be used with that algorithm.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param alg the {@code alg} header value
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package io.jsonwebtoken.security;
|
||||
package io.jsonwebtoken;
|
||||
|
||||
/**
|
||||
* @since JJWT_RELEASE_VERSION
|
|
@ -25,47 +25,47 @@ public interface JwsHeader extends Header<JwsHeader> {
|
|||
/**
|
||||
* JWS <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1">Algorithm Header</a> name: the string literal <b><code>alg</code></b>
|
||||
*/
|
||||
public static final String ALGORITHM = "alg";
|
||||
String ALGORITHM = "alg";
|
||||
|
||||
/**
|
||||
* JWS <a href="https://tools.ietf.org/html/rfc7515#section-4.1.2">JWK Set URL Header</a> name: the string literal <b><code>jku</code></b>
|
||||
*/
|
||||
public static final String JWK_SET_URL = "jku";
|
||||
String JWK_SET_URL = "jku";
|
||||
|
||||
/**
|
||||
* JWS <a href="https://tools.ietf.org/html/rfc7515#section-4.1.3">JSON Web Key Header</a> name: the string literal <b><code>jwk</code></b>
|
||||
*/
|
||||
public static final String JSON_WEB_KEY = "jwk";
|
||||
String JSON_WEB_KEY = "jwk";
|
||||
|
||||
/**
|
||||
* JWS <a href="https://tools.ietf.org/html/rfc7516#section-4.1.4">Key ID Header</a> name: the string literal <b><code>kid</code></b>
|
||||
*/
|
||||
public static final String KEY_ID = "kid";
|
||||
String KEY_ID = "kid";
|
||||
|
||||
/**
|
||||
* JWS <a href="https://tools.ietf.org/html/rfc7516#section-4.1.5">X.509 URL Header</a> name: the string literal <b><code>x5u</code></b>
|
||||
*/
|
||||
public static final String X509_URL = "x5u";
|
||||
String X509_URL = "x5u";
|
||||
|
||||
/**
|
||||
* JWS <a href="https://tools.ietf.org/html/rfc7516#section-4.1.6">X.509 Certificate Chain Header</a> name: the string literal <b><code>x5c</code></b>
|
||||
*/
|
||||
public static final String X509_CERT_CHAIN = "x5c";
|
||||
String X509_CERT_CHAIN = "x5c";
|
||||
|
||||
/**
|
||||
* JWS <a href="https://tools.ietf.org/html/rfc7516#section-4.1.7">X.509 Certificate SHA-1 Thumbprint Header</a> name: the string literal <b><code>x5t</code></b>
|
||||
*/
|
||||
public static final String X509_CERT_SHA1_THUMBPRINT = "x5t";
|
||||
String X509_CERT_SHA1_THUMBPRINT = "x5t";
|
||||
|
||||
/**
|
||||
* JWS <a href="https://tools.ietf.org/html/rfc7516#section-4.1.8">X.509 Certificate SHA-256 Thumbprint Header</a> name: the string literal <b><code>x5t#S256</code></b>
|
||||
*/
|
||||
public static final String X509_CERT_SHA256_THUMBPRINT = "x5t#S256";
|
||||
String X509_CERT_SHA256_THUMBPRINT = "x5t#S256";
|
||||
|
||||
/**
|
||||
* JWS <a href="https://tools.ietf.org/html/rfc7516#section-4.1.11">Critical Header</a> name: the string literal <b><code>crit</code></b>
|
||||
*/
|
||||
public static final String CRITICAL = "crit";
|
||||
String CRITICAL = "crit";
|
||||
|
||||
/**
|
||||
* Returns the JWS <a href="https://tools.ietf.org/html/rfc7516#section-4.1.4">
|
||||
|
|
|
@ -22,7 +22,7 @@ package io.jsonwebtoken;
|
|||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
public interface Jwt<H extends Header, B> {
|
||||
public interface Jwt<H extends Header<H>, B> {
|
||||
|
||||
/**
|
||||
* Returns the JWT {@link Header} or {@code null} if not present.
|
||||
|
|
|
@ -31,7 +31,7 @@ public interface JwtHandler<T> {
|
|||
* @param jwt the parsed plaintext JWT
|
||||
* @return any object to be used after inspecting the JWT, or {@code null} if no return value is necessary.
|
||||
*/
|
||||
T onPlaintextJwt(Jwt<Header, String> jwt);
|
||||
T onPlaintextJwt(Jwt<?, String> jwt);
|
||||
|
||||
/**
|
||||
* This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is
|
||||
|
@ -40,7 +40,7 @@ public interface JwtHandler<T> {
|
|||
* @param jwt the parsed claims JWT
|
||||
* @return any object to be used after inspecting the JWT, or {@code null} if no return value is necessary.
|
||||
*/
|
||||
T onClaimsJwt(Jwt<Header, Claims> jwt);
|
||||
T onClaimsJwt(Jwt<?, Claims> jwt);
|
||||
|
||||
/**
|
||||
* This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is
|
||||
|
|
|
@ -31,12 +31,12 @@ package io.jsonwebtoken;
|
|||
public class JwtHandlerAdapter<T> implements JwtHandler<T> {
|
||||
|
||||
@Override
|
||||
public T onPlaintextJwt(Jwt<Header, String> jwt) {
|
||||
public T onPlaintextJwt(Jwt<?, String> jwt) {
|
||||
throw new UnsupportedJwtException("Unsigned plaintext JWTs are not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public T onClaimsJwt(Jwt<Header, Claims> jwt) {
|
||||
public T onClaimsJwt(Jwt<?, Claims> jwt) {
|
||||
throw new UnsupportedJwtException("Unsigned Claims JWTs are not supported.");
|
||||
}
|
||||
|
||||
|
|
|
@ -319,7 +319,8 @@ public interface JwtParser {
|
|||
* immutable JwtParser.
|
||||
* <p><b>NOTE: this method will be removed before version 1.0</b>
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||
@Deprecated // TODO: remove for 1.0
|
||||
JwtParser setSigningKeyResolver(SigningKeyResolver signingKeyResolver);
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,11 +17,13 @@ package io.jsonwebtoken;
|
|||
|
||||
import io.jsonwebtoken.io.Decoder;
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.security.KeyResolver;
|
||||
import io.jsonwebtoken.security.KeyAlgorithm;
|
||||
import io.jsonwebtoken.security.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.SymmetricAeadAlgorithm;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -54,7 +56,7 @@ public interface JwtParserBuilder {
|
|||
* value does not equal the specified value, an exception will be thrown indicating that the
|
||||
* JWT is invalid and may not be used.
|
||||
*
|
||||
* @param id
|
||||
* @param id {@code jti} value
|
||||
* @return the parser builder for method chaining.
|
||||
* @see MissingClaimException
|
||||
* @see IncorrectClaimException
|
||||
|
@ -66,7 +68,7 @@ public interface JwtParserBuilder {
|
|||
* value does not equal the specified value, an exception will be thrown indicating that the
|
||||
* JWT is invalid and may not be used.
|
||||
*
|
||||
* @param subject
|
||||
* @param subject the required subject value
|
||||
* @return the parser builder for method chaining.
|
||||
* @see MissingClaimException
|
||||
* @see IncorrectClaimException
|
||||
|
@ -78,7 +80,7 @@ public interface JwtParserBuilder {
|
|||
* value does not equal the specified value, an exception will be thrown indicating that the
|
||||
* JWT is invalid and may not be used.
|
||||
*
|
||||
* @param audience
|
||||
* @param audience the required audience value
|
||||
* @return the parser builder for method chaining.
|
||||
* @see MissingClaimException
|
||||
* @see IncorrectClaimException
|
||||
|
@ -90,7 +92,7 @@ public interface JwtParserBuilder {
|
|||
* value does not equal the specified value, an exception will be thrown indicating that the
|
||||
* JWT is invalid and may not be used.
|
||||
*
|
||||
* @param issuer
|
||||
* @param issuer the required issuer value
|
||||
* @return the parser builder for method chaining.
|
||||
* @see MissingClaimException
|
||||
* @see IncorrectClaimException
|
||||
|
@ -102,7 +104,7 @@ public interface JwtParserBuilder {
|
|||
* value does not equal the specified value, an exception will be thrown indicating that the
|
||||
* JWT is invalid and may not be used.
|
||||
*
|
||||
* @param issuedAt
|
||||
* @param issuedAt the required issuedAt value
|
||||
* @return the parser builder for method chaining.
|
||||
* @see MissingClaimException
|
||||
* @see IncorrectClaimException
|
||||
|
@ -114,7 +116,7 @@ public interface JwtParserBuilder {
|
|||
* value does not equal the specified value, an exception will be thrown indicating that the
|
||||
* JWT is invalid and may not be used.
|
||||
*
|
||||
* @param expiration
|
||||
* @param expiration the required expiration value
|
||||
* @return the parser builder for method chaining.
|
||||
* @see MissingClaimException
|
||||
* @see IncorrectClaimException
|
||||
|
@ -126,7 +128,7 @@ public interface JwtParserBuilder {
|
|||
* value does not equal the specified value, an exception will be thrown indicating that the
|
||||
* JWT is invalid and may not be used.
|
||||
*
|
||||
* @param notBefore
|
||||
* @param notBefore the required not before {@code nbf} value.
|
||||
* @return the parser builder for method chaining
|
||||
* @see MissingClaimException
|
||||
* @see IncorrectClaimException
|
||||
|
@ -138,8 +140,8 @@ public interface JwtParserBuilder {
|
|||
* value does not equal the specified value, an exception will be thrown indicating that the
|
||||
* JWT is invalid and may not be used.
|
||||
*
|
||||
* @param claimName
|
||||
* @param value
|
||||
* @param claimName the name of the claim to require
|
||||
* @param value the value the claim value must equal
|
||||
* @return the parser builder for method chaining.
|
||||
* @see MissingClaimException
|
||||
* @see IncorrectClaimException
|
||||
|
@ -234,13 +236,13 @@ public interface JwtParserBuilder {
|
|||
* <p/>
|
||||
* <p>If there is any chance that the parser will encounter JWSs
|
||||
* that need different signature verification keys based on the JWS being parsed, it is strongly
|
||||
* recommended to configure your own {@link KeyResolver} via the
|
||||
* {@link #setKeyResolver(KeyResolver) setKeyResolver} method instead of using this one.</p>
|
||||
* recommended to configure your own {@link Locator Locator<?,Key>} via the
|
||||
* {@link #setKeyLocator(Locator) setKeyLocator} method instead of using this one.</p>
|
||||
* <p/>
|
||||
* <p>Calling this method overrides any previously set signature verification key.</p>
|
||||
*
|
||||
* @param key the algorithm-specific signature verification key to use to verify all encountered JWS digital
|
||||
* signature.
|
||||
* signatures.
|
||||
* @return the parser builder for method chaining.
|
||||
*/
|
||||
JwtParserBuilder setSigningKey(Key key);
|
||||
|
@ -256,7 +258,7 @@ public interface JwtParserBuilder {
|
|||
* <p/>
|
||||
* <p>If there is any chance that the parser will encounter JWEs
|
||||
* that need different decryption keys based on the JWE being parsed, it is strongly recommended to configure
|
||||
* your own {@link KeyResolver} via the {@link #setKeyResolver(KeyResolver) setKeyResolver} method instead of
|
||||
* your own {@link Locator Locator<?,Key>} via the {@link #setKeyLocator(Locator) setKeyLocator} method instead of
|
||||
* using this one.</p>
|
||||
* <p/>
|
||||
* <p>Calling this method overrides any previously set decryption key.</p>
|
||||
|
@ -266,22 +268,22 @@ public interface JwtParserBuilder {
|
|||
JwtParserBuilder decryptWith(Key key);
|
||||
|
||||
/**
|
||||
* Sets the {@link KeyResolver} used to acquire any signature verification or decryption key needed during parsing.
|
||||
* Sets the {@link Locator} used to acquire any signature verification or decryption key needed during parsing.
|
||||
* <ul>
|
||||
* <li>If the parsed String is a JWS, the {@code KeyResolver} will be called to find the appropriate key
|
||||
* <li>If the parsed String is a JWS, the {@code Locator} will be called to find the appropriate key
|
||||
* necessary to verify the JWS signature.</li>
|
||||
* <li>If the parsed String is a JWE, it will be called to find the appropriate decryption key.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <p>Specifying a {@code KeyResolver} is necessary when the signing or decryption key is not already known before
|
||||
* <p>Specifying a key {@code Locator} is necessary when the signing or decryption key is not already known before
|
||||
* parsing the JWT and the JWT header must be inspected first to determine how to
|
||||
* look up the verification or decryption key. Once returned by the resolver, the JwtParser will then either
|
||||
* look up the verification or decryption key. Once returned by the locator, the JwtParser will then either
|
||||
* verify the JWS signature or decrypt the JWE payload with the returned key. For example:</p>
|
||||
* <p>
|
||||
* <pre>
|
||||
* Jws<Claims> jws = Jwts.parser().setKeyResolver(new KeyResolver() {
|
||||
* Jws<Claims> jws = Jwts.parser().setKeyLocator(new Locator<Header,Key>() {
|
||||
* @Override
|
||||
* public Key resolveKey(Header header) {
|
||||
* public Key locate(Header header) {
|
||||
* if (header instanceof JwsHeader) {
|
||||
* return getSignatureVerificationKey((JwsHeader)header); // implement me
|
||||
* } else {
|
||||
|
@ -293,13 +295,19 @@ public interface JwtParserBuilder {
|
|||
* <p>
|
||||
* <p>A {@code KeyResolver} is invoked once during parsing before performing decryption or signature verification.</p>
|
||||
*
|
||||
* @param keyResolver the key resolver used to retrieve decryption or signature verification keys.
|
||||
* @param keyLocator the locator used to retrieve decryption or signature verification keys.
|
||||
* @return the parser builder for method chaining.
|
||||
* @since JJWT_RELEASE_VERSION
|
||||
*/
|
||||
JwtParserBuilder setKeyResolver(KeyResolver keyResolver);
|
||||
JwtParserBuilder setKeyLocator(Locator<? extends Header<?>, Key> keyLocator);
|
||||
|
||||
/**
|
||||
* <h4>Deprecation Notice</h4>
|
||||
* <p>This method has been deprecated as of JJWT version JJWT_RELEASE_VERSION because it only supports key location
|
||||
* for JWSs (signed JWTs) instead of both signed (JWS) and encrypted (JWE) scenarios. Use the
|
||||
* {@link #setKeyLocator(Locator) setKeyLocator} method instead to ensure a locator that can work for both JWS and
|
||||
* JWE inputs. This method will be removed for the 1.0 release.</p>
|
||||
* <h4>Previous Documentation</h4>
|
||||
* Sets the {@link SigningKeyResolver} used to acquire the <code>signing key</code> that should be used to verify
|
||||
* a JWS's signature. If the parsed String is not a JWS (no signature), this resolver is not used.
|
||||
* <p>
|
||||
|
@ -323,11 +331,20 @@ public interface JwtParserBuilder {
|
|||
* <p>This method should only be used if a signing key is not provided by the other {@code setSigningKey*} builder
|
||||
* methods.</p>
|
||||
*
|
||||
* @deprecated since JJWT_RELEASE_VERSION
|
||||
* @param signingKeyResolver the signing key resolver used to retrieve the signing key.
|
||||
* @return the parser builder for method chaining.
|
||||
*/
|
||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||
@Deprecated
|
||||
JwtParserBuilder setSigningKeyResolver(SigningKeyResolver signingKeyResolver);
|
||||
|
||||
JwtParserBuilder addEncryptionAlgorithms(Collection<SymmetricAeadAlgorithm> encAlgs);
|
||||
|
||||
JwtParserBuilder addSignatureAlgorithms(Collection<SignatureAlgorithm<?,?>> sigAlgs);
|
||||
|
||||
JwtParserBuilder addKeyAlgorithms(Collection<KeyAlgorithm<?,?>> keyAlgs);
|
||||
|
||||
/**
|
||||
* Sets the {@link CompressionCodecResolver} used to acquire the {@link CompressionCodec} that should be used to
|
||||
* decompress the JWT body. If the parsed JWT is not compressed, this resolver is not used.
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package io.jsonwebtoken;
|
||||
|
||||
public interface Locator<H extends Header<H>, R> {
|
||||
|
||||
R locate(H header);
|
||||
}
|
|
@ -44,11 +44,11 @@ import java.security.Key;
|
|||
* the {@link io.jsonwebtoken.SigningKeyResolverAdapter} and overriding only the method you need to support instead of
|
||||
* implementing this interface directly.</p>
|
||||
*
|
||||
* @see io.jsonwebtoken.SigningKeyResolverAdapter
|
||||
* @since 0.4
|
||||
* @deprecated since JJWT_RELEASE_VERSION. Implement {@link io.jsonwebtoken.security.KeyResolver KeyResolver} instead.
|
||||
* @see io.jsonwebtoken.security.KeyResolver
|
||||
* @deprecated since JJWT_RELEASE_VERSION. Implement {@link io.jsonwebtoken.Locator Locator<?, Key>} instead.
|
||||
* @see io.jsonwebtoken.JwtParserBuilder#setKeyLocator(Locator)
|
||||
*/
|
||||
@Deprecated
|
||||
public interface SigningKeyResolver {
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,12 +2,8 @@ package io.jsonwebtoken.security;
|
|||
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
import io.jsonwebtoken.lang.Maps;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @since JJWT_RELEASE_VERSION
|
||||
|
@ -18,12 +14,30 @@ public final class EncryptionAlgorithms {
|
|||
private EncryptionAlgorithms() {
|
||||
}
|
||||
|
||||
private static final String HMAC = "io.jsonwebtoken.impl.security.HmacAesAeadAlgorithm";
|
||||
private static final String GCM = "io.jsonwebtoken.impl.security.GcmAesAeadAlgorithm";
|
||||
private static final Class<?>[] CTOR_ARG_TYPES = new Class[]{int.class};
|
||||
private static final String BRIDGE_CLASSNAME = "io.jsonwebtoken.impl.security.EncryptionAlgorithmsBridge";
|
||||
private static final Class<?>[] ID_ARG_TYPES = new Class[]{String.class};
|
||||
|
||||
private static SymmetricAeadAlgorithm alg(String fqcn, int keyLength) {
|
||||
return Classes.newInstance(fqcn, CTOR_ARG_TYPES, keyLength);
|
||||
public static Collection<SymmetricAeadAlgorithm> values() {
|
||||
return Classes.invokeStatic(BRIDGE_CLASSNAME, "values", null, (Object[]) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JWE Encryption Algorithm with the specified
|
||||
* <a href="https://datatracker.ietf.org/doc/html/rfc7518#section-5.1">{@code enc} algorithm identifier</a> or
|
||||
* {@code null} if an algorithm for the specified {@code id} cannot be found.
|
||||
*
|
||||
* @param id a JWE standard {@code enc} algorithm identifier
|
||||
* @return the associated Encryption Algorithm instance or {@code null} otherwise.
|
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc7518#section-5.1">RFC 7518, Section 5.1</a>
|
||||
*/
|
||||
public static SymmetricAeadAlgorithm findById(String id) {
|
||||
Assert.hasText(id, "id cannot be null or empty.");
|
||||
return Classes.invokeStatic(BRIDGE_CLASSNAME, "findById", ID_ARG_TYPES, id);
|
||||
}
|
||||
|
||||
private static SymmetricAeadAlgorithm forId(String id) {
|
||||
Assert.hasText(id, "id cannot be null or empty.");
|
||||
return Classes.invokeStatic(BRIDGE_CLASSNAME, "forId", ID_ARG_TYPES, id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,64 +45,40 @@ public final class EncryptionAlgorithms {
|
|||
* <a href="https://tools.ietf.org/html/rfc7518#section-5.2.3">RFC 7518, Section 5.2.3</a>. This algorithm
|
||||
* requires a 256 bit (32 byte) key.
|
||||
*/
|
||||
public static final SymmetricAeadAlgorithm A128CBC_HS256 = alg(HMAC, 128);
|
||||
public static final SymmetricAeadAlgorithm A128CBC_HS256 = forId("A128CBC-HS256");
|
||||
|
||||
/**
|
||||
* AES_192_CBC_HMAC_SHA_384 authenticated encryption algorithm, as defined by
|
||||
* <a href="https://tools.ietf.org/html/rfc7518#section-5.2.4">RFC 7518, Section 5.2.4</a>. This algorithm
|
||||
* requires a 384 bit (48 byte) key.
|
||||
*/
|
||||
public static final SymmetricAeadAlgorithm A192CBC_HS384 = alg(HMAC, 192);
|
||||
public static final SymmetricAeadAlgorithm A192CBC_HS384 = forId("A192CBC-HS384");
|
||||
|
||||
/**
|
||||
* AES_256_CBC_HMAC_SHA_512 authenticated encryption algorithm, as defined by
|
||||
* <a href="https://tools.ietf.org/html/rfc7518#section-5.2.5">RFC 7518, Section 5.2.5</a>. This algorithm
|
||||
* requires a 512 bit (64 byte) key.
|
||||
*/
|
||||
public static final SymmetricAeadAlgorithm A256CBC_HS512 = alg(HMAC, 256);
|
||||
public static final SymmetricAeadAlgorithm A256CBC_HS512 = forId("A256CBC-HS512");
|
||||
|
||||
/**
|
||||
* "AES GCM using 128-bit key" as defined by
|
||||
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a>. This algorithm requires
|
||||
* a 128 bit (16 byte) key.
|
||||
*/
|
||||
public static final SymmetricAeadAlgorithm A128GCM = alg(GCM, 128);
|
||||
public static final SymmetricAeadAlgorithm A128GCM = forId("A128GCM");
|
||||
|
||||
/**
|
||||
* "AES GCM using 192-bit key" as defined by
|
||||
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a>. This algorithm requires
|
||||
* a 192 bit (24 byte) key.
|
||||
*/
|
||||
public static final SymmetricAeadAlgorithm A192GCM = alg(GCM, 192);
|
||||
public static final SymmetricAeadAlgorithm A192GCM = forId("A192GCM");
|
||||
|
||||
/**
|
||||
* "AES GCM using 256-bit key" as defined by
|
||||
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a>. This algorithm requires
|
||||
* a 256 bit (32 byte) key.
|
||||
*/
|
||||
public static final SymmetricAeadAlgorithm A256GCM = alg(GCM, 256);
|
||||
|
||||
private static final Map<String, SymmetricAeadAlgorithm> SYMMETRIC_VALUES_BY_NAME = Collections.unmodifiableMap(Maps
|
||||
.of(A128CBC_HS256.getId(), A128CBC_HS256)
|
||||
.and(A192CBC_HS384.getId(), A192CBC_HS384)
|
||||
.and(A256CBC_HS512.getId(), A256CBC_HS512)
|
||||
.and(A128GCM.getId(), A128GCM)
|
||||
.and(A192GCM.getId(), A192GCM)
|
||||
.and(A256GCM.getId(), A256GCM)
|
||||
.build());
|
||||
|
||||
public static SymmetricAeadAlgorithm forName(String name) {
|
||||
Assert.hasText(name, "name cannot be null or empty.");
|
||||
SymmetricAeadAlgorithm alg = SYMMETRIC_VALUES_BY_NAME.get(name.toUpperCase());
|
||||
if (alg == null) {
|
||||
String msg = "'" + name + "' is not a JWE specification standard name. The standard names are: " +
|
||||
Strings.collectionToCommaDelimitedString(SYMMETRIC_VALUES_BY_NAME.keySet());
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
return alg;
|
||||
}
|
||||
|
||||
public static Collection<SymmetricAeadAlgorithm> values() {
|
||||
return SYMMETRIC_VALUES_BY_NAME.values();
|
||||
}
|
||||
public static final SymmetricAeadAlgorithm A256GCM = forId("A256GCM");
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
|
||||
public interface HashAlgorithm extends Identifiable {
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
|
||||
import java.security.Key;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.security.Key;
|
||||
|
||||
|
|
|
@ -7,9 +7,6 @@ import javax.crypto.SecretKey;
|
|||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @since JJWT_RELEASE_VERSION
|
||||
|
@ -20,62 +17,45 @@ public final class KeyAlgorithms {
|
|||
private KeyAlgorithms() {
|
||||
}
|
||||
|
||||
private static final String BRIDGE_CLASSNAME = "io.jsonwebtoken.impl.security.KeyAlgorithms";
|
||||
private static final String BRIDGE_CLASSNAME = "io.jsonwebtoken.impl.security.KeyAlgorithmsBridge";
|
||||
private static final Class<?>[] ID_ARG_TYPES = new Class[]{String.class};
|
||||
|
||||
private static <T> T lookup(String methodName) {
|
||||
return Classes.invokeStatic(BRIDGE_CLASSNAME, methodName, null, (Object[]) null);
|
||||
}
|
||||
|
||||
public static final KeyAlgorithm<SecretKey, SecretKey> DIRECT = lookup("direct");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A128KW = lookup("a128kw");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A192KW = lookup("a192kw");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A256KW = lookup("a256kw");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A128GCMKW = lookup("a128gcmkw");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A192GCMKW = lookup("a192gcmkw");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A256GCMKW = lookup("a256gcmkw");
|
||||
public static final EncryptedKeyAlgorithm<RSAPublicKey, RSAPrivateKey> RSA1_5 = lookup("rsa1_5");
|
||||
public static final EncryptedKeyAlgorithm<RSAPublicKey, RSAPrivateKey> RSA_OAEP = lookup("rsaOaep");
|
||||
public static final EncryptedKeyAlgorithm<RSAPublicKey, RSAPrivateKey> RSA_OAEP_256 = lookup("rsaOaep256");
|
||||
|
||||
private static Map<String,KeyAlgorithm<?,?>> toMap(KeyAlgorithm<?,?>... algs) {
|
||||
Map<String, KeyAlgorithm<?,?>> m = new LinkedHashMap<>();
|
||||
for (KeyAlgorithm<?,?> alg : algs) {
|
||||
m.put(alg.getId(), alg);
|
||||
}
|
||||
return Collections.unmodifiableMap(m);
|
||||
}
|
||||
|
||||
private static final Map<String,KeyAlgorithm<?,?>> STANDARD_ALGORITHMS = toMap(
|
||||
DIRECT, A128KW, A192KW, A256KW, A128GCMKW, A192GCMKW, A256GCMKW, RSA1_5, RSA_OAEP, RSA_OAEP_256
|
||||
);
|
||||
|
||||
public static Collection<? extends KeyAlgorithm<?,?>> values() {
|
||||
return STANDARD_ALGORITHMS.values();
|
||||
public static Collection<SymmetricAeadAlgorithm> values() {
|
||||
return Classes.invokeStatic(BRIDGE_CLASSNAME, "values", null, (Object[]) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up and returns the corresponding JWA standard {@code KeyAlgorithm} instance based on a
|
||||
* case-<em>insensitive</em> name comparison.
|
||||
* Returns the JWE KeyAlgorithm with the specified
|
||||
* <a href="https://datatracker.ietf.org/doc/html/rfc7518#section-4.1">{@code alg} key algorithm identifier</a> or
|
||||
* {@code null} if an algorithm for the specified {@code id} cannot be found.
|
||||
*
|
||||
* @param id The case-insensitive identifier of the JWA standard {@code KeyAlgorithm} instance to return
|
||||
* @return the corresponding JWA standard {@code KeyAlgorithm} enum instance based on a
|
||||
* case-<em>insensitive</em> name comparison.
|
||||
* @throws SignatureException if the specified value does not match any JWA standard {@code KeyAlgorithm} name.
|
||||
* @param id a JWE standard {@code alg} key algorithm identifier
|
||||
* @return the associated KeyAlgorithm instance or {@code null} otherwise.
|
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc7518#section-4.1">RFC 7518, Section 4.1</a>
|
||||
*/
|
||||
public static KeyAlgorithm<?,?> forName(String id) {
|
||||
Assert.hasText(id, "id argument cannot be null or empty.");
|
||||
//try constant time lookup first. This will satisfy 99% of invocations:
|
||||
KeyAlgorithm<?,?> alg = STANDARD_ALGORITHMS.get(id);
|
||||
if (alg != null) {
|
||||
return alg;
|
||||
public static KeyAlgorithm<?, ?> findById(String id) {
|
||||
Assert.hasText(id, "id cannot be null or empty.");
|
||||
return Classes.invokeStatic(BRIDGE_CLASSNAME, "findById", ID_ARG_TYPES, id);
|
||||
}
|
||||
//fall back to case-insensitive lookup:
|
||||
for (KeyAlgorithm<?,?> kalg : STANDARD_ALGORITHMS.values()) {
|
||||
if (id.equalsIgnoreCase(kalg.getId())) {
|
||||
return kalg;
|
||||
|
||||
public static KeyAlgorithm<?, ?> forId(String id) {
|
||||
return forId0(id);
|
||||
}
|
||||
|
||||
// do not change this visibility. Raw type method signature not be publicly exposed
|
||||
private static <T> T forId0(String id) {
|
||||
Assert.hasText(id, "id cannot be null or empty.");
|
||||
return Classes.invokeStatic(BRIDGE_CLASSNAME, "forId", ID_ARG_TYPES, id);
|
||||
}
|
||||
// still no result - error:
|
||||
throw new IllegalArgumentException("Unrecognized key algorithm id '" + id + "'");
|
||||
}
|
||||
|
||||
public static final KeyAlgorithm<SecretKey, SecretKey> DIRECT = forId0("dir");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A128KW = forId0("A128KW");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A192KW = forId0("A192KW");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A256KW = forId0("A256KW");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A128GCMKW = forId0("A128GCMKW");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A192GCMKW = forId0("A192GCMKW");
|
||||
public static final EncryptedKeyAlgorithm<SecretKey, SecretKey> A256GCMKW = forId0("A256GCMKW");
|
||||
public static final EncryptedKeyAlgorithm<RSAPublicKey, RSAPrivateKey> RSA1_5 = forId0("RSA1_5");
|
||||
public static final EncryptedKeyAlgorithm<RSAPublicKey, RSAPrivateKey> RSA_OAEP = forId0("RSA-OAEP");
|
||||
public static final EncryptedKeyAlgorithm<RSAPublicKey, RSAPrivateKey> RSA_OAEP_256 = forId0("RSA-OAEP-256");
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ public final class Keys {
|
|||
@Deprecated
|
||||
public static SecretKey secretKeyFor(io.jsonwebtoken.SignatureAlgorithm alg) throws IllegalArgumentException {
|
||||
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
|
||||
SignatureAlgorithm<?,?> salg = SignatureAlgorithms.forName(alg.name());
|
||||
SignatureAlgorithm<?,?> salg = SignatureAlgorithms.forId(alg.name());
|
||||
if (!(salg instanceof SecretKeySignatureAlgorithm)) {
|
||||
String msg = "The " + alg.name() + " algorithm does not support shared secret keys.";
|
||||
throw new IllegalArgumentException(msg);
|
||||
|
@ -210,7 +210,7 @@ public final class Keys {
|
|||
@Deprecated
|
||||
public static KeyPair keyPairFor(io.jsonwebtoken.SignatureAlgorithm alg) throws IllegalArgumentException {
|
||||
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
|
||||
SignatureAlgorithm<?,?> salg = SignatureAlgorithms.forName(alg.name());
|
||||
SignatureAlgorithm<?,?> salg = SignatureAlgorithms.forId(alg.name());
|
||||
if (!(salg instanceof AsymmetricKeySignatureAlgorithm)) {
|
||||
String msg = "The " + alg.name() + " algorithm does not support Key Pairs.";
|
||||
throw new IllegalArgumentException(msg);
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.JweHeader;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.Locator;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
public abstract class LocatorAdapter<H extends Header<H>, R> implements Locator<H, R> {
|
||||
|
||||
@Override
|
||||
public final R locate(H header) {
|
||||
Assert.notNull(header, "Header cannot be null.");
|
||||
if (header instanceof JwsHeader) {
|
||||
return locate((JwsHeader) header);
|
||||
} else if (header instanceof JweHeader) {
|
||||
return locate((JweHeader) header);
|
||||
}
|
||||
String msg = "Unrecognized header type: " + header.getClass().getName();
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
protected R locate(JweHeader header) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected R locate(JwsHeader header) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
|
||||
import java.security.Key;
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,9 +9,6 @@ import java.security.PrivateKey;
|
|||
import java.security.interfaces.ECKey;
|
||||
import java.security.interfaces.RSAKey;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @since JJWT_RELEASE_VERSION
|
||||
|
@ -23,89 +20,40 @@ public final class SignatureAlgorithms {
|
|||
private SignatureAlgorithms() {
|
||||
}
|
||||
|
||||
private static final String HMAC = "io.jsonwebtoken.impl.security.MacSignatureAlgorithm";
|
||||
private static final Class<?>[] HMAC_ARGS = new Class[]{String.class, String.class, int.class};
|
||||
private static final String BRIDGE_CLASSNAME = "io.jsonwebtoken.impl.security.SignatureAlgorithmsBridge";
|
||||
private static final Class<?>[] ID_ARG_TYPES = new Class[]{String.class};
|
||||
|
||||
private static final String RSA = "io.jsonwebtoken.impl.security.DefaultRsaSignatureAlgorithm";
|
||||
private static final Class<?>[] RSA_ARGS = new Class[]{String.class, String.class, int.class};
|
||||
private static final Class<?>[] PSS_ARGS = new Class[]{String.class, String.class, int.class, int.class};
|
||||
|
||||
private static final String EC = "io.jsonwebtoken.impl.security.DefaultEllipticCurveSignatureAlgorithm";
|
||||
private static final Class<?>[] EC_ARGS = new Class[]{String.class, String.class, String.class, int.class, int.class};
|
||||
|
||||
private static SecretKeySignatureAlgorithm hmacSha(int minKeyLength) {
|
||||
return Classes.newInstance(HMAC, HMAC_ARGS, "HS" + minKeyLength, "HmacSHA" + minKeyLength, minKeyLength);
|
||||
public static Collection<SignatureAlgorithm<?,?>> values() {
|
||||
return Classes.invokeStatic(BRIDGE_CLASSNAME, "values", null, (Object[]) null);
|
||||
}
|
||||
|
||||
private static RsaSignatureAlgorithm rsa(int digestLength, int preferredKeyLength) {
|
||||
return Classes.newInstance(RSA, RSA_ARGS, "RS" + digestLength, "SHA" + digestLength + "withRSA", preferredKeyLength);
|
||||
public static SignatureAlgorithm<?, ?> findById(String id) {
|
||||
Assert.hasText(id, "id cannot be null or empty.");
|
||||
return Classes.invokeStatic(BRIDGE_CLASSNAME, "findById", ID_ARG_TYPES, id);
|
||||
}
|
||||
|
||||
private static RsaSignatureAlgorithm pss(int digestLength, int preferredKeyLength) {
|
||||
return Classes.newInstance(RSA, PSS_ARGS, "PS" + digestLength, "RSASSA-PSS", preferredKeyLength, digestLength);
|
||||
public static SignatureAlgorithm<?,?> forId(String id) {
|
||||
return forId0(id);
|
||||
}
|
||||
|
||||
private static EllipticCurveSignatureAlgorithm ec(int keySize, int signatureLength) {
|
||||
int shaSize = keySize == 521 ? 512 : keySize;
|
||||
return Classes.newInstance(EC, EC_ARGS, "ES" + shaSize, "SHA" + shaSize + "withECDSA", "secp" + keySize + "r1", keySize, signatureLength);
|
||||
static <T> T forId0(String id) {
|
||||
Assert.hasText(id, "id cannot be null or empty.");
|
||||
return Classes.invokeStatic(BRIDGE_CLASSNAME, "forId", ID_ARG_TYPES, id);
|
||||
}
|
||||
|
||||
public static final SignatureAlgorithm<Key, Key> NONE = Classes.newInstance("io.jsonwebtoken.impl.security.NoneSignatureAlgorithm");
|
||||
public static final SecretKeySignatureAlgorithm HS256 = hmacSha(256);
|
||||
public static final SecretKeySignatureAlgorithm HS384 = hmacSha(384);
|
||||
public static final SecretKeySignatureAlgorithm HS512 = hmacSha(512);
|
||||
public static final RsaSignatureAlgorithm RS256 = rsa(256, 2048);
|
||||
public static final RsaSignatureAlgorithm RS384 = rsa(384, 3072);
|
||||
public static final RsaSignatureAlgorithm RS512 = rsa(512, 4096);
|
||||
public static final RsaSignatureAlgorithm PS256 = pss(256, 2048);
|
||||
public static final RsaSignatureAlgorithm PS384 = pss(384, 3072);
|
||||
public static final RsaSignatureAlgorithm PS512 = pss(512, 4096);
|
||||
public static final EllipticCurveSignatureAlgorithm ES256 = ec(256, 64);
|
||||
public static final EllipticCurveSignatureAlgorithm ES384 = ec(384, 96);
|
||||
public static final EllipticCurveSignatureAlgorithm ES512 = ec(521, 132);
|
||||
|
||||
private static Map<String, SignatureAlgorithm<?,?>> toMap(SignatureAlgorithm<?,?>... algs) {
|
||||
Map<String, SignatureAlgorithm<?,?>> m = new LinkedHashMap<>();
|
||||
for (SignatureAlgorithm<?,?> alg : algs) {
|
||||
m.put(alg.getId(), alg);
|
||||
}
|
||||
return Collections.unmodifiableMap(m);
|
||||
}
|
||||
|
||||
private static final Map<String, SignatureAlgorithm<?,?>> STANDARD_ALGORITHMS = toMap(
|
||||
NONE, HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512
|
||||
);
|
||||
|
||||
public static Collection<? extends SignatureAlgorithm<?,?>> values() {
|
||||
return STANDARD_ALGORITHMS.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up and returns the corresponding JWA standard {@code SignatureAlgorithm} instance based on a
|
||||
* case-<em>insensitive</em> name comparison.
|
||||
*
|
||||
* @param name The case-insensitive name of the JWA standard {@code SignatureAlgorithm} instance to return
|
||||
* @return the corresponding JWA standard {@code SignatureAlgorithm} enum instance based on a
|
||||
* case-<em>insensitive</em> name comparison.
|
||||
* @throws SignatureException if the specified value does not match any JWA standard {@code SignatureAlgorithm}
|
||||
* name.
|
||||
*/
|
||||
public static SignatureAlgorithm<?,?> forName(String name) {
|
||||
Assert.notNull(name, "name argument cannot be null.");
|
||||
//try constant time lookup first. This will satisfy 99% of invocations:
|
||||
SignatureAlgorithm alg = STANDARD_ALGORITHMS.get(name);
|
||||
if (alg != null) {
|
||||
return alg;
|
||||
}
|
||||
//fall back to case-insensitive lookup:
|
||||
for (SignatureAlgorithm salg : STANDARD_ALGORITHMS.values()) {
|
||||
if (name.equalsIgnoreCase(salg.getId())) {
|
||||
return salg;
|
||||
}
|
||||
}
|
||||
// still no result - error:
|
||||
throw new SignatureException("Unsupported signature algorithm '" + name + "'");
|
||||
}
|
||||
public static final SignatureAlgorithm<Key, Key> NONE = forId0("none");
|
||||
public static final SecretKeySignatureAlgorithm HS256 = forId0("HS256");
|
||||
public static final SecretKeySignatureAlgorithm HS384 = forId0("HS384");
|
||||
public static final SecretKeySignatureAlgorithm HS512 = forId0("HS512");
|
||||
public static final RsaSignatureAlgorithm RS256 = forId0("RS256");
|
||||
public static final RsaSignatureAlgorithm RS384 = forId0("RS384");
|
||||
public static final RsaSignatureAlgorithm RS512 = forId0("RS512");
|
||||
public static final RsaSignatureAlgorithm PS256 = forId0("PS256");
|
||||
public static final RsaSignatureAlgorithm PS384 = forId0("PS384");
|
||||
public static final RsaSignatureAlgorithm PS512 = forId0("PS512");
|
||||
public static final EllipticCurveSignatureAlgorithm ES256 = forId0("ES256");
|
||||
public static final EllipticCurveSignatureAlgorithm ES384 = forId0("ES384");
|
||||
public static final EllipticCurveSignatureAlgorithm ES512 = forId0("ES512");
|
||||
|
||||
/**
|
||||
* Returns the recommended signature algorithm to be used with the specified key according to the following
|
||||
|
@ -217,9 +165,9 @@ public final class SignatureAlgorithms {
|
|||
* @throws InvalidKeyException for any key that does not match the heuristics and requirements documented above,
|
||||
* since that inevitably means the Key is either insufficient or explicitly disallowed by the JWT specification.
|
||||
*/
|
||||
public static SignatureAlgorithm<?,?> forSigningKey(Key key) {
|
||||
public static SignatureAlgorithm<?, ?> forSigningKey(Key key) {
|
||||
@SuppressWarnings("deprecation")
|
||||
io.jsonwebtoken.SignatureAlgorithm alg = io.jsonwebtoken.SignatureAlgorithm.forSigningKey(key);
|
||||
return forName(alg.getValue());
|
||||
return forId(alg.getValue());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
|
||||
/**
|
||||
* @since JJWT_RELEASE_VERSION
|
||||
*/
|
||||
|
|
|
@ -114,7 +114,7 @@ class KeysTest {
|
|||
def key = createMock(SecretKey)
|
||||
def salg = createMock(SecretKeySignatureAlgorithm)
|
||||
|
||||
expect(SignatureAlgorithms.forName(eq(name))).andReturn(salg)
|
||||
expect(SignatureAlgorithms.forId(eq(name))).andReturn(salg)
|
||||
expect(salg.generateKey()).andReturn(key)
|
||||
replay SignatureAlgorithms, salg, key
|
||||
|
||||
|
@ -125,7 +125,7 @@ class KeysTest {
|
|||
|
||||
} else {
|
||||
def salg = name == 'NONE' ? createMock(io.jsonwebtoken.security.SignatureAlgorithm) : createMock(AsymmetricKeySignatureAlgorithm)
|
||||
expect(SignatureAlgorithms.forName(eq(name))).andReturn(salg)
|
||||
expect(SignatureAlgorithms.forId(eq(name))).andReturn(salg)
|
||||
replay SignatureAlgorithms, salg
|
||||
try {
|
||||
Keys.secretKeyFor(alg)
|
||||
|
@ -149,7 +149,7 @@ class KeysTest {
|
|||
|
||||
if (name.equals('NONE') || name.startsWith('H')) {
|
||||
def salg = name == 'NONE' ? createMock(io.jsonwebtoken.security.SignatureAlgorithm) : createMock(SecretKeySignatureAlgorithm)
|
||||
expect(SignatureAlgorithms.forName(eq(name))).andReturn(salg)
|
||||
expect(SignatureAlgorithms.forId(eq(name))).andReturn(salg)
|
||||
replay SignatureAlgorithms, salg
|
||||
try {
|
||||
Keys.keyPairFor(alg)
|
||||
|
@ -163,7 +163,7 @@ class KeysTest {
|
|||
def pair = createMock(KeyPair)
|
||||
def salg = createMock(AsymmetricKeySignatureAlgorithm)
|
||||
|
||||
expect(SignatureAlgorithms.forName(eq(name))).andReturn(salg)
|
||||
expect(SignatureAlgorithms.forId(eq(name))).andReturn(salg)
|
||||
expect(salg.generateKeyPair()).andReturn(pair)
|
||||
replay SignatureAlgorithms, pair, salg
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package io.jsonwebtoken.impl;
|
||||
|
||||
import io.jsonwebtoken.CompressionCodec;
|
||||
import io.jsonwebtoken.CompressionCodecResolver;
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.impl.lang.Function;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
public class CompressionCodecLocator<H extends Header<H>> implements Function<H, CompressionCodec> {
|
||||
|
||||
private final CompressionCodecResolver resolver;
|
||||
|
||||
public CompressionCodecLocator(CompressionCodecResolver resolver) {
|
||||
this.resolver = Assert.notNull(resolver, "CompressionCodecResolver cannot be null.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompressionCodec apply(H header) {
|
||||
return resolver.resolveCompressionCodec(header);
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ import io.jsonwebtoken.lang.Strings;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class DefaultHeader<T extends Header<T>> extends JwtMap implements Header<T> {
|
||||
|
||||
public DefaultHeader() {
|
||||
|
@ -31,6 +30,11 @@ public class DefaultHeader<T extends Header<T>> extends JwtMap implements Header
|
|||
super(map);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T tthis() {
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return getString(TYPE);
|
||||
|
@ -39,7 +43,7 @@ public class DefaultHeader<T extends Header<T>> extends JwtMap implements Header
|
|||
@Override
|
||||
public T setType(String typ) {
|
||||
setValue(TYPE, typ);
|
||||
return (T)this;
|
||||
return tthis();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -50,7 +54,7 @@ public class DefaultHeader<T extends Header<T>> extends JwtMap implements Header
|
|||
@Override
|
||||
public T setContentType(String cty) {
|
||||
setValue(CONTENT_TYPE, cty);
|
||||
return (T)this;
|
||||
return tthis();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,7 +65,7 @@ public class DefaultHeader<T extends Header<T>> extends JwtMap implements Header
|
|||
@Override
|
||||
public T setAlgorithm(String alg) {
|
||||
setValue(ALGORITHM, alg);
|
||||
return (T)this;
|
||||
return tthis();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
@ -78,7 +82,6 @@ public class DefaultHeader<T extends Header<T>> extends JwtMap implements Header
|
|||
@Override
|
||||
public T setCompressionAlgorithm(String compressionAlgorithm) {
|
||||
setValue(COMPRESSION_ALGORITHM, compressionAlgorithm);
|
||||
return (T) this;
|
||||
return tthis();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,8 +67,8 @@ public class DefaultJweBuilder extends DefaultJwtBuilder<JweBuilder> implements
|
|||
|
||||
@Override
|
||||
public JweBuilder encryptWith(final SymmetricAeadAlgorithm enc) {
|
||||
this.enc = Assert.notNull(enc, "EncryptionAlgorithm cannot be null.");
|
||||
Assert.hasText(enc.getId(), "EncryptionAlgorithm id cannot be null or empty.");
|
||||
this.enc = Assert.notNull(enc, "Encryption algorithm cannot be null.");
|
||||
Assert.hasText(enc.getId(), "Encryption algorithm id cannot be null or empty.");
|
||||
String encMsg = enc.getId() + " encryption failed.";
|
||||
this.encFunction = wrap(encMsg, new Function<SymmetricAeadRequest, AeadResult>() {
|
||||
@Override
|
||||
|
@ -115,7 +115,7 @@ public class DefaultJweBuilder extends DefaultJwtBuilder<JweBuilder> implements
|
|||
}
|
||||
|
||||
Assert.state(key != null, "Key is required.");
|
||||
Assert.state(enc != null, "EncryptionAlgorithm is required.");
|
||||
Assert.state(enc != null, "Encryption algorithm is required.");
|
||||
assert alg != null : "KeyAlgorithm is required."; //always set by withKey calling withKeyFrom
|
||||
|
||||
if (this.serializer == null) { // try to find one based on the services available
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package io.jsonwebtoken.impl;
|
||||
|
||||
import io.jsonwebtoken.Jwe;
|
||||
import io.jsonwebtoken.JwtException;
|
||||
|
||||
public class DefaultJweParser implements JweParser {
|
||||
|
||||
@Override
|
||||
public Jwe<?> parse(String jwt) throws JwtException {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import io.jsonwebtoken.impl.lang.Function;
|
|||
import io.jsonwebtoken.impl.lang.LegacyServices;
|
||||
import io.jsonwebtoken.impl.lang.PropagatingExceptionFunction;
|
||||
import io.jsonwebtoken.impl.security.DefaultSignatureRequest;
|
||||
import io.jsonwebtoken.impl.security.SignatureAlgorithmsBridge;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.Encoder;
|
||||
import io.jsonwebtoken.io.Encoders;
|
||||
|
@ -130,9 +131,7 @@ public class DefaultJwtBuilder<T extends JwtBuilder<T>> implements JwtBuilder<T>
|
|||
public T setHeaderParams(Map<String, Object> params) {
|
||||
if (!Collections.isEmpty(params)) {
|
||||
Header<?> header = ensureHeader();
|
||||
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||
header.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
header.putAll(params);
|
||||
}
|
||||
return (T)this;
|
||||
}
|
||||
|
@ -153,32 +152,34 @@ public class DefaultJwtBuilder<T extends JwtBuilder<T>> implements JwtBuilder<T>
|
|||
@Override
|
||||
public T signWith(Key key) throws InvalidKeyException {
|
||||
Assert.notNull(key, "Key argument cannot be null.");
|
||||
SignatureAlgorithm<?,?> alg = SignatureAlgorithms.forSigningKey(key);
|
||||
SignatureAlgorithm<Key,?> alg = (SignatureAlgorithm<Key,?>)SignatureAlgorithms.forSigningKey(key);
|
||||
return signWith(key, alg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T signWith(Key key, final SignatureAlgorithm alg) throws InvalidKeyException {
|
||||
public <K extends Key> T signWith(K key, final SignatureAlgorithm<K,?> alg) throws InvalidKeyException {
|
||||
Assert.notNull(key, "Key argument cannot be null.");
|
||||
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
|
||||
this.key = key;
|
||||
this.algorithm = alg;
|
||||
this.algorithm = (SignatureAlgorithm<Key,?>)alg;
|
||||
this.signFunction = new PropagatingExceptionFunction<>(SignatureException.class,
|
||||
"Unable to compute " + alg.getId() + " signature.", new Function<SignatureRequest<Key>, byte[]>() {
|
||||
@Override
|
||||
public byte[] apply(SignatureRequest<Key> request) {
|
||||
return alg.sign(request);
|
||||
return algorithm.sign(request);
|
||||
}
|
||||
});
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // TODO: remove method for 1.0
|
||||
@Override
|
||||
public T signWith(Key key, io.jsonwebtoken.SignatureAlgorithm alg) throws InvalidKeyException {
|
||||
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
|
||||
return signWith(key, SignatureAlgorithms.forName(alg.getValue()));
|
||||
return signWith(key, (SignatureAlgorithm<Key,?>)SignatureAlgorithmsBridge.forId(alg.getValue()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // TODO: remove method for 1.0
|
||||
@Override
|
||||
public T signWith(io.jsonwebtoken.SignatureAlgorithm alg, byte[] secretKeyBytes) throws InvalidKeyException {
|
||||
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
|
||||
|
@ -188,6 +189,7 @@ public class DefaultJwtBuilder<T extends JwtBuilder<T>> implements JwtBuilder<T>
|
|||
return signWith(key, alg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // TODO: remove method for 1.0
|
||||
@Override
|
||||
public T signWith(io.jsonwebtoken.SignatureAlgorithm alg, String base64EncodedSecretKey) throws InvalidKeyException {
|
||||
Assert.hasText(base64EncodedSecretKey, "base64-encoded secret key cannot be null or empty.");
|
||||
|
@ -196,6 +198,7 @@ public class DefaultJwtBuilder<T extends JwtBuilder<T>> implements JwtBuilder<T>
|
|||
return signWith(alg, bytes);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // TODO: remove method for 1.0
|
||||
@Override
|
||||
public T signWith(io.jsonwebtoken.SignatureAlgorithm alg, Key key) {
|
||||
return signWith(key, alg);
|
||||
|
|
|
@ -22,6 +22,7 @@ import io.jsonwebtoken.CompressionCodec;
|
|||
import io.jsonwebtoken.CompressionCodecResolver;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
import io.jsonwebtoken.IncorrectClaimException;
|
||||
import io.jsonwebtoken.InvalidClaimException;
|
||||
import io.jsonwebtoken.Jwe;
|
||||
|
@ -39,13 +40,16 @@ import io.jsonwebtoken.PrematureJwtException;
|
|||
import io.jsonwebtoken.SigningKeyResolver;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver;
|
||||
import io.jsonwebtoken.impl.lang.ConstantFunction;
|
||||
import io.jsonwebtoken.impl.lang.Function;
|
||||
import io.jsonwebtoken.impl.lang.LegacyServices;
|
||||
import io.jsonwebtoken.impl.security.ConstantKeyLocator;
|
||||
import io.jsonwebtoken.impl.security.DefaultAeadResult;
|
||||
import io.jsonwebtoken.impl.security.DefaultKeyRequest;
|
||||
import io.jsonwebtoken.impl.security.DefaultVerifySignatureRequest;
|
||||
import io.jsonwebtoken.impl.security.DelegatingSigningKeyResolver;
|
||||
import io.jsonwebtoken.impl.security.StaticKeyResolver;
|
||||
import io.jsonwebtoken.impl.security.StaticSigningKeyResolver;
|
||||
import io.jsonwebtoken.impl.security.EncryptionAlgorithmsBridge;
|
||||
import io.jsonwebtoken.impl.security.KeyAlgorithmsBridge;
|
||||
import io.jsonwebtoken.impl.security.SignatureAlgorithmsBridge;
|
||||
import io.jsonwebtoken.io.Decoder;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.DecodingException;
|
||||
|
@ -53,14 +57,12 @@ import io.jsonwebtoken.io.DeserializationException;
|
|||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.lang.Arrays;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.lang.DateFormats;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
import io.jsonwebtoken.security.EncryptionAlgorithms;
|
||||
import io.jsonwebtoken.security.InvalidKeyException;
|
||||
import io.jsonwebtoken.security.KeyAlgorithm;
|
||||
import io.jsonwebtoken.security.KeyAlgorithms;
|
||||
import io.jsonwebtoken.security.KeyRequest;
|
||||
import io.jsonwebtoken.security.KeyResolver;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import io.jsonwebtoken.security.PayloadSupplier;
|
||||
import io.jsonwebtoken.security.SignatureAlgorithm;
|
||||
|
@ -75,6 +77,7 @@ import javax.crypto.SecretKey;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Key;
|
||||
import java.security.Provider;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -85,14 +88,62 @@ public class DefaultJwtParser implements JwtParser {
|
|||
|
||||
private static final JwtTokenizer jwtTokenizer = new JwtTokenizer();
|
||||
|
||||
// TODO: make the folling fields final for v1.0
|
||||
public static final String MISSING_JWS_ALG_MSG =
|
||||
"JWS header does not contain a required 'alg' (Algorithm) header parameter. " +
|
||||
"This header parameter is mandatory per the JWS Specification, Section 4.1.1. See " +
|
||||
"https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.1 for more information.";
|
||||
|
||||
public static final String MISSING_JWE_ALG_MSG =
|
||||
"JWE header does not contain a required 'alg' (Algorithm) header parameter. " +
|
||||
"This header parameter is mandatory per the JWE Specification, Section 4.1.1. See " +
|
||||
"https://datatracker.ietf.org/doc/html/rfc7516#section-4.1.1 for more information.";
|
||||
|
||||
private static final String MISSING_ENC_MSG =
|
||||
"JWE header does not contain a required 'enc' (Encryption Algorithm) header parameter. " +
|
||||
"This header parameter is mandatory per the JWE Specification, Section 4.1.2. See " +
|
||||
"https://datatracker.ietf.org/doc/html/rfc7516#section-4.1.2 for more information.";
|
||||
|
||||
private static <H extends Header<H>, R extends Identifiable> Function<H, R> backup(String id, String msg, Collection<R> extras) {
|
||||
if (Collections.isEmpty(extras)) {
|
||||
return ConstantFunction.forNull();
|
||||
} else {
|
||||
return new IdLocator<>(id, msg, new IdRegistry<>(extras), ConstantFunction.<H, R>forNull());
|
||||
}
|
||||
}
|
||||
|
||||
private static <H extends Header<H>, R extends Identifiable> Function<H, R> locFn(String id, String msg, Function<String, R> reg, Collection<R> extras) {
|
||||
Function<H,R> backup = backup(id, msg, extras);
|
||||
return new IdLocator<>(id, msg, reg, backup);
|
||||
}
|
||||
|
||||
private static Function<JwsHeader, SignatureAlgorithm<?, ?>> sigFn(Collection<SignatureAlgorithm<?, ?>> extras) {
|
||||
return locFn(JwsHeader.ALGORITHM, MISSING_JWS_ALG_MSG, SignatureAlgorithmsBridge.REGISTRY, extras);
|
||||
}
|
||||
|
||||
private static Function<JweHeader, SymmetricAeadAlgorithm> encFn(Collection<SymmetricAeadAlgorithm> extras) {
|
||||
return locFn(JweHeader.ENCRYPTION_ALGORITHM, MISSING_ENC_MSG, EncryptionAlgorithmsBridge.REGISTRY, extras);
|
||||
}
|
||||
|
||||
private static Function<JweHeader, KeyAlgorithm<?, ?>> keyFn(Collection<KeyAlgorithm<?, ?>> extras) {
|
||||
return locFn(JweHeader.ALGORITHM, MISSING_JWE_ALG_MSG, KeyAlgorithmsBridge.REGISTRY, extras);
|
||||
}
|
||||
|
||||
// TODO: make the following fields final for v1.0
|
||||
private Provider provider;
|
||||
|
||||
@SuppressWarnings("deprecation") // will remove for 1.0
|
||||
private SigningKeyResolver signingKeyResolver;
|
||||
|
||||
private KeyResolver keyResolver;
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Function<Header, CompressionCodec> compressionCodecLocator;
|
||||
|
||||
private CompressionCodecResolver compressionCodecResolver = new DefaultCompressionCodecResolver();
|
||||
private final Function<JwsHeader, SignatureAlgorithm<?, ?>> signatureAlgorithmLocator;
|
||||
|
||||
private final Function<JweHeader, SymmetricAeadAlgorithm> encryptionAlgorithmLocator;
|
||||
|
||||
private final Function<JweHeader, KeyAlgorithm<?, ?>> keyAlgorithmLocator;
|
||||
|
||||
private final Function<?, Key> keyLocator;
|
||||
|
||||
private Decoder<String, byte[]> base64UrlDecoder = Decoders.BASE64URL;
|
||||
|
||||
|
@ -109,30 +160,43 @@ public class DefaultJwtParser implements JwtParser {
|
|||
*
|
||||
* @deprecated for backward compatibility only, see other constructors.
|
||||
*/
|
||||
@SuppressWarnings("DeprecatedIsStillUsed") // will remove before 1.0
|
||||
@Deprecated
|
||||
public DefaultJwtParser() {
|
||||
this.keyResolver = new StaticKeyResolver(null, null);
|
||||
this.signingKeyResolver = new DelegatingSigningKeyResolver(this.keyResolver);
|
||||
ConstantKeyLocator<?> constantKeyLocator = new ConstantKeyLocator<>(null, null);
|
||||
this.keyLocator = constantKeyLocator;
|
||||
this.signingKeyResolver = constantKeyLocator;
|
||||
this.signatureAlgorithmLocator = sigFn(Collections.<SignatureAlgorithm<?, ?>>emptyList());
|
||||
this.keyAlgorithmLocator = keyFn(Collections.<KeyAlgorithm<?, ?>>emptyList());
|
||||
this.encryptionAlgorithmLocator = encFn(Collections.<SymmetricAeadAlgorithm>emptyList());
|
||||
this.compressionCodecLocator = new CompressionCodecLocator<>(new DefaultCompressionCodecResolver());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") //SigningKeyResolver will be removed for 1.0
|
||||
DefaultJwtParser(Provider provider,
|
||||
SigningKeyResolver signingKeyResolver,
|
||||
KeyResolver keyResolver,
|
||||
Function<?,Key> keyLocator,
|
||||
Clock clock,
|
||||
long allowedClockSkewMillis,
|
||||
Claims expectedClaims,
|
||||
Decoder<String, byte[]> base64UrlDecoder,
|
||||
Deserializer<Map<String, ?>> deserializer,
|
||||
CompressionCodecResolver compressionCodecResolver) {
|
||||
CompressionCodecResolver compressionCodecResolver,
|
||||
Collection<SignatureAlgorithm<?, ?>> extraSigAlgs,
|
||||
Collection<KeyAlgorithm<?, ?>> extraKeyAlgs,
|
||||
Collection<SymmetricAeadAlgorithm> extraEncAlgs) {
|
||||
this.provider = provider;
|
||||
this.signingKeyResolver = Assert.notNull(signingKeyResolver, "SigningKeyResolver cannot be null.");
|
||||
this.keyResolver = Assert.notNull(keyResolver, "KeyResolver cannot be null.");
|
||||
this.keyLocator = Assert.notNull(keyLocator, "Key Locator cannot be null.");
|
||||
this.clock = clock;
|
||||
this.allowedClockSkewMillis = allowedClockSkewMillis;
|
||||
this.expectedClaims = expectedClaims;
|
||||
this.base64UrlDecoder = base64UrlDecoder;
|
||||
this.deserializer = deserializer;
|
||||
this.compressionCodecResolver = compressionCodecResolver;
|
||||
this.signatureAlgorithmLocator = sigFn(extraSigAlgs);
|
||||
this.keyAlgorithmLocator = keyFn(extraKeyAlgs);
|
||||
this.encryptionAlgorithmLocator = encFn(extraEncAlgs);
|
||||
this.compressionCodecLocator = new CompressionCodecLocator<>(compressionCodecResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -229,10 +293,11 @@ public class DefaultJwtParser implements JwtParser {
|
|||
@Override
|
||||
public JwtParser setSigningKey(final Key key) {
|
||||
Assert.notNull(key, "signing key cannot be null.");
|
||||
setSigningKeyResolver(new StaticSigningKeyResolver(key));
|
||||
setSigningKeyResolver(new ConstantKeyLocator<>(key, null));
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // required until 1.0
|
||||
@Override
|
||||
public JwtParser setSigningKeyResolver(SigningKeyResolver signingKeyResolver) {
|
||||
Assert.notNull(signingKeyResolver, "SigningKeyResolver cannot be null.");
|
||||
|
@ -243,7 +308,7 @@ public class DefaultJwtParser implements JwtParser {
|
|||
@Override
|
||||
public JwtParser setCompressionCodecResolver(CompressionCodecResolver compressionCodecResolver) {
|
||||
Assert.notNull(compressionCodecResolver, "compressionCodecResolver cannot be null.");
|
||||
this.compressionCodecResolver = compressionCodecResolver;
|
||||
this.compressionCodecLocator = new CompressionCodecLocator<>(compressionCodecResolver);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -271,13 +336,14 @@ public class DefaultJwtParser implements JwtParser {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Jwt<?,?> parse(String compact) throws ExpiredJwtException, MalformedJwtException, SignatureException {
|
||||
public Jwt<?, ?> parse(String compact) throws ExpiredJwtException, MalformedJwtException, SignatureException {
|
||||
|
||||
// TODO, this logic is only need for a now deprecated code path
|
||||
// remove this block in v1.0 (the equivalent is already in DefaultJwtParserBuilder)
|
||||
if (this.deserializer == null) {
|
||||
// try to find one based on the services available
|
||||
// TODO: This util class will throw a UnavailableImplementationException here to retain behavior of previous version, remove in v1.0
|
||||
//noinspection deprecation
|
||||
this.deserializer = LegacyServices.loadFirst(Deserializer.class);
|
||||
}
|
||||
|
||||
|
@ -293,9 +359,8 @@ public class DefaultJwtParser implements JwtParser {
|
|||
// =============== Header =================
|
||||
final byte[] headerBytes = base64UrlDecode(base64UrlHeader, "protected header");
|
||||
String origValue = new String(headerBytes, Strings.UTF_8);
|
||||
Map<String, ?> m = readValue(origValue, "protected header" );
|
||||
@SuppressWarnings("rawtypes")
|
||||
Header header = tokenized instanceof TokenizedJwe ? new DefaultJweHeader(m) : new DefaultJwsHeader(m);
|
||||
Map<String, ?> m = readValue(origValue, "protected header");
|
||||
Header<?> header = tokenized.createHeader(m);
|
||||
|
||||
// https://tools.ietf.org/html/rfc7515#section-10.7 , second-to-last bullet point, note the use of 'always':
|
||||
//
|
||||
|
@ -303,14 +368,17 @@ public class DefaultJwtParser implements JwtParser {
|
|||
// Protected Header. (This is always the case when using the JWS
|
||||
// Compact Serialization and is the approach taken by CMS [RFC6211].)
|
||||
//
|
||||
final String alg = header.getAlgorithm();
|
||||
final String alg = Strings.clean(header.getAlgorithm());
|
||||
if (!Strings.hasText(alg)) {
|
||||
String msg = "Compact JWT strings MUST always have an Algorithm ('alg') header value per https://tools.ietf.org/html/rfc7515#section-4.1.1 and https://tools.ietf.org/html/rfc7516#section-4.1.1. Also see https://tools.ietf.org/html/rfc7515#section-10.7 for more information.";
|
||||
String msg = "Compact JWT strings MUST always have an 'alg' (Algorithm) header value per " +
|
||||
"https://tools.ietf.org/html/rfc7515#section-4.1.1 and " +
|
||||
"https://tools.ietf.org/html/rfc7516#section-4.1.1. Also see " +
|
||||
"https://tools.ietf.org/html/rfc7515#section-10.7 for more information.";
|
||||
throw new MalformedJwtException(msg);
|
||||
}
|
||||
|
||||
// =============== Body =================
|
||||
CompressionCodec compressionCodec = compressionCodecResolver.resolveCompressionCodec(header);
|
||||
CompressionCodec compressionCodec = compressionCodecLocator.apply(header);
|
||||
byte[] bytes = base64UrlDecode(tokenized.getBody(), "payload"); // Only JWS body can be empty per https://github.com/jwtk/jjwt/pull/540
|
||||
if (tokenized instanceof TokenizedJwe && Arrays.length(bytes) == 0) {
|
||||
String msg = "Compact JWE strings MUST always contain a payload (ciphertext).";
|
||||
|
@ -324,8 +392,8 @@ public class DefaultJwtParser implements JwtParser {
|
|||
byte[] tag = null;
|
||||
if (tokenized instanceof TokenizedJwe) { //need to decrypt the ciphertext
|
||||
|
||||
TokenizedJwe tokenizedJwe = (TokenizedJwe)tokenized;
|
||||
JweHeader jweHeader = (JweHeader)header;
|
||||
TokenizedJwe tokenizedJwe = (TokenizedJwe) tokenized;
|
||||
JweHeader jweHeader = (JweHeader) header;
|
||||
|
||||
byte[] cekBytes = new byte[0]; //ignored unless using an encrypted key algorithm
|
||||
String base64Url = tokenizedJwe.getEncryptedKey();
|
||||
|
@ -346,6 +414,9 @@ public class DefaultJwtParser implements JwtParser {
|
|||
throw new MalformedJwtException(msg);
|
||||
}
|
||||
|
||||
// This is intentional - the AAD (Additional Authenticated Data) scheme for compact JWEs is to use
|
||||
// the ASCII bytes of the raw base64url text as the AAD, and *not* the base64url-decoded bytes per
|
||||
// https://datatracker.ietf.org/doc/html/rfc7516#section-5.1, Step 14.
|
||||
final byte[] aad = base64UrlHeader.getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
base64Url = tokenizedJwe.getDigest();
|
||||
|
@ -359,19 +430,28 @@ public class DefaultJwtParser implements JwtParser {
|
|||
|
||||
String enc = jweHeader.getEncryptionAlgorithm();
|
||||
if (!Strings.hasText(enc)) {
|
||||
String msg = "JWE header does not contain the required 'enc' (Encryption Algorithm) header value";
|
||||
throw new MalformedJwtException(msg);
|
||||
throw new MalformedJwtException(MISSING_ENC_MSG);
|
||||
}
|
||||
final SymmetricAeadAlgorithm encAlg = EncryptionAlgorithms.forName(enc); //TODO: ensure lookup goes through a resolver
|
||||
final KeyAlgorithm<?,Key> keyAlg = (KeyAlgorithm<?,Key>)KeyAlgorithms.forName(alg); //TODO: ensure lookup goes through a resolver
|
||||
final Key key = this.keyResolver.resolveKey(jweHeader);
|
||||
if (key == null) {
|
||||
String msg = "Unable to locate decryption key for encryption algorithm '" + encAlg.getId() +
|
||||
"' using key management algorithm '" + keyAlg.getId() + "'.";
|
||||
final SymmetricAeadAlgorithm encAlg = this.encryptionAlgorithmLocator.apply(jweHeader);
|
||||
if (encAlg == null) {
|
||||
String msg = "Unrecognized JWE encryption algorithm identifier: " + enc;
|
||||
throw new UnsupportedJwtException(msg);
|
||||
}
|
||||
|
||||
KeyRequest<byte[],Key> request = new DefaultKeyRequest<>(this.provider, null, cekBytes, key, jweHeader);
|
||||
@SuppressWarnings("rawtypes") final KeyAlgorithm keyAlg = this.keyAlgorithmLocator.apply(jweHeader);
|
||||
if (keyAlg == null) {
|
||||
String msg = "Unrecognized JWE key management algorithm: " + alg;
|
||||
throw new UnsupportedJwtException(msg);
|
||||
}
|
||||
|
||||
final Key key = ((Function<JweHeader,Key>)this.keyLocator).apply(jweHeader);
|
||||
if (key == null) {
|
||||
String msg = "No key available for the '" + keyAlg.getId() + "' key management algorithm. Unable to " +
|
||||
"perform '" + encAlg + "' decryption.";
|
||||
throw new UnsupportedJwtException(msg);
|
||||
}
|
||||
|
||||
KeyRequest<byte[], ?> request = new DefaultKeyRequest<>(this.provider, null, cekBytes, key, jweHeader);
|
||||
final SecretKey cek = keyAlg.getDecryptionKey(request);
|
||||
|
||||
SymmetricAeadDecryptionRequest decryptRequest =
|
||||
|
@ -388,13 +468,14 @@ public class DefaultJwtParser implements JwtParser {
|
|||
claims = new DefaultClaims(claimsMap);
|
||||
}
|
||||
|
||||
Jwt<?,?> jwt;
|
||||
Jwt<?, ?> jwt;
|
||||
Object body = claims != null ? claims : payload;
|
||||
if (tokenized instanceof TokenizedJwe) {
|
||||
if (header instanceof JweHeader) {
|
||||
jwt = new DefaultJwe<>((JweHeader)header, body, iv, tag);
|
||||
} else { // JWS
|
||||
if (!Strings.hasText(tokenized.getDigest()) && SignatureAlgorithms.NONE.getId().equalsIgnoreCase(alg)) {
|
||||
jwt = new DefaultJwt<>(header, body);
|
||||
//noinspection rawtypes
|
||||
jwt = new DefaultJwt(header, body);
|
||||
} else {
|
||||
jwt = new DefaultJws<>((JwsHeader)header, body, tokenized.getDigest());
|
||||
}
|
||||
|
@ -403,12 +484,15 @@ public class DefaultJwtParser implements JwtParser {
|
|||
// =============== Signature =================
|
||||
if (jwt instanceof Jws) { // it's a JWS, validate the signature
|
||||
|
||||
Jws<?> jws = (Jws<?>)jwt;
|
||||
Jws<?> jws = (Jws<?>) jwt;
|
||||
|
||||
final JwsHeader jwsHeader = jws.getHeader();
|
||||
|
||||
//TODO: ensure lookup goes through resolver (to support custom algorithms):
|
||||
SignatureAlgorithm algorithm = SignatureAlgorithms.forName(alg);
|
||||
SignatureAlgorithm<?,Key> algorithm = (SignatureAlgorithm<?,Key>)signatureAlgorithmLocator.apply(jwsHeader);
|
||||
if (algorithm == null) {
|
||||
String msg = "Unrecognized JWS algorithm identifier: " + alg;
|
||||
throw new UnsupportedJwtException(msg);
|
||||
}
|
||||
|
||||
String digest = tokenized.getDigest();
|
||||
|
||||
|
@ -575,7 +659,7 @@ public class DefaultJwtParser implements JwtParser {
|
|||
Assert.notNull(handler, "JwtHandler argument cannot be null.");
|
||||
Assert.hasText(compact, "JWT String argument cannot be null or empty.");
|
||||
|
||||
Jwt<?,?> jwt = parse(compact);
|
||||
Jwt<?, ?> jwt = parse(compact);
|
||||
|
||||
if (jwt instanceof Jws) {
|
||||
Jws<?> jws = (Jws<?>) jwt;
|
||||
|
@ -586,39 +670,39 @@ public class DefaultJwtParser implements JwtParser {
|
|||
return handler.onPlaintextJws((Jws<String>) jws);
|
||||
}
|
||||
} else if (jwt instanceof Jwe) {
|
||||
Jwe<?> jwe = (Jwe<?>)jwt;
|
||||
Jwe<?> jwe = (Jwe<?>) jwt;
|
||||
Object body = jwe.getBody();
|
||||
if (body instanceof Claims) {
|
||||
return handler.onClaimsJwe((Jwe<Claims>)jwe);
|
||||
return handler.onClaimsJwe((Jwe<Claims>) jwe);
|
||||
} else {
|
||||
return handler.onPlaintextJwe((Jwe<String>) jwe);
|
||||
}
|
||||
} else {
|
||||
Object body = jwt.getBody();
|
||||
if (body instanceof Claims) {
|
||||
return handler.onClaimsJwt((Jwt<Header, Claims>) jwt);
|
||||
return handler.onClaimsJwt((Jwt<?, Claims>) jwt);
|
||||
} else {
|
||||
return handler.onPlaintextJwt((Jwt<Header, String>) jwt);
|
||||
return handler.onPlaintextJwt((Jwt<?, String>) jwt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Jwt<Header, String> parsePlaintextJwt(String plaintextJwt) {
|
||||
return parse(plaintextJwt, new JwtHandlerAdapter<Jwt<Header, String>>() {
|
||||
public Jwt<?, String> parsePlaintextJwt(String plaintextJwt) {
|
||||
return parse(plaintextJwt, new JwtHandlerAdapter<Jwt<?, String>>() {
|
||||
@Override
|
||||
public Jwt<Header, String> onPlaintextJwt(Jwt<Header, String> jwt) {
|
||||
public Jwt<?, String> onPlaintextJwt(Jwt<?, String> jwt) {
|
||||
return jwt;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Jwt<Header, Claims> parseClaimsJwt(String claimsJwt) {
|
||||
public Jwt<?, Claims> parseClaimsJwt(String claimsJwt) {
|
||||
try {
|
||||
return parse(claimsJwt, new JwtHandlerAdapter<Jwt<Header, Claims>>() {
|
||||
return parse(claimsJwt, new JwtHandlerAdapter<Jwt<?, Claims>>() {
|
||||
@Override
|
||||
public Jwt<Header, Claims> onClaimsJwt(Jwt<Header, Claims> jwt) {
|
||||
public Jwt<?, Claims> onClaimsJwt(Jwt<?, Claims> jwt) {
|
||||
return jwt;
|
||||
}
|
||||
});
|
||||
|
@ -680,7 +764,6 @@ public class DefaultJwtParser implements JwtParser {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Map<String, ?> readValue(String val, final String name) {
|
||||
try {
|
||||
byte[] bytes = val.getBytes(Strings.UTF_8);
|
||||
|
|
|
@ -18,24 +18,33 @@ package io.jsonwebtoken.impl;
|
|||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Clock;
|
||||
import io.jsonwebtoken.CompressionCodecResolver;
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.JweHeader;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.JwtParser;
|
||||
import io.jsonwebtoken.JwtParserBuilder;
|
||||
import io.jsonwebtoken.Locator;
|
||||
import io.jsonwebtoken.SigningKeyResolver;
|
||||
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver;
|
||||
import io.jsonwebtoken.impl.lang.ConstantFunction;
|
||||
import io.jsonwebtoken.impl.lang.Function;
|
||||
import io.jsonwebtoken.impl.lang.LocatorFunction;
|
||||
import io.jsonwebtoken.impl.lang.Services;
|
||||
import io.jsonwebtoken.impl.security.DelegatingSigningKeyResolver;
|
||||
import io.jsonwebtoken.impl.security.StaticKeyResolver;
|
||||
import io.jsonwebtoken.impl.security.StaticSigningKeyResolver;
|
||||
import io.jsonwebtoken.impl.security.ConstantKeyLocator;
|
||||
import io.jsonwebtoken.io.Decoder;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.security.KeyResolver;
|
||||
import io.jsonwebtoken.security.KeyAlgorithm;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import io.jsonwebtoken.security.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.SymmetricAeadAlgorithm;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.Provider;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -57,13 +66,18 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
|
|||
|
||||
private Provider provider;
|
||||
|
||||
private Key signatureVerificationKey;
|
||||
private Key decryptionKey;
|
||||
private KeyResolver keyResolver;
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
private Function<Header, Key> keyLocator = ConstantFunction.forNull();
|
||||
@SuppressWarnings("deprecation") //TODO: remove for 1.0
|
||||
private SigningKeyResolver signingKeyResolver;
|
||||
private SigningKeyResolver signingKeyResolver = new ConstantKeyLocator<>(null, null);
|
||||
|
||||
private CompressionCodecResolver compressionCodecResolver;
|
||||
private CompressionCodecResolver compressionCodecResolver = new DefaultCompressionCodecResolver();
|
||||
|
||||
private final Collection<SymmetricAeadAlgorithm> extraEncryptionAlgorithms = new LinkedHashSet<>();
|
||||
|
||||
private final Collection<KeyAlgorithm<?, ?>> extraKeyAlgorithms = new LinkedHashSet<>();
|
||||
|
||||
private final Collection<SignatureAlgorithm<?, ?>> extraSignatureAlgorithms = new LinkedHashSet<>();
|
||||
|
||||
private Decoder<String, byte[]> base64UrlDecoder = Decoders.BASE64URL;
|
||||
|
||||
|
@ -172,16 +186,52 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
|
|||
return setSigningKey(bytes);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public JwtParserBuilder setSigningKey(Key key) {
|
||||
public JwtParserBuilder setSigningKey(final Key key) {
|
||||
Assert.notNull(key, "signing key cannot be null.");
|
||||
this.signatureVerificationKey = key;
|
||||
return setSigningKeyResolver(new StaticSigningKeyResolver(key));
|
||||
final Function<Header, Key> existing = this.keyLocator;
|
||||
this.keyLocator = new Function<Header, Key>() {
|
||||
@Override
|
||||
public Key apply(Header header) {
|
||||
return header instanceof JwsHeader ? key : existing.apply(header);
|
||||
}
|
||||
};
|
||||
return setSigningKeyResolver(new ConstantKeyLocator<>(key, null));
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public JwtParserBuilder decryptWith(final Key key) {
|
||||
Assert.notNull(key, "decryption key cannot be null.");
|
||||
final Function<Header, Key> existing = this.keyLocator;
|
||||
this.keyLocator = new Function<Header, Key>() {
|
||||
@Override
|
||||
public Key apply(Header header) {
|
||||
return header instanceof JweHeader ? key : existing.apply(header);
|
||||
}
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwtParserBuilder decryptWith(Key key) {
|
||||
this.decryptionKey = Assert.notNull(key, "decryption key cannot be null.");
|
||||
public JwtParserBuilder addEncryptionAlgorithms(Collection<SymmetricAeadAlgorithm> encAlgs) {
|
||||
Assert.notEmpty(encAlgs, "Additional EncryptionAlgorithm collection cannot be null or empty.");
|
||||
this.extraEncryptionAlgorithms.addAll(encAlgs);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwtParserBuilder addSignatureAlgorithms(Collection<SignatureAlgorithm<?, ?>> sigAlgs) {
|
||||
Assert.notEmpty(sigAlgs, "Additional SignatureAlgorithm collection cannot be null or empty.");
|
||||
this.extraSignatureAlgorithms.addAll(sigAlgs);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwtParserBuilder addKeyAlgorithms(Collection<KeyAlgorithm<?, ?>> keyAlgs) {
|
||||
Assert.notEmpty(keyAlgs, "Additional KeyAlgorithm collection cannot be null or empty.");
|
||||
this.extraKeyAlgorithms.addAll(keyAlgs);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -193,10 +243,15 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static Function<Header, Key> coerce(Function f) {
|
||||
return (Function<Header, Key>) f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwtParserBuilder setKeyResolver(KeyResolver keyResolver) {
|
||||
Assert.notNull(keyResolver, "KeyResolver cannot be null.");
|
||||
this.keyResolver = keyResolver;
|
||||
public JwtParserBuilder setKeyLocator(Locator<? extends Header<?>, Key> keyLocator) {
|
||||
Assert.notNull(keyLocator, "Key locator cannot be null.");
|
||||
this.keyLocator = coerce(new LocatorFunction<>(keyLocator));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -218,28 +273,25 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
|
|||
this.deserializer = Services.loadFirst(Deserializer.class);
|
||||
}
|
||||
|
||||
// if the compressionCodecResolver is not set default it.
|
||||
if (this.compressionCodecResolver == null) {
|
||||
this.compressionCodecResolver = new DefaultCompressionCodecResolver();
|
||||
}
|
||||
|
||||
if (this.keyResolver == null) {
|
||||
this.keyResolver = new StaticKeyResolver(this.signatureVerificationKey, this.decryptionKey);
|
||||
}
|
||||
|
||||
if (this.signingKeyResolver == null) {
|
||||
this.signingKeyResolver = new DelegatingSigningKeyResolver(this.keyResolver);
|
||||
}
|
||||
// Invariants. If these are ever violated, it's an error in this class implementation
|
||||
// (we default to non-null instances, and the setters should never allow null):
|
||||
assert this.keyLocator != null : "Key locator should never be null.";
|
||||
assert this.signingKeyResolver != null : "SigningKeyResolver should never be null.";
|
||||
assert this.compressionCodecResolver != null : "CompressionCodecResolver should never be null.";
|
||||
|
||||
return new ImmutableJwtParser(new DefaultJwtParser(
|
||||
provider,
|
||||
signingKeyResolver,
|
||||
keyResolver,
|
||||
keyLocator,
|
||||
clock,
|
||||
allowedClockSkewMillis,
|
||||
expectedClaims,
|
||||
base64UrlDecoder,
|
||||
deserializer,
|
||||
compressionCodecResolver));
|
||||
compressionCodecResolver,
|
||||
extraSignatureAlgorithms,
|
||||
extraKeyAlgorithms,
|
||||
extraEncryptionAlgorithms
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package io.jsonwebtoken.impl;
|
||||
|
||||
import io.jsonwebtoken.Header;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
class DefaultTokenizedJwe extends DefaultTokenizedJwt implements TokenizedJwe {
|
||||
|
||||
private final String encryptedKey;
|
||||
|
@ -20,4 +24,9 @@ class DefaultTokenizedJwe extends DefaultTokenizedJwt implements TokenizedJwe {
|
|||
public String getIv() {
|
||||
return this.iv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Header<?> createHeader(Map<String, ?> m) {
|
||||
return new DefaultJweHeader(m);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
package io.jsonwebtoken.impl;
|
||||
|
||||
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
class DefaultTokenizedJwt implements TokenizedJwt {
|
||||
|
||||
private final String protectedHeader;
|
||||
|
@ -26,4 +32,12 @@ class DefaultTokenizedJwt implements TokenizedJwt {
|
|||
public String getDigest() {
|
||||
return this.digest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Header<?> createHeader(Map<String, ?> m) {
|
||||
if (Strings.hasText(getDigest())) {
|
||||
return new DefaultJwsHeader(m);
|
||||
}
|
||||
return new DefaultHeader<>(m);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package io.jsonwebtoken.impl;
|
||||
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.JweHeader;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.impl.lang.Function;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
||||
public class IdLocator<H extends Header<H>, R> implements Function<H, R> {
|
||||
|
||||
private final String headerName;
|
||||
private final String requiredMsg;
|
||||
private final boolean headerValueRequired;
|
||||
private final Function<String, R> primary;
|
||||
private final Function<H, R> backup;
|
||||
|
||||
public IdLocator(String headerName, String requiredMsg, Function<String, R> primary, Function<H, R> backup) {
|
||||
this.headerName = Assert.hasText(headerName, "Header name cannot be null or empty.");
|
||||
this.requiredMsg = requiredMsg;
|
||||
this.headerValueRequired = Strings.hasText(requiredMsg);
|
||||
this.primary = Assert.notNull(primary, "Primary lookup function cannot be null.");
|
||||
this.backup = Assert.notNull(backup, "Backup locator cannot be null.");
|
||||
}
|
||||
|
||||
private static String type(Header<?> header) {
|
||||
if (header instanceof JweHeader) {
|
||||
return "JWE";
|
||||
} else if (header instanceof JwsHeader) {
|
||||
return "JWS";
|
||||
} else {
|
||||
return "JWT";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R apply(H header) {
|
||||
|
||||
Assert.notNull(header, "Header argument cannot be null.");
|
||||
|
||||
Object val = header.get(this.headerName);
|
||||
String id = val != null ? String.valueOf(val) : null;
|
||||
|
||||
if (this.headerValueRequired && !Strings.hasText(id)) {
|
||||
throw new MalformedJwtException(requiredMsg);
|
||||
}
|
||||
|
||||
R instance = primary.apply(id);
|
||||
if (instance == null) {
|
||||
instance = backup.apply(header);
|
||||
}
|
||||
|
||||
if (this.headerValueRequired && instance == null) {
|
||||
String msg = "Unrecognized " + type(header) + " '" + this.headerName + "' header value: " + id;
|
||||
throw new UnsupportedJwtException(msg);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package io.jsonwebtoken.impl;
|
||||
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
import io.jsonwebtoken.impl.lang.Registry;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class IdRegistry<T extends Identifiable> implements Registry<String, T> {
|
||||
|
||||
private final Map<String, T> INSTANCES;
|
||||
|
||||
public IdRegistry(Collection<T> instances) {
|
||||
Assert.notEmpty(instances, "Collection of Identifiable instances may not be null or empty.");
|
||||
Map<String, T> m = new LinkedHashMap<>(instances.size());
|
||||
for (T instance : instances) {
|
||||
String id = Assert.hasText(Strings.clean(instance.getId()), "All Identifiable instances within the collection cannot be null or empty.");
|
||||
m.put(id, instance);
|
||||
}
|
||||
this.INSTANCES = java.util.Collections.unmodifiableMap(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T apply(String id) {
|
||||
Assert.hasText(id, "id argument cannot be null or empty.");
|
||||
//try constant time lookup first. This will satisfy 99% of invocations:
|
||||
T instance = INSTANCES.get(id);
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
//fall back to case-insensitive lookup:
|
||||
for (T i : INSTANCES.values()) {
|
||||
if (id.equalsIgnoreCase(i.getId())) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null; //no match
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> values() {
|
||||
return this.INSTANCES.values();
|
||||
}
|
||||
}
|
|
@ -156,12 +156,12 @@ class ImmutableJwtParser implements JwtParser {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Jwt<Header, String> parsePlaintextJwt(String plaintextJwt) throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
|
||||
public <H extends Header<H>> Jwt<H, String> parsePlaintextJwt(String plaintextJwt) throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
|
||||
return this.jwtParser.parsePlaintextJwt(plaintextJwt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Jwt<Header, Claims> parseClaimsJwt(String claimsJwt) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
|
||||
public <H extends Header<H>> Jwt<H, Claims> parseClaimsJwt(String claimsJwt) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
|
||||
return this.jwtParser.parseClaimsJwt(claimsJwt);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
package io.jsonwebtoken.impl;
|
||||
|
||||
import io.jsonwebtoken.Jwe;
|
||||
import io.jsonwebtoken.Jwt;
|
||||
import io.jsonwebtoken.JwtException;
|
||||
|
||||
public interface JweParser {
|
||||
|
||||
Jwe<?> parse(String jwt) throws JwtException;
|
||||
}
|
|
@ -178,7 +178,6 @@ public class JwtMap implements Map<String, Object> {
|
|||
return map.remove(o);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ?> m) {
|
||||
if (m == null) {
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
package io.jsonwebtoken.impl;
|
||||
|
||||
import io.jsonwebtoken.Header;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface TokenizedJwt {
|
||||
|
||||
/**
|
||||
* Protected header.
|
||||
*
|
||||
* @return protected header.
|
||||
*/
|
||||
String getProtected();
|
||||
|
@ -17,4 +22,6 @@ public interface TokenizedJwt {
|
|||
* Signature for JWS, AAD Tag for JWE.
|
||||
*/
|
||||
String getDigest();
|
||||
|
||||
Header<?> createHeader(Map<String, ?> m);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package io.jsonwebtoken.impl.lang;
|
||||
|
||||
|
||||
/**
|
||||
* Function that always returns the same value
|
||||
*
|
||||
* @param <T> Input type
|
||||
* @param <R> Return value type
|
||||
*/
|
||||
public final class ConstantFunction<T, R> implements Function<T, R> {
|
||||
|
||||
private static final Function<?, ?> NULL = new ConstantFunction<>(null);
|
||||
|
||||
private final R value;
|
||||
|
||||
public ConstantFunction(R value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T, R> Function<T, R> forNull() {
|
||||
return (Function<T, R>) NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R apply(T t) {
|
||||
return this.value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package io.jsonwebtoken.impl.lang;
|
||||
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.Locator;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
public class LocatorFunction<H extends Header<H>, R> implements Function<H, R> {
|
||||
|
||||
private final Locator<H, R> locator;
|
||||
|
||||
public LocatorFunction(Locator<H, R> locator) {
|
||||
this.locator = Assert.notNull(locator, "Locator instance cannot be null.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public R apply(H h) {
|
||||
return this.locator.locate(h);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package io.jsonwebtoken.impl.lang;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface Registry<I, T> extends Function<I, T> {
|
||||
|
||||
Collection<T> values();
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.JweHeader;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.SigningKeyResolver;
|
||||
import io.jsonwebtoken.impl.lang.Function;
|
||||
import io.jsonwebtoken.security.LocatorAdapter;
|
||||
|
||||
import java.security.Key;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ConstantKeyLocator<H extends Header<H>> extends LocatorAdapter<H, Key> implements SigningKeyResolver, Function<H, Key> {
|
||||
|
||||
private final Key jwsKey;
|
||||
private final Key jweKey;
|
||||
|
||||
public ConstantKeyLocator(Key jwsKey, Key jweKey) {
|
||||
this.jwsKey = jwsKey;
|
||||
this.jweKey = jweKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key resolveSigningKey(JwsHeader header, Claims claims) {
|
||||
return locate(header);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key resolveSigningKey(JwsHeader header, String plaintext) {
|
||||
return locate(header);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Key locate(JwsHeader header) {
|
||||
return this.jwsKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Key locate(JweHeader header) {
|
||||
return this.jweKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key apply(H header) {
|
||||
return locate(header);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.security.Identifiable;
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.security.SecurityRequest;
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ import java.security.Signature;
|
|||
import java.security.interfaces.ECKey;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
|
||||
@SuppressWarnings("unused") //used via reflection in the io.jsonwebtoken.security.SignatureAlgorithms class
|
||||
public class DefaultEllipticCurveSignatureAlgorithm<SK extends ECKey & PrivateKey, VK extends ECKey & PublicKey> extends AbstractSignatureAlgorithm<SK, VK> implements EllipticCurveSignatureAlgorithm<SK, VK> {
|
||||
|
||||
private static final String EC_PUBLIC_KEY_REQD_MSG =
|
||||
|
@ -31,6 +30,14 @@ public class DefaultEllipticCurveSignatureAlgorithm<SK extends ECKey & PrivateKe
|
|||
|
||||
private final int signatureLength;
|
||||
|
||||
private static int shaSize(int keyBitLength) {
|
||||
return keyBitLength == 521 ? 512 : keyBitLength;
|
||||
}
|
||||
|
||||
public DefaultEllipticCurveSignatureAlgorithm(int keyBitLength, int signatureLength) {
|
||||
this("ES" + shaSize(keyBitLength), "SHA" + shaSize(keyBitLength) + "withECDSA", "secp" + keyBitLength + "r1", keyBitLength, signatureLength);
|
||||
}
|
||||
|
||||
public DefaultEllipticCurveSignatureAlgorithm(String name, String jcaName, String curveName, int minKeyLength, int signatureLength) {
|
||||
super(name, jcaName);
|
||||
Assert.hasText(curveName, "Curve name cannot be null or empty.");
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.JweHeader;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
import io.jsonwebtoken.security.EncryptionAlgorithmLocator;
|
||||
import io.jsonwebtoken.security.EncryptionAlgorithms;
|
||||
import io.jsonwebtoken.security.SymmetricAeadAlgorithm;
|
||||
|
||||
/**
|
||||
* @since JJWT_RELEASE_VERSION
|
||||
*/
|
||||
public class DefaultEncryptionAlgorithmLocator implements EncryptionAlgorithmLocator {
|
||||
|
||||
@Override
|
||||
public SymmetricAeadAlgorithm getEncryptionAlgorithm(JweHeader jweHeader) {
|
||||
|
||||
String enc = Strings.clean(jweHeader.getEncryptionAlgorithm());
|
||||
//TODO: this check needs to be in the parser, to be enforced regardless of the locator implementation
|
||||
if (enc == null) {
|
||||
String msg = "JWE header does not contain an 'enc' header parameter. This header parameter is mandatory " +
|
||||
"per the JWE Specification, Section 4.1.2. See " +
|
||||
"https://tools.ietf.org/html/rfc7516#section-4.1.2 for more information.";
|
||||
throw new MalformedJwtException(msg);
|
||||
}
|
||||
|
||||
try {
|
||||
return EncryptionAlgorithms.forName(enc); //TODO: change to findByName and let the parser throw on null return. See below:
|
||||
} catch (IllegalArgumentException e) {
|
||||
//TODO: move this check to the parser - needs to be enforced if the locator returns null or throws a non-JWT exception
|
||||
//couldn't find one:
|
||||
String msg = "JWE 'enc' header parameter value of '" + enc + "' does not match a JWE standard algorithm " +
|
||||
"identifier. If '" + enc + "' represents a custom algorithm, the JwtParser must be configured with " +
|
||||
"a custom EncryptionAlgorithmLocator instance that knows how to return a compatible " +
|
||||
"EncryptionAlgorithm instance. Otherwise, this JWE is invalid and may not be used safely.";
|
||||
throw new UnsupportedJwtException(msg, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
import io.jsonwebtoken.impl.JwtMap;
|
||||
import io.jsonwebtoken.impl.io.CodecConverter;
|
||||
import io.jsonwebtoken.impl.lang.BiFunction;
|
||||
|
@ -11,7 +12,6 @@ import io.jsonwebtoken.lang.Assert;
|
|||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.lang.Objects;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
import io.jsonwebtoken.security.Identifiable;
|
||||
import io.jsonwebtoken.security.MalformedKeyException;
|
||||
|
||||
import java.net.URI;
|
||||
|
|
|
@ -17,7 +17,6 @@ import java.security.spec.AlgorithmParameterSpec;
|
|||
import java.security.spec.MGF1ParameterSpec;
|
||||
import java.security.spec.PSSParameterSpec;
|
||||
|
||||
@SuppressWarnings("unused") //used via reflection in the io.jsonwebtoken.security.SignatureAlgorithms class
|
||||
public class DefaultRsaSignatureAlgorithm<SK extends RSAKey & PrivateKey, VK extends RSAKey & PublicKey> extends AbstractSignatureAlgorithm<SK, VK> implements RsaSignatureAlgorithm<SK, VK> {
|
||||
|
||||
static {
|
||||
|
@ -46,6 +45,14 @@ public class DefaultRsaSignatureAlgorithm<SK extends RSAKey & PrivateKey, VK ext
|
|||
this.algorithmParameterSpec = algParam;
|
||||
}
|
||||
|
||||
public DefaultRsaSignatureAlgorithm(int digestBitLength, int preferredKeyBitLength) {
|
||||
this("RS" + digestBitLength, "SHA" + digestBitLength + "withRSA", preferredKeyBitLength);
|
||||
}
|
||||
|
||||
public DefaultRsaSignatureAlgorithm(int digestBitLength, int preferredKeyBitLength, int pssSaltBitLength) {
|
||||
this("PS" + digestBitLength, "RSASSA-PSS", preferredKeyBitLength, pssSaltBitLength);
|
||||
}
|
||||
|
||||
public DefaultRsaSignatureAlgorithm(String name, String jcaName, int preferredKeyLengthBits) {
|
||||
this(name, jcaName, preferredKeyLengthBits, null);
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.SigningKeyResolver;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.security.KeyResolver;
|
||||
|
||||
import java.security.Key;
|
||||
|
||||
public class DelegatingSigningKeyResolver implements SigningKeyResolver {
|
||||
|
||||
private final KeyResolver keyResolver;
|
||||
|
||||
public DelegatingSigningKeyResolver(KeyResolver keyResolver) {
|
||||
this.keyResolver = Assert.notNull(keyResolver, "KeyResolver cannot be null.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key resolveSigningKey(JwsHeader header, Claims claims) {
|
||||
return this.keyResolver.resolveKey(header);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key resolveSigningKey(JwsHeader header, String plaintext) {
|
||||
return this.keyResolver.resolveKey(header);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
import io.jsonwebtoken.security.CryptoException;
|
||||
import io.jsonwebtoken.security.CryptoRequest;
|
||||
import io.jsonwebtoken.security.Identifiable;
|
||||
import io.jsonwebtoken.security.KeyException;
|
||||
import io.jsonwebtoken.security.PayloadSupplier;
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.impl.IdRegistry;
|
||||
import io.jsonwebtoken.impl.lang.Registry;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.security.SymmetricAeadAlgorithm;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@SuppressWarnings({"unused"}) // reflection bridge class for the io.jsonwebtoken.security.EncryptionAlgorithms implementation
|
||||
public class EncryptionAlgorithmsBridge {
|
||||
|
||||
// prevent instantiation
|
||||
private EncryptionAlgorithmsBridge() {
|
||||
}
|
||||
|
||||
//For parser implementation - do not expose outside the impl module:
|
||||
public static final Registry<String, SymmetricAeadAlgorithm> REGISTRY;
|
||||
|
||||
static {
|
||||
REGISTRY = new IdRegistry<>(Collections.of(
|
||||
(SymmetricAeadAlgorithm) new HmacAesAeadAlgorithm(128),
|
||||
new HmacAesAeadAlgorithm(192),
|
||||
new HmacAesAeadAlgorithm(256),
|
||||
new GcmAesAeadAlgorithm(128),
|
||||
new GcmAesAeadAlgorithm(192),
|
||||
new GcmAesAeadAlgorithm(256)
|
||||
));
|
||||
}
|
||||
|
||||
public static Collection<SymmetricAeadAlgorithm> values() {
|
||||
return REGISTRY.values();
|
||||
}
|
||||
|
||||
public static SymmetricAeadAlgorithm findById(String id) {
|
||||
return REGISTRY.apply(id);
|
||||
}
|
||||
|
||||
public static SymmetricAeadAlgorithm forId(String id) {
|
||||
SymmetricAeadAlgorithm alg = findById(id);
|
||||
if (alg == null) {
|
||||
String msg = "Unrecognized JWA EncryptionAlgorithm identifier: " + id;
|
||||
throw new UnsupportedJwtException(msg);
|
||||
}
|
||||
return alg;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.security.Identifiable;
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
import io.jsonwebtoken.security.Jwk;
|
||||
|
||||
import java.security.Key;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.security.Identifiable;
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
|
||||
import java.net.URI;
|
||||
import java.security.Key;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
import io.jsonwebtoken.impl.lang.Converter;
|
||||
import io.jsonwebtoken.security.Identifiable;
|
||||
import io.jsonwebtoken.security.Jwk;
|
||||
|
||||
import java.security.Key;
|
||||
import java.util.Map;
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.security.EncryptedKeyAlgorithm;
|
||||
import io.jsonwebtoken.security.KeyAlgorithm;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.OAEPParameterSpec;
|
||||
import javax.crypto.spec.PSource;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.RSAKey;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.MGF1ParameterSpec;
|
||||
|
||||
//bridge class for the API so it doesn't need to know implementation/constructor argument details
|
||||
public final class KeyAlgorithms {
|
||||
|
||||
private static final String RSA1_5_ID = "RSA1_5";
|
||||
private static final String RSA1_5_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
|
||||
private static final String RSA_OAEP_ID = "RSA-OAEP";
|
||||
private static final String RSA_OAEP_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
|
||||
private static final String RSA_OAEP_256_ID = "RSA-OAEP-256";
|
||||
private static final String RSA_OAEP_256_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
|
||||
private static final AlgorithmParameterSpec RSA_OAEP_256_SPEC =
|
||||
new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
|
||||
|
||||
// prevent instantiation
|
||||
private KeyAlgorithms() {
|
||||
}
|
||||
|
||||
public static KeyAlgorithm<SecretKey, SecretKey> direct() {
|
||||
return new DirectKeyAlgorithm();
|
||||
}
|
||||
|
||||
public static EncryptedKeyAlgorithm<SecretKey, SecretKey> a128kw() {
|
||||
return new AesWrapKeyAlgorithm(128);
|
||||
}
|
||||
|
||||
public static EncryptedKeyAlgorithm<SecretKey, SecretKey> a192kw() {
|
||||
return new AesWrapKeyAlgorithm(192);
|
||||
}
|
||||
|
||||
public static EncryptedKeyAlgorithm<SecretKey, SecretKey> a256kw() {
|
||||
return new AesWrapKeyAlgorithm(256);
|
||||
}
|
||||
|
||||
public static EncryptedKeyAlgorithm<SecretKey, SecretKey> a128gcmkw() {
|
||||
return new AesGcmKeyAlgorithm(128);
|
||||
}
|
||||
|
||||
public static EncryptedKeyAlgorithm<SecretKey, SecretKey> a192gcmkw() {
|
||||
return new AesGcmKeyAlgorithm(192);
|
||||
}
|
||||
|
||||
public static EncryptedKeyAlgorithm<SecretKey, SecretKey> a256gcmkw() {
|
||||
return new AesGcmKeyAlgorithm(256);
|
||||
}
|
||||
|
||||
public static <EK extends RSAKey & PublicKey, DK extends RSAKey & PrivateKey> EncryptedKeyAlgorithm<EK, DK> rsa1_5() {
|
||||
return new DefaultRsaKeyAlgorithm<>(RSA1_5_ID, RSA1_5_TRANSFORMATION);
|
||||
}
|
||||
|
||||
public static <EK extends RSAKey & PublicKey, DK extends RSAKey & PrivateKey> EncryptedKeyAlgorithm<EK, DK> rsaOaep() {
|
||||
return new DefaultRsaKeyAlgorithm<>(RSA_OAEP_ID, RSA_OAEP_TRANSFORMATION);
|
||||
}
|
||||
|
||||
public static <EK extends RSAKey & PublicKey, DK extends RSAKey & PrivateKey> EncryptedKeyAlgorithm<EK, DK> rsaOaep256() {
|
||||
return new DefaultRsaKeyAlgorithm<>(RSA_OAEP_256_ID, RSA_OAEP_256_TRANSFORMATION, RSA_OAEP_256_SPEC);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.impl.IdRegistry;
|
||||
import io.jsonwebtoken.impl.lang.Registry;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.security.KeyAlgorithm;
|
||||
|
||||
import javax.crypto.spec.OAEPParameterSpec;
|
||||
import javax.crypto.spec.PSource;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.MGF1ParameterSpec;
|
||||
import java.util.Collection;
|
||||
|
||||
@SuppressWarnings({"unused"}) // reflection bridge class for the io.jsonwebtoken.security.KeyAlgorithms implementation
|
||||
public final class KeyAlgorithmsBridge {
|
||||
|
||||
// prevent instantiation
|
||||
private KeyAlgorithmsBridge() {
|
||||
}
|
||||
|
||||
private static final String RSA1_5_ID = "RSA1_5";
|
||||
private static final String RSA1_5_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
|
||||
private static final String RSA_OAEP_ID = "RSA-OAEP";
|
||||
private static final String RSA_OAEP_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
|
||||
private static final String RSA_OAEP_256_ID = "RSA-OAEP-256";
|
||||
private static final String RSA_OAEP_256_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
|
||||
private static final AlgorithmParameterSpec RSA_OAEP_256_SPEC =
|
||||
new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
|
||||
|
||||
//For parser implementation - do not expose outside the impl module
|
||||
public static final Registry<String, KeyAlgorithm<?, ?>> REGISTRY;
|
||||
|
||||
static {
|
||||
REGISTRY = new IdRegistry<>(Collections.<KeyAlgorithm<?, ?>>of(
|
||||
new DirectKeyAlgorithm(),
|
||||
new AesWrapKeyAlgorithm(128),
|
||||
new AesWrapKeyAlgorithm(192),
|
||||
new AesWrapKeyAlgorithm(256),
|
||||
new AesGcmKeyAlgorithm(128),
|
||||
new AesGcmKeyAlgorithm(192),
|
||||
new AesGcmKeyAlgorithm(256),
|
||||
new DefaultRsaKeyAlgorithm<>(RSA1_5_ID, RSA1_5_TRANSFORMATION),
|
||||
new DefaultRsaKeyAlgorithm<>(RSA_OAEP_ID, RSA_OAEP_TRANSFORMATION),
|
||||
new DefaultRsaKeyAlgorithm<>(RSA_OAEP_256_ID, RSA_OAEP_256_TRANSFORMATION, RSA_OAEP_256_SPEC)
|
||||
));
|
||||
}
|
||||
|
||||
public static Collection<KeyAlgorithm<?, ?>> values() {
|
||||
return REGISTRY.values();
|
||||
}
|
||||
|
||||
public static KeyAlgorithm<?, ?> findById(String id) {
|
||||
return REGISTRY.apply(id);
|
||||
}
|
||||
|
||||
public static KeyAlgorithm<?, ?> forId(String id) {
|
||||
KeyAlgorithm<?, ?> instance = findById(id);
|
||||
if (instance == null) {
|
||||
String msg = "Unrecognized JWA KeyAlgorithm identifier: " + id;
|
||||
throw new UnsupportedJwtException(msg);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
|
@ -5,8 +5,8 @@ import io.jsonwebtoken.lang.Assert;
|
|||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
import io.jsonwebtoken.security.InvalidKeyException;
|
||||
import io.jsonwebtoken.security.SignatureRequest;
|
||||
import io.jsonwebtoken.security.SecretKeySignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.SignatureRequest;
|
||||
import io.jsonwebtoken.security.WeakKeyException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
|
@ -16,7 +16,6 @@ import java.util.LinkedHashSet;
|
|||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("unused") //used via reflection in the io.jsonwebtoken.security.SignatureAlgorithms class
|
||||
public class MacSignatureAlgorithm extends AbstractSignatureAlgorithm<SecretKey, SecretKey> implements SecretKeySignatureAlgorithm {
|
||||
|
||||
private final int minKeyLength; //in bits
|
||||
|
@ -38,6 +37,10 @@ public class MacSignatureAlgorithm extends AbstractSignatureAlgorithm<SecretKey,
|
|||
VALID_HS256_JCA_NAMES.addAll(VALID_HS384_JCA_NAMES);
|
||||
}
|
||||
|
||||
public MacSignatureAlgorithm(int digestBitLength) {
|
||||
this("HS" + digestBitLength, "HmacSHA" + digestBitLength, digestBitLength);
|
||||
}
|
||||
|
||||
public MacSignatureAlgorithm(String id, String jcaName, int minKeyLength) {
|
||||
super(id, jcaName);
|
||||
Assert.isTrue(minKeyLength > 0, "minKeyLength must be greater than zero.");
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.impl.IdRegistry;
|
||||
import io.jsonwebtoken.impl.lang.Registry;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.security.SignatureAlgorithm;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@SuppressWarnings({"unused"}) // reflection bridge class for the io.jsonwebtoken.security.SignatureAlgorithms implementation
|
||||
public class SignatureAlgorithmsBridge {
|
||||
|
||||
//prevent instantiation
|
||||
private SignatureAlgorithmsBridge() {
|
||||
}
|
||||
|
||||
//For parser implementation - do not expose outside the impl module
|
||||
public static final Registry<String, SignatureAlgorithm<?, ?>> REGISTRY;
|
||||
|
||||
static {
|
||||
//noinspection RedundantTypeArguments
|
||||
REGISTRY = new IdRegistry<>(Collections.<SignatureAlgorithm<?, ?>>of(
|
||||
new NoneSignatureAlgorithm(),
|
||||
new MacSignatureAlgorithm(256),
|
||||
new MacSignatureAlgorithm(384),
|
||||
new MacSignatureAlgorithm(512),
|
||||
new DefaultRsaSignatureAlgorithm<>(256, 2048),
|
||||
new DefaultRsaSignatureAlgorithm<>(384, 3072),
|
||||
new DefaultRsaSignatureAlgorithm<>(512, 4096),
|
||||
new DefaultRsaSignatureAlgorithm<>(256, 2048, 256),
|
||||
new DefaultRsaSignatureAlgorithm<>(384, 3072, 384),
|
||||
new DefaultRsaSignatureAlgorithm<>(512, 4096, 512),
|
||||
new DefaultEllipticCurveSignatureAlgorithm<>(256, 64),
|
||||
new DefaultEllipticCurveSignatureAlgorithm<>(384, 96),
|
||||
new DefaultEllipticCurveSignatureAlgorithm<>(521, 132)
|
||||
));
|
||||
}
|
||||
|
||||
public static Collection<SignatureAlgorithm<?, ?>> values() {
|
||||
return REGISTRY.values();
|
||||
}
|
||||
|
||||
public static SignatureAlgorithm<?, ?> findById(String id) {
|
||||
return REGISTRY.apply(id);
|
||||
}
|
||||
|
||||
public static SignatureAlgorithm<?, ?> forId(String id) {
|
||||
SignatureAlgorithm<?, ?> instance = findById(id);
|
||||
if (instance == null) {
|
||||
String msg = "Unrecognized JWA SignatureAlgorithm identifier: " + id;
|
||||
throw new UnsupportedJwtException(msg);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.security.KeyResolver;
|
||||
|
||||
import java.security.Key;
|
||||
|
||||
public class StaticKeyResolver implements KeyResolver {
|
||||
|
||||
private final Key signatureVerificationKey;
|
||||
private final Key decryptionKey;
|
||||
|
||||
public StaticKeyResolver(Key signatureVerificationKey, Key decryptionKey) {
|
||||
this.signatureVerificationKey = signatureVerificationKey;
|
||||
this.decryptionKey = decryptionKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key resolveKey(Header<?> header) {
|
||||
if (header instanceof JwsHeader) {
|
||||
if (this.signatureVerificationKey == null) {
|
||||
String msg = "Signed JWTs are not supported: the JwtParser has not been configured with a " +
|
||||
"signature verification key or a KeyResolver. Consider configuring the JwtParserBuilder with " +
|
||||
"one of these to ensure it can use the necessary key to verify JWS signatures.";
|
||||
throw new UnsupportedJwtException(msg);
|
||||
}
|
||||
return this.signatureVerificationKey;
|
||||
} else { // JweHeader
|
||||
if (this.decryptionKey == null) {
|
||||
String msg = "Encrypted JWTs are not supported: the JwtParser has not been configured with a " +
|
||||
"decryption key or a KeyResolver. Consider configuring the JwtParserBuilder with " +
|
||||
"one of these to ensure it can use the necessary key to decrypt JWEs.";
|
||||
throw new UnsupportedJwtException(msg);
|
||||
}
|
||||
return this.decryptionKey;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.SigningKeyResolver;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import java.security.Key;
|
||||
|
||||
//TODO: delete when removing SigningKeyResolver
|
||||
public class StaticSigningKeyResolver implements SigningKeyResolver {
|
||||
|
||||
private final Key key;
|
||||
|
||||
public StaticSigningKeyResolver(Key key) {
|
||||
this.key = Assert.notNull(key, "Key cannot be null.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key resolveSigningKey(JwsHeader header, Claims claims) {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key resolveSigningKey(JwsHeader header, String plaintext) {
|
||||
return this.key;
|
||||
}
|
||||
}
|
|
@ -10,17 +10,17 @@ import javax.crypto.spec.SecretKeySpec
|
|||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertSame
|
||||
|
||||
class StaticKeyResolverTest {
|
||||
class ConstantKeyLocatorTest {
|
||||
|
||||
@Test
|
||||
void testSignatureVerificationKey() {
|
||||
def key = new SecretKeySpec(new byte[1], 'AES') //dummy key for testing
|
||||
assertSame key, new StaticKeyResolver(key, null).resolveKey(new DefaultJwsHeader())
|
||||
assertSame key, new ConstantKeyLocator(key, null).resolveKey(new DefaultJwsHeader())
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSignatureVerificationKeyMissing() {
|
||||
def resolver = new StaticKeyResolver(null, null)
|
||||
def resolver = new ConstantKeyLocator(null, null)
|
||||
try {
|
||||
resolver.resolveKey(new DefaultJwsHeader())
|
||||
} catch (UnsupportedJwtException uje) {
|
||||
|
@ -34,12 +34,12 @@ class StaticKeyResolverTest {
|
|||
@Test
|
||||
void testDecryptionKey() {
|
||||
def key = new SecretKeySpec(new byte[1], 'AES') //dummy key for testing
|
||||
assertSame key, new StaticKeyResolver(null, key).resolveKey(new DefaultJweHeader())
|
||||
assertSame key, new ConstantKeyLocator(null, key).resolveKey(new DefaultJweHeader())
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecryptionKeyMissing() {
|
||||
def resolver = new StaticKeyResolver(null, null)
|
||||
def resolver = new ConstantKeyLocator(null, null)
|
||||
try {
|
||||
resolver.resolveKey(new DefaultJweHeader())
|
||||
} catch (UnsupportedJwtException uje) {
|
|
@ -1,98 +0,0 @@
|
|||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.JweHeader
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.MalformedJwtException
|
||||
import io.jsonwebtoken.UnsupportedJwtException
|
||||
import io.jsonwebtoken.security.EncryptionAlgorithms
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* @since JJWT_RELEASE_VERSION
|
||||
*/
|
||||
class DefaultEncryptionAlgorithmLocatorTest {
|
||||
|
||||
private DefaultEncryptionAlgorithmLocator locator
|
||||
|
||||
@Before
|
||||
void setUp() {
|
||||
locator = new DefaultEncryptionAlgorithmLocator()
|
||||
}
|
||||
|
||||
private static JweHeader header(String enc) {
|
||||
return Jwts.jweHeader().setEncryptionAlgorithm(enc)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testA128CBCHS256() {
|
||||
assertSame EncryptionAlgorithms.A128CBC_HS256, locator.getEncryptionAlgorithm(header('A128CBC-HS256'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testA192CBCHS384() {
|
||||
assertSame EncryptionAlgorithms.A192CBC_HS384, locator.getEncryptionAlgorithm(header('A192CBC-HS384'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testA256CBCHS512() {
|
||||
assertSame EncryptionAlgorithms.A256CBC_HS512, locator.getEncryptionAlgorithm(header('A256CBC-HS512'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testA128GCM() {
|
||||
assertSame EncryptionAlgorithms.A128GCM, locator.getEncryptionAlgorithm(header('A128GCM'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testA192GCM() {
|
||||
assertSame EncryptionAlgorithms.A192GCM, locator.getEncryptionAlgorithm(header('A192GCM'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testA256GCM() {
|
||||
assertSame EncryptionAlgorithms.A256GCM, locator.getEncryptionAlgorithm(header('A256GCM'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMissingEncAlg() {
|
||||
try {
|
||||
locator.getEncryptionAlgorithm(Jwts.jweHeader())
|
||||
fail()
|
||||
} catch (MalformedJwtException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullEncAlg() {
|
||||
try {
|
||||
locator.getEncryptionAlgorithm(header(null))
|
||||
fail()
|
||||
} catch (MalformedJwtException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyEncAlg() {
|
||||
try {
|
||||
locator.getEncryptionAlgorithm(header(' '))
|
||||
fail()
|
||||
} catch (MalformedJwtException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnknownEncAlg() {
|
||||
try {
|
||||
locator.getEncryptionAlgorithm(header('foo'))
|
||||
fail()
|
||||
} catch (UnsupportedJwtException e) {
|
||||
assertEquals "JWE 'enc' header parameter value of 'foo' does not match a JWE standard algorithm " +
|
||||
"identifier. If 'foo' represents a custom algorithm, the JwtParser must be configured " +
|
||||
"with a custom EncryptionAlgorithmLocator instance that knows how to return a compatible " +
|
||||
"EncryptionAlgorithm instance. Otherwise, this JWE is invalid and may not be used safely.", e.message
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.io.Decoders
|
||||
|
||||
import io.jsonwebtoken.security.EncryptionAlgorithms
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -70,9 +70,7 @@ class RFC7518AppendixB1Test {
|
|||
void test() {
|
||||
|
||||
def alg = EncryptionAlgorithms.A128CBC_HS256
|
||||
|
||||
def request = new DefaultSymmetricAeadRequest(null, null, P, KEY, A, IV)
|
||||
|
||||
def result = alg.encrypt(request);
|
||||
|
||||
byte[] ciphertext = result.getPayload()
|
||||
|
@ -84,33 +82,9 @@ class RFC7518AppendixB1Test {
|
|||
assertArrayEquals IV, iv //shouldn't have been altered
|
||||
|
||||
// now test decryption:
|
||||
|
||||
def dreq = new DefaultAeadResult(null, null, ciphertext, KEY, A, tag, iv)
|
||||
|
||||
byte[] decryptionResult = alg.decrypt(dreq).getPayload()
|
||||
|
||||
assertArrayEquals(P, decryptionResult)
|
||||
|
||||
String encryptedCek = 'UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm' +
|
||||
'1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc' +
|
||||
'HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF' +
|
||||
'NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8' +
|
||||
'rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv' +
|
||||
'-B3oWh2TbqmScqXMR4gp_A' as String
|
||||
|
||||
ciphertext = Decoders.BASE64URL.decode('KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY')
|
||||
byte[] aad = Decoders.BASE64URL.decode('eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0')
|
||||
tag = Decoders.BASE64URL.decode('9hH0vgRfYgPnAHOd8stkvw')
|
||||
iv = Decoders.BASE64URL.decode('AxY8DCtDaGlsbGljb3RoZQ')
|
||||
SecretKey key = new SecretKeySpec([4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106,
|
||||
206, 107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156,
|
||||
44, 207] as byte[], "AES")
|
||||
|
||||
dreq = new DefaultAeadResult(null, null, ciphertext, key, aad, tag, iv)
|
||||
|
||||
decryptionResult = alg.decrypt(dreq).getPayload()
|
||||
|
||||
println decryptionResult
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -70,9 +70,7 @@ class RFC7518AppendixB2Test {
|
|||
void test() {
|
||||
|
||||
def alg = EncryptionAlgorithms.A192CBC_HS384
|
||||
|
||||
SymmetricAeadRequest req = new DefaultSymmetricAeadRequest(null, null, P, KEY, A, IV)
|
||||
|
||||
AeadResult result = alg.encrypt(req)
|
||||
|
||||
byte[] resultCiphertext = result.getPayload()
|
||||
|
@ -86,7 +84,6 @@ class RFC7518AppendixB2Test {
|
|||
// now test decryption:
|
||||
def dreq = new DefaultAeadResult(null, null, resultCiphertext, KEY, A, resultTag, resultIv)
|
||||
byte[] decryptionResult = alg.decrypt(dreq).getPayload()
|
||||
|
||||
assertArrayEquals(P, decryptionResult)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,9 +70,7 @@ class RFC7518AppendixB3Test {
|
|||
void test() {
|
||||
|
||||
def alg = EncryptionAlgorithms.A256CBC_HS512
|
||||
|
||||
SymmetricAeadRequest req = new DefaultSymmetricAeadRequest(null, null, P, KEY, A, IV)
|
||||
|
||||
AeadResult result = alg.encrypt(req)
|
||||
|
||||
byte[] resultCiphertext = result.getPayload()
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package io.jsonwebtoken.security
|
||||
|
||||
import io.jsonwebtoken.impl.security.DefaultSymmetricAeadRequest
|
||||
import io.jsonwebtoken.impl.security.DefaultAeadResult
|
||||
import io.jsonwebtoken.impl.security.EncryptionAlgorithm
|
||||
import io.jsonwebtoken.impl.security.DefaultSymmetricAeadRequest
|
||||
import io.jsonwebtoken.impl.security.GcmAesAeadAlgorithm
|
||||
import org.junit.Test
|
||||
|
||||
|
|
|
@ -4,9 +4,7 @@ import org.junit.Test
|
|||
|
||||
import java.security.Key
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertSame
|
||||
import static org.junit.Assert.assertTrue
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class KeyAlgorithmsTest {
|
||||
|
||||
|
@ -33,22 +31,12 @@ class KeyAlgorithmsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testForNameExactId() {
|
||||
assertSame KeyAlgorithms.A128KW, KeyAlgorithms.forName('A128KW')
|
||||
void testFindByExactId() {
|
||||
assertSame KeyAlgorithms.A128KW, KeyAlgorithms.findById('A128KW')
|
||||
}
|
||||
|
||||
@Test
|
||||
void testForNameCaseInsensitive() {
|
||||
assertSame KeyAlgorithms.A128GCMKW, KeyAlgorithms.forName('a128GcMkW')
|
||||
}
|
||||
|
||||
@Test
|
||||
void testForNameUnrecognizedId() {
|
||||
try {
|
||||
KeyAlgorithms.forName('foo')
|
||||
} catch (IllegalArgumentException iae) {
|
||||
String msg = "Unrecognized key algorithm id 'foo'"
|
||||
assertEquals msg, iae.getMessage()
|
||||
}
|
||||
void testFindByIdCaseInsensitive() {
|
||||
assertSame KeyAlgorithms.A128GCMKW, KeyAlgorithms.findById('a128GcMkW')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package io.jsonwebtoken.security
|
||||
|
||||
import static org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.assertSame
|
||||
|
||||
class SignatureAlgorithmsTest {
|
||||
|
||||
@Test
|
||||
|
@ -13,7 +14,7 @@ class SignatureAlgorithmsTest {
|
|||
@Test
|
||||
void testForNameCaseInsensitive() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithms.STANDARD_ALGORITHMS.values()) {
|
||||
assertSame alg, SignatureAlgorithms.forName(alg.getId().toLowerCase())
|
||||
assertSame alg, SignatureAlgorithms.forId(alg.getId().toLowerCase())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue