Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Claims.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Claims.java (.../Claims.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Claims.java (.../Claims.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -141,7 +141,7 @@ * *

If present, this value is the timestamp when the JWT was created.

* - * @return the JWT {@code nbf} value or {@code null} if not present. + * @return the JWT {@code iat} value or {@code null} if not present. */ Date getIssuedAt(); @@ -170,5 +170,22 @@ @Override //only for better/targeted JavaDoc Claims setId(String jti); + /** + * Returns the JWTs claim ({@code claimName}) value as a type {@code requiredType}, or {@code null} if not present. + * + *

JJWT only converts simple String, Date, Long, Integer, Short and Byte types automatically. Anything more + * complex is expected to be already converted to your desired type by the JSON + * {@link io.jsonwebtoken.io.Deserializer Deserializer} implementation. You may specify a custom Deserializer for a + * JwtParser with the desired conversion configuration via the {@link JwtParserBuilder#deserializeJsonWith} method. + * See custom JSON processor for more + * information. If using Jackson, you can specify custom claim POJO types as described in + * custom claim types. + * + * @param claimName name of claim + * @param requiredType the type of the value expected to be returned + * @param the type of the value expected to be returned + * @return the JWT {@code claimName} value or {@code null} if not present. + * @throws RequiredTypeException throw if the claim value is not null and not of type {@code requiredType} + */ T get(String claimName, Class requiredType); } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Clock.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Clock.java (.../Clock.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Clock.java (.../Clock.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jsonwebtoken; import java.util.Date; Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/CompressionCodec.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/CompressionCodec.java (.../CompressionCodec.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/CompressionCodec.java (.../CompressionCodec.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -18,16 +18,16 @@ /** * Compresses and decompresses byte arrays according to a compression algorithm. * - * @see io.jsonwebtoken.impl.compression.DeflateCompressionCodec - * @see io.jsonwebtoken.impl.compression.GzipCompressionCodec + * @see CompressionCodecs#DEFLATE + * @see CompressionCodecs#GZIP * @since 0.6.0 */ public interface CompressionCodec { /** - * The algorithm name to use as the JWT's {@code calg} header value. + * The compression algorithm name to use as the JWT's {@code zip} header value. * - * @return the algorithm name to use as the JWT's {@code calg} header value. + * @return the compression algorithm name to use as the JWT's {@code zip} header value. */ String getAlgorithmName(); Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/CompressionCodecResolver.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/CompressionCodecResolver.java (.../CompressionCodecResolver.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/CompressionCodecResolver.java (.../CompressionCodecResolver.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -16,12 +16,12 @@ package io.jsonwebtoken; /** - * Looks for a JWT {@code calg} header, and if found, returns the corresponding {@link CompressionCodec} the parser + * Looks for a JWT {@code zip} header, and if found, returns the corresponding {@link CompressionCodec} the parser * can use to decompress the JWT body. * *

JJWT's default {@link JwtParser} implementation supports both the - * {@link io.jsonwebtoken.impl.compression.DeflateCompressionCodec DEFLATE} - * and {@link io.jsonwebtoken.impl.compression.GzipCompressionCodec GZIP} algorithms by default - you do not need to + * {@link CompressionCodecs#DEFLATE DEFLATE} + * and {@link CompressionCodecs#GZIP GZIP} algorithms by default - you do not need to * specify a {@code CompressionCodecResolver} in these cases.

* *

However, if you want to use a compression algorithm other than {@code DEF} or {@code GZIP}, you must implement @@ -34,12 +34,12 @@ public interface CompressionCodecResolver { /** - * Looks for a JWT {@code calg} header, and if found, returns the corresponding {@link CompressionCodec} the parser + * Looks for a JWT {@code zip} header, and if found, returns the corresponding {@link CompressionCodec} the parser * can use to decompress the JWT body. * * @param header of the JWT - * @return CompressionCodec matching the {@code calg} header, or null if there is no {@code calg} header. - * @throws CompressionException if a {@code calg} header value is found and not supported. + * @return CompressionCodec matching the {@code zip} header, or null if there is no {@code zip} header. + * @throws CompressionException if a {@code zip} header value is found and not supported. */ CompressionCodec resolveCompressionCodec(Header header) throws CompressionException; Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/CompressionCodecs.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/CompressionCodecs.java (.../CompressionCodecs.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/CompressionCodecs.java (.../CompressionCodecs.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -1,7 +1,21 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jsonwebtoken; -import io.jsonwebtoken.impl.compression.DeflateCompressionCodec; -import io.jsonwebtoken.impl.compression.GzipCompressionCodec; +import io.jsonwebtoken.lang.Classes; /** * Provides default implementations of the {@link CompressionCodec} interface. @@ -12,15 +26,15 @@ */ public final class CompressionCodecs { - private static final CompressionCodecs INSTANCE = new CompressionCodecs(); + private CompressionCodecs() { + } //prevent external instantiation - private CompressionCodecs() {} //prevent external instantiation - /** * Codec implementing the JWA standard * deflate compression algorithm */ - public static final CompressionCodec DEFLATE = new DeflateCompressionCodec(); + public static final CompressionCodec DEFLATE = + Classes.newInstance("io.jsonwebtoken.impl.compression.DeflateCompressionCodec"); /** * Codec implementing the gzip compression algorithm. @@ -29,6 +43,7 @@ * that all parties accessing the token support the gzip algorithm.

*

If you're concerned about compatibility, the {@link #DEFLATE DEFLATE} code is JWA standards-compliant.

*/ - public static final CompressionCodec GZIP = new GzipCompressionCodec(); + public static final CompressionCodec GZIP = + Classes.newInstance("io.jsonwebtoken.impl.compression.GzipCompressionCodec"); } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Header.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Header.java (.../Header.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Header.java (.../Header.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -109,24 +109,24 @@ T setContentType(String cty); /** - * Returns the JWT calg (Compression Algorithm) header value or {@code null} if not present. + * Returns the JWT zip (Compression Algorithm) header value or {@code null} if not present. * - * @return the {@code calg} header parameter value or {@code null} if not present. + * @return the {@code zip} header parameter value or {@code null} if not present. * @since 0.6.0 */ String getCompressionAlgorithm(); /** - * Sets the JWT calg (Compression Algorithm) header parameter value. A {@code null} value will remove + * Sets the JWT zip (Compression Algorithm) header parameter value. A {@code null} value will remove * the property from the JSON map. *

*

The compression algorithm is NOT part of the JWT specification * and must be used carefully since, is not expected that other libraries (including previous versions of this one) * be able to deserialize a compressed JTW body correctly.

* - * @param calg the JWT compression algorithm {@code calg} value or {@code null} to remove the property from the JSON map. + * @param zip the JWT compression algorithm {@code zip} value or {@code null} to remove the property from the JSON map. * @since 0.6.0 */ - T setCompressionAlgorithm(String calg); + T setCompressionAlgorithm(String zip); } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/IncorrectClaimException.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/IncorrectClaimException.java (.../IncorrectClaimException.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/IncorrectClaimException.java (.../IncorrectClaimException.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -22,6 +22,7 @@ * @since 0.6 */ public class IncorrectClaimException extends InvalidClaimException { + public IncorrectClaimException(Header header, Claims claims, String message) { super(header, claims, message); } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/InvalidClaimException.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/InvalidClaimException.java (.../InvalidClaimException.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/InvalidClaimException.java (.../InvalidClaimException.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -25,6 +25,7 @@ * @since 0.6 */ public class InvalidClaimException extends ClaimJwtException { + private String claimName; private Object claimValue; Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/JwtBuilder.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/JwtBuilder.java (.../JwtBuilder.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/JwtBuilder.java (.../JwtBuilder.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,6 +15,12 @@ */ package io.jsonwebtoken; +import io.jsonwebtoken.io.Decoder; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.io.Encoder; +import io.jsonwebtoken.io.Serializer; +import io.jsonwebtoken.security.InvalidKeyException; +import io.jsonwebtoken.security.Keys; import java.security.Key; import java.util.Date; import java.util.Map; @@ -99,7 +105,7 @@ * @param claims the JWT claims to be set as the JWT body. * @return the builder for method chaining. */ - JwtBuilder setClaims(Map claims); + JwtBuilder setClaims(Map claims); /** * Adds all given name/value pairs to the JSON Claims in the payload. If a Claims instance does not yet exist at the @@ -136,7 +142,8 @@ * @return the builder instance for method chaining. * @since 0.2 */ - @Override //only for better/targeted JavaDoc + @Override + //only for better/targeted JavaDoc JwtBuilder setIssuer(String iss); /** @@ -162,7 +169,8 @@ * @return the builder instance for method chaining. * @since 0.2 */ - @Override //only for better/targeted JavaDoc + @Override + //only for better/targeted JavaDoc JwtBuilder setSubject(String sub); /** @@ -179,7 +187,7 @@ * *

instead of this:

*
-     * Claims claims = Jwts.claims().setSubject("You");
+     * Claims claims = Jwts.claims().setAudience("You");
      * String jwt = Jwts.builder().setClaims(claims).compact();
      * 
*

if desired.

@@ -188,7 +196,8 @@ * @return the builder instance for method chaining. * @since 0.2 */ - @Override //only for better/targeted JavaDoc + @Override + //only for better/targeted JavaDoc JwtBuilder setAudience(String aud); /** @@ -216,7 +225,8 @@ * @return the builder instance for method chaining. * @since 0.2 */ - @Override //only for better/targeted JavaDoc + @Override + //only for better/targeted JavaDoc JwtBuilder setExpiration(Date exp); /** @@ -244,7 +254,8 @@ * @return the builder instance for method chaining. * @since 0.2 */ - @Override //only for better/targeted JavaDoc + @Override + //only for better/targeted JavaDoc JwtBuilder setNotBefore(Date nbf); /** @@ -272,7 +283,8 @@ * @return the builder instance for method chaining. * @since 0.2 */ - @Override //only for better/targeted JavaDoc + @Override + //only for better/targeted JavaDoc JwtBuilder setIssuedAt(Date iat); /** @@ -293,7 +305,7 @@ * *

instead of this:

*
-     * Claims claims = Jwts.claims().setIssuedAt(UUID.randomUUID().toString());
+     * Claims claims = Jwts.claims().setId(UUID.randomUUID().toString());
      * String jwt = Jwts.builder().setClaims(claims).compact();
      * 
*

if desired.

@@ -302,7 +314,8 @@ * @return the builder instance for method chaining. * @since 0.2 */ - @Override //only for better/targeted JavaDoc + @Override + //only for better/targeted JavaDoc JwtBuilder setId(String jti); /** @@ -323,45 +336,139 @@ * *

if desired.

* - * @param name the JWT Claims property name + * @param name the JWT Claims property name * @param value the value to set for the specified Claims property name * @return the builder instance for method chaining. * @since 0.2 */ JwtBuilder claim(String name, Object value); /** + * Signs the constructed JWT with the specified key using the key's + * {@link SignatureAlgorithm#forSigningKey(Key) recommended signature algorithm}, producing a JWS. If the + * recommended signature algorithm isn't sufficient for your needs, consider using + * {@link #signWith(Key, SignatureAlgorithm)} instead. + * + *

If you are looking to invoke this method with a byte array that you are confident may be used for HMAC-SHA + * algorithms, consider using {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(bytes)} to + * convert the byte array into a valid {@code Key}.

+ * + * @param key the key to use for signing + * @return the builder instance for method chaining. + * @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification as + * described by {@link SignatureAlgorithm#forSigningKey(Key)}. + * @see #signWith(Key, SignatureAlgorithm) + * @since 0.10.0 + */ + JwtBuilder signWith(Key key) throws InvalidKeyException; + + /** * Signs the constructed JWT using the specified algorithm with the specified key, producing a JWS. * + *

Deprecation Notice: Deprecated as of 0.10.0

+ * + *

Use {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(bytes)} to + * obtain the {@code Key} and then invoke {@link #signWith(Key)} or {@link #signWith(Key, SignatureAlgorithm)}.

+ * + *

This method will be removed in the 1.0 release.

+ * * @param alg the JWS algorithm to use to digitally sign the JWT, thereby producing a JWS. * @param secretKey the algorithm-specific signing key to use to digitally sign the JWT. * @return the builder for method chaining. + * @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification as + * described by {@link SignatureAlgorithm#forSigningKey(Key)}. + * @deprecated as of 0.10.0: use {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(bytes)} to + * obtain the {@code Key} and then invoke {@link #signWith(Key)} or {@link #signWith(Key, SignatureAlgorithm)}. + * This method will be removed in the 1.0 release. */ - JwtBuilder signWith(SignatureAlgorithm alg, byte[] secretKey); + @Deprecated + JwtBuilder signWith(SignatureAlgorithm alg, byte[] secretKey) throws InvalidKeyException; /** * Signs the constructed JWT using the specified algorithm with the specified key, producing a JWS. * *

This is a convenience method: the string argument is first BASE64-decoded to a byte array and this resulting * byte array is used to invoke {@link #signWith(SignatureAlgorithm, byte[])}.

* + *

Deprecation Notice: Deprecated as of 0.10.0, will be removed in the 1.0 release.

+ * + *

This method has been deprecated because the {@code key} argument for this method can be confusing: keys for + * cryptographic operations are always binary (byte arrays), and many people were confused as to how bytes were + * obtained from the String argument.

+ * + *

This method always expected a String argument that was effectively the same as the result of the following + * (pseudocode):

+ * + *

{@code String base64EncodedSecretKey = base64Encode(secretKeyBytes);}

+ * + *

However, a non-trivial number of JJWT users were confused by the method signature and attempted to + * use raw password strings as the key argument - for example {@code signWith(HS256, myPassword)} - which is + * almost always incorrect for cryptographic hashes and can produce erroneous or insecure results.

+ * + *

See this + * + * StackOverflow answer explaining why raw (non-base64-encoded) strings are almost always incorrect for + * signature operations.

+ * + *

To perform the correct logic with base64EncodedSecretKey strings with JJWT >= 0.10.0, you may do this: + *


+     * byte[] keyBytes = {@link Decoders Decoders}.{@link Decoders#BASE64 BASE64}.{@link Decoder#decode(Object) decode(base64EncodedSecretKey)};
+     * Key key = {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(keyBytes)};
+     * jwtBuilder.signWith(key); //or {@link #signWith(Key, SignatureAlgorithm)}
+     * 
+ *

+ * + *

This method will be removed in the 1.0 release.

+ * * @param alg the JWS algorithm to use to digitally sign the JWT, thereby producing a JWS. * @param base64EncodedSecretKey the BASE64-encoded algorithm-specific signing key to use to digitally sign the * JWT. * @return the builder for method chaining. + * @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification as + * described by {@link SignatureAlgorithm#forSigningKey(Key)}. + * @deprecated as of 0.10.0: use {@link #signWith(Key)} or {@link #signWith(Key, SignatureAlgorithm)} instead. This + * method will be removed in the 1.0 release. */ - JwtBuilder signWith(SignatureAlgorithm alg, String base64EncodedSecretKey); + @Deprecated + JwtBuilder signWith(SignatureAlgorithm alg, String base64EncodedSecretKey) throws InvalidKeyException; /** * Signs the constructed JWT using the specified algorithm with the specified key, producing a JWS. * + *

It is typically recommended to call the {@link #signWith(Key)} instead for simplicity. + * However, this method can be useful if the recommended algorithm heuristics do not meet your needs or if + * you want explicit control over the signature algorithm used with the specified key.

+ * * @param alg the JWS algorithm to use to digitally sign the JWT, thereby producing a JWS. * @param key the algorithm-specific signing key to use to digitally sign the JWT. * @return the builder for method chaining. + * @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification for + * the specified algorithm. + * @see #signWith(Key) + * @deprecated since 0.10.0: use {@link #signWith(Key, SignatureAlgorithm)} instead. This method will be removed + * in the 1.0 release. */ - JwtBuilder signWith(SignatureAlgorithm alg, Key key); + @Deprecated + JwtBuilder signWith(SignatureAlgorithm alg, Key key) throws InvalidKeyException; /** + * Signs the constructed JWT with the specified key using the specified algorithm, producing a JWS. + * + *

It is typically recommended to call the {@link #signWith(Key)} instead for simplicity. + * However, this method can be useful if the recommended algorithm heuristics do not meet your needs or if + * you want explicit control over the signature algorithm used with the specified key.

+ * + * @param key the signing key to use to digitally sign the JWT. + * @param alg the JWS algorithm to use with the key to digitally sign the JWT, thereby producing a JWS. + * @return the builder for method chaining. + * @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification for + * the specified algorithm. + * @see #signWith(Key) + * @since 0.10.0 + */ + JwtBuilder signWith(Key key, SignatureAlgorithm alg) throws InvalidKeyException; + + /** * Compresses the JWT body using the specified {@link CompressionCodec}. * *

If your compact JWTs are large, and you want to reduce their total size during network transmission, this @@ -379,15 +486,40 @@ *

Compression when creating JWE tokens however should be universally accepted for any * library that supports JWE.

* - * @see io.jsonwebtoken.CompressionCodecs - * * @param codec implementation of the {@link CompressionCodec} to be used. * @return the builder for method chaining. + * @see io.jsonwebtoken.CompressionCodecs * @since 0.6.0 */ JwtBuilder compressWith(CompressionCodec codec); /** + * Perform Base64Url encoding with the specified Encoder. + * + *

JJWT uses a spec-compliant encoder that works on all supported JDK versions, but you may call this method + * to specify a different encoder if you desire.

+ * + * @param base64UrlEncoder the encoder to use when Base64Url-encoding + * @return the builder for method chaining. + * @since 0.10.0 + */ + JwtBuilder base64UrlEncodeWith(Encoder base64UrlEncoder); + + /** + * Performs object-to-JSON serialization with the specified Serializer. This is used by the builder to convert + * JWT/JWS/JWT headers and claims Maps to JSON strings as required by the JWT specification. + * + *

If this method is not called, JJWT will use whatever serializer it can find at runtime, checking for the + * presence of well-known implementations such Jackson, Gson, and org.json. If one of these is not found + * in the runtime classpath, an exception will be thrown when the {@link #compact()} method is invoked.

+ * + * @param serializer the serializer to use when converting Map objects to JSON strings. + * @return the builder for method chaining. + * @since 0.10.0 + */ + JwtBuilder serializeToJsonWith(Serializer> serializer); + + /** * Actually builds the JWT and serializes it to a compact, URL-safe string according to the * JWT Compact Serialization * rules. Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/JwtParser.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/JwtParser.java (.../JwtParser.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/JwtParser.java (.../JwtParser.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,10 +15,13 @@ */ package io.jsonwebtoken; -import io.jsonwebtoken.impl.DefaultClock; +import io.jsonwebtoken.io.Decoder; +import io.jsonwebtoken.io.Deserializer; +import io.jsonwebtoken.security.SignatureException; import java.security.Key; import java.util.Date; +import java.util.Map; /** * A parser for reading JWT strings, used to convert them into a {@link Jwt} object representing the expanded JWT. @@ -38,7 +41,12 @@ * @return the parser method for chaining. * @see MissingClaimException * @see IncorrectClaimException + * @deprecated see {@link JwtParserBuilder#requireId(String)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser requireId(String id); /** @@ -50,7 +58,12 @@ * @return the parser for method chaining. * @see MissingClaimException * @see IncorrectClaimException + * @deprecated see {@link JwtParserBuilder#requireSubject(String)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser requireSubject(String subject); /** @@ -62,7 +75,12 @@ * @return the parser for method chaining. * @see MissingClaimException * @see IncorrectClaimException + * @deprecated see {@link JwtParserBuilder#requireAudience(String)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser requireAudience(String audience); /** @@ -74,7 +92,12 @@ * @return the parser for method chaining. * @see MissingClaimException * @see IncorrectClaimException + * @deprecated see {@link JwtParserBuilder#requireIssuer(String)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser requireIssuer(String issuer); /** @@ -86,7 +109,12 @@ * @return the parser for method chaining. * @see MissingClaimException * @see IncorrectClaimException + * @deprecated see {@link JwtParserBuilder#requireIssuedAt(Date)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser requireIssuedAt(Date issuedAt); /** @@ -98,7 +126,12 @@ * @return the parser for method chaining. * @see MissingClaimException * @see IncorrectClaimException + * @deprecated see {@link JwtParserBuilder#requireExpiration(Date)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser requireExpiration(Date expiration); /** @@ -110,7 +143,12 @@ * @return the parser for method chaining * @see MissingClaimException * @see IncorrectClaimException + * @deprecated see {@link JwtParserBuilder#requireNotBefore(Date)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser requireNotBefore(Date notBefore); /** @@ -123,17 +161,27 @@ * @return the parser for method chaining. * @see MissingClaimException * @see IncorrectClaimException + * @deprecated see {@link JwtParserBuilder#require(String, Object)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser require(String claimName, Object value); /** * Sets the {@link Clock} that determines the timestamp to use when validating the parsed JWT. - * The parser uses a {@link DefaultClock DefaultClock} instance by default. + * The parser uses a default Clock implementation that simply returns {@code new Date()} when called. * * @param clock a {@code Clock} object to return the timestamp to use when validating the parsed JWT. * @return the parser for method chaining. * @since 0.7.0 + * @deprecated see {@link JwtParserBuilder#setClock(Clock)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser setClock(Clock clock); /** @@ -143,8 +191,15 @@ * @param seconds the number of seconds to tolerate for clock skew when verifying {@code exp} or {@code nbf} claims. * @return the parser for method chaining. * @since 0.7.0 + * @throws IllegalArgumentException if {@code seconds} is a value greater than {@code Long.MAX_VALUE / 1000} as + * any such value would cause numeric overflow when multiplying by 1000 to obtain a millisecond value. + * @deprecated see {@link JwtParserBuilder#setAllowedClockSkewSeconds(long)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ - JwtParser setAllowedClockSkewSeconds(long seconds); + @Deprecated + JwtParser setAllowedClockSkewSeconds(long seconds) throws IllegalArgumentException; /** * Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not @@ -158,26 +213,59 @@ * @param key the algorithm-specific signature verification key used to validate any discovered JWS digital * signature. * @return the parser for method chaining. + * @deprecated see {@link JwtParserBuilder#setSigningKey(byte[])}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser setSigningKey(byte[] key); /** * Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not * a JWS (no signature), this key is not used. - *

+ * *

Note that this key MUST be a valid key for the signature algorithm found in the JWT header * (as the {@code alg} header parameter).

- *

+ * *

This method overwrites any previously set key.

- *

+ * *

This is a convenience method: the string argument is first BASE64-decoded to a byte array and this resulting * byte array is used to invoke {@link #setSigningKey(byte[])}.

* - * @param base64EncodedKeyBytes the BASE64-encoded algorithm-specific signature verification key to use to validate - * any discovered JWS digital signature. + *

Deprecation Notice: Deprecated as of 0.10.0, will be removed in 1.0.0

+ * + *

This method has been deprecated because the {@code key} argument for this method can be confusing: keys for + * cryptographic operations are always binary (byte arrays), and many people were confused as to how bytes were + * obtained from the String argument.

+ * + *

This method always expected a String argument that was effectively the same as the result of the following + * (pseudocode):

+ * + *

{@code String base64EncodedSecretKey = base64Encode(secretKeyBytes);}

+ * + *

However, a non-trivial number of JJWT users were confused by the method signature and attempted to + * use raw password strings as the key argument - for example {@code setSigningKey(myPassword)} - which is + * almost always incorrect for cryptographic hashes and can produce erroneous or insecure results.

+ * + *

See this + * + * StackOverflow answer explaining why raw (non-base64-encoded) strings are almost always incorrect for + * signature operations.

+ * + *

Finally, please use the {@link #setSigningKey(Key) setSigningKey(Key)} instead, as this method and the + * {@code byte[]} variant will be removed before the 1.0.0 release.

+ * + * @param base64EncodedSecretKey the BASE64-encoded algorithm-specific signature verification key to use to validate + * any discovered JWS digital signature. * @return the parser for method chaining. + * @deprecated see {@link JwtParserBuilder#setSigningKey(String)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ - JwtParser setSigningKey(String base64EncodedKeyBytes); + @Deprecated + JwtParser setSigningKey(String base64EncodedSecretKey); /** * Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not @@ -191,7 +279,12 @@ * @param key the algorithm-specific signature verification key to use to validate any discovered JWS digital * signature. * @return the parser for method chaining. + * @deprecated see {@link JwtParserBuilder#setSigningKey(Key)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser setSigningKey(Key key); /** @@ -221,7 +314,12 @@ * @param signingKeyResolver the signing key resolver used to retrieve the signing key. * @return the parser for method chaining. * @since 0.4 + * @deprecated see {@link JwtParserBuilder#setSigningKeyResolver(SigningKeyResolver)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser setSigningKeyResolver(SigningKeyResolver signingKeyResolver); /** @@ -233,8 +331,8 @@ * the same behavior.

*

Default Support

*

JJWT's default {@link JwtParser} implementation supports both the - * {@link io.jsonwebtoken.impl.compression.DeflateCompressionCodec DEFLATE} - * and {@link io.jsonwebtoken.impl.compression.GzipCompressionCodec GZIP} algorithms by default - you do not need to + * {@link CompressionCodecs#DEFLATE DEFLATE} + * and {@link CompressionCodecs#GZIP GZIP} algorithms by default - you do not need to * specify a {@code CompressionCodecResolver} in these cases.

*

However, if you want to use a compression algorithm other than {@code DEF} or {@code GZIP}, you must implement * your own {@link CompressionCodecResolver} and specify that via this method and also when @@ -243,10 +341,53 @@ * @param compressionCodecResolver the compression codec resolver used to decompress the JWT body. * @return the parser for method chaining. * @since 0.6.0 + * @deprecated see {@link JwtParserBuilder#setCompressionCodecResolver(CompressionCodecResolver)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 */ + @Deprecated JwtParser setCompressionCodecResolver(CompressionCodecResolver compressionCodecResolver); /** + * Perform Base64Url decoding with the specified Decoder + * + *

JJWT uses a spec-compliant decoder that works on all supported JDK versions, but you may call this method + * to specify a different decoder if you desire.

+ * + * @param base64UrlDecoder the decoder to use when Base64Url-decoding + * @return the parser for method chaining. + * @since 0.10.0 + * @deprecated see {@link JwtParserBuilder#base64UrlDecodeWith(Decoder)}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 + */ + @Deprecated + JwtParser base64UrlDecodeWith(Decoder base64UrlDecoder); + + /** + * Uses the specified deserializer to convert JSON Strings (UTF-8 byte arrays) into Java Map objects. This is + * used by the parser after Base64Url-decoding to convert JWT/JWS/JWT JSON headers and claims into Java Map + * objects. + * + *

If this method is not called, JJWT will use whatever deserializer it can find at runtime, checking for the + * presence of well-known implementations such Jackson, Gson, and org.json. If one of these is not found + * in the runtime classpath, an exception will be thrown when one of the various {@code parse}* methods is + * invoked.

+ * + * @param deserializer the deserializer to use when converting JSON Strings (UTF-8 byte arrays) into Map objects. + * @return the parser for method chaining. + * @since 0.10.0 + * @deprecated see {@link JwtParserBuilder#deserializeJsonWith(Deserializer)} )}. + * To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an + * immutable JwtParser. + *

NOTE: this method will be removed before version 1.0 + */ + @Deprecated + JwtParser deserializeJsonWith(Deserializer> deserializer); + + /** * Returns {@code true} if the specified JWT compact string represents a signed JWT (aka a 'JWS'), {@code false} * otherwise. *

@@ -333,7 +474,7 @@ * @since 0.2 */ T parse(String jwt, JwtHandler handler) - throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; + throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; /** * Parses the specified compact serialized JWT string based on the builder's current configuration state and @@ -363,7 +504,7 @@ * @since 0.2 */ Jwt parsePlaintextJwt(String plaintextJwt) - throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; + throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; /** * Parses the specified compact serialized JWT string based on the builder's current configuration state and @@ -394,7 +535,7 @@ * @since 0.2 */ Jwt parseClaimsJwt(String claimsJwt) - throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; + throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; /** * Parses the specified compact serialized JWS string based on the builder's current configuration state and @@ -422,7 +563,7 @@ * @since 0.2 */ Jws parsePlaintextJws(String plaintextJws) - throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; + throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; /** * Parses the specified compact serialized JWS string based on the builder's current configuration state and @@ -451,5 +592,5 @@ * @since 0.2 */ Jws parseClaimsJws(String claimsJws) - throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; + throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/JwtParserBuilder.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/JwtParserBuilder.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/JwtParserBuilder.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2019 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken; + +import io.jsonwebtoken.io.Decoder; +import io.jsonwebtoken.io.Deserializer; + +import java.security.Key; +import java.util.Date; +import java.util.Map; + +/** + * A builder to construct a {@link JwtParser}. Example usage: + *

{@code
+ *     Jwts.parserBuilder()
+ *         .setSigningKey(...)
+ *         .requireIssuer("https://issuer.example.com")
+ *         .build()
+ *         .parse(jwtString)
+ * }
+ * @since 0.11.0 + */ +public interface JwtParserBuilder { + + /** + * Ensures that the specified {@code jti} exists in the parsed JWT. If missing or if the parsed + * 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 + * @return the parser builder for method chaining. + * @see MissingClaimException + * @see IncorrectClaimException + */ + JwtParserBuilder requireId(String id); + + /** + * Ensures that the specified {@code sub} exists in the parsed JWT. If missing or if the parsed + * 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 + * @return the parser builder for method chaining. + * @see MissingClaimException + * @see IncorrectClaimException + */ + JwtParserBuilder requireSubject(String subject); + + /** + * Ensures that the specified {@code aud} exists in the parsed JWT. If missing or if the parsed + * 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 + * @return the parser builder for method chaining. + * @see MissingClaimException + * @see IncorrectClaimException + */ + JwtParserBuilder requireAudience(String audience); + + /** + * Ensures that the specified {@code iss} exists in the parsed JWT. If missing or if the parsed + * 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 + * @return the parser builder for method chaining. + * @see MissingClaimException + * @see IncorrectClaimException + */ + JwtParserBuilder requireIssuer(String issuer); + + /** + * Ensures that the specified {@code iat} exists in the parsed JWT. If missing or if the parsed + * 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 + * @return the parser builder for method chaining. + * @see MissingClaimException + * @see IncorrectClaimException + */ + JwtParserBuilder requireIssuedAt(Date issuedAt); + + /** + * Ensures that the specified {@code exp} exists in the parsed JWT. If missing or if the parsed + * 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 + * @return the parser builder for method chaining. + * @see MissingClaimException + * @see IncorrectClaimException + */ + JwtParserBuilder requireExpiration(Date expiration); + + /** + * Ensures that the specified {@code nbf} exists in the parsed JWT. If missing or if the parsed + * 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 + * @return the parser builder for method chaining + * @see MissingClaimException + * @see IncorrectClaimException + */ + JwtParserBuilder requireNotBefore(Date notBefore); + + /** + * Ensures that the specified {@code claimName} exists in the parsed JWT. If missing or if the parsed + * 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 + * @return the parser builder for method chaining. + * @see MissingClaimException + * @see IncorrectClaimException + */ + JwtParserBuilder require(String claimName, Object value); + + /** + * Sets the {@link Clock} that determines the timestamp to use when validating the parsed JWT. + * The parser uses a default Clock implementation that simply returns {@code new Date()} when called. + * + * @param clock a {@code Clock} object to return the timestamp to use when validating the parsed JWT. + * @return the parser builder for method chaining. + */ + JwtParserBuilder setClock(Clock clock); + + /** + * Sets the amount of clock skew in seconds to tolerate when verifying the local time against the {@code exp} + * and {@code nbf} claims. + * + * @param seconds the number of seconds to tolerate for clock skew when verifying {@code exp} or {@code nbf} claims. + * @return the parser builder for method chaining. + * @throws IllegalArgumentException if {@code seconds} is a value greater than {@code Long.MAX_VALUE / 1000} as + * any such value would cause numeric overflow when multiplying by 1000 to obtain a millisecond value. + */ + JwtParserBuilder setAllowedClockSkewSeconds(long seconds) throws IllegalArgumentException; + + /** + * Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not + * a JWS (no signature), this key is not used. + *

+ *

Note that this key MUST be a valid key for the signature algorithm found in the JWT header + * (as the {@code alg} header parameter).

+ *

+ *

This method overwrites any previously set key.

+ * + * @param key the algorithm-specific signature verification key used to validate any discovered JWS digital + * signature. + * @return the parser builder for method chaining. + */ + JwtParserBuilder setSigningKey(byte[] key); + + /** + * Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not + * a JWS (no signature), this key is not used. + * + *

Note that this key MUST be a valid key for the signature algorithm found in the JWT header + * (as the {@code alg} header parameter).

+ * + *

This method overwrites any previously set key.

+ * + *

This is a convenience method: the string argument is first BASE64-decoded to a byte array and this resulting + * byte array is used to invoke {@link #setSigningKey(byte[])}.

+ * + *

Deprecation Notice: Deprecated as of 0.10.0, will be removed in 1.0.0

+ * + *

This method has been deprecated because the {@code key} argument for this method can be confusing: keys for + * cryptographic operations are always binary (byte arrays), and many people were confused as to how bytes were + * obtained from the String argument.

+ * + *

This method always expected a String argument that was effectively the same as the result of the following + * (pseudocode):

+ * + *

{@code String base64EncodedSecretKey = base64Encode(secretKeyBytes);}

+ * + *

However, a non-trivial number of JJWT users were confused by the method signature and attempted to + * use raw password strings as the key argument - for example {@code setSigningKey(myPassword)} - which is + * almost always incorrect for cryptographic hashes and can produce erroneous or insecure results.

+ * + *

See this + * + * StackOverflow answer explaining why raw (non-base64-encoded) strings are almost always incorrect for + * signature operations.

+ * + *

Finally, please use the {@link #setSigningKey(Key) setSigningKey(Key)} instead, as this method and the + * {@code byte[]} variant will be removed before the 1.0.0 release.

+ * + * @param base64EncodedSecretKey the BASE64-encoded algorithm-specific signature verification key to use to validate + * any discovered JWS digital signature. + * @return the parser builder for method chaining. + */ + JwtParserBuilder setSigningKey(String base64EncodedSecretKey); + + /** + * Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not + * a JWS (no signature), this key is not used. + *

+ *

Note that this key MUST be a valid key for the signature algorithm found in the JWT header + * (as the {@code alg} header parameter).

+ *

+ *

This method overwrites any previously set key.

+ * + * @param key the algorithm-specific signature verification key to use to validate any discovered JWS digital + * signature. + * @return the parser builder for method chaining. + */ + JwtParserBuilder setSigningKey(Key key); + + /** + * Sets the {@link SigningKeyResolver} used to acquire the signing key 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. + *

+ *

Specifying a {@code SigningKeyResolver} is necessary when the signing key is not already known before parsing + * the JWT and the JWT header or payload (plaintext body or Claims) must be inspected first to determine how to + * look up the signing key. Once returned by the resolver, the JwtParser will then verify the JWS signature with the + * returned key. For example:

+ *

+ *

+     * Jws<Claims> jws = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {
+     *         @Override
+     *         public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {
+     *             //inspect the header or claims, lookup and return the signing key
+     *             return getSigningKey(header, claims); //implement me
+     *         }})
+     *     .parseClaimsJws(compact);
+     * 
+ *

+ *

A {@code SigningKeyResolver} is invoked once during parsing before the signature is verified.

+ *

+ *

This method should only be used if a signing key is not provided by the other {@code setSigningKey*} builder + * methods.

+ * + * @param signingKeyResolver the signing key resolver used to retrieve the signing key. + * @return the parser builder for method chaining. + */ + JwtParserBuilder setSigningKeyResolver(SigningKeyResolver signingKeyResolver); + + /** + * 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. + *

NOTE: Compression is not defined by the JWT Specification, and it is not expected that other libraries + * (including JJWT versions < 0.6.0) are able to consume a compressed JWT body correctly. This method is only + * useful if the compact JWT was compressed with JJWT >= 0.6.0 or another library that you know implements + * the same behavior.

+ *

Default Support

+ *

JJWT's default {@link JwtParser} implementation supports both the + * {@link CompressionCodecs#DEFLATE DEFLATE} + * and {@link CompressionCodecs#GZIP GZIP} algorithms by default - you do not need to + * specify a {@code CompressionCodecResolver} in these cases.

+ *

However, if you want to use a compression algorithm other than {@code DEF} or {@code GZIP}, you must implement + * your own {@link CompressionCodecResolver} and specify that via this method and also when + * {@link io.jsonwebtoken.JwtBuilder#compressWith(CompressionCodec) building} JWTs.

+ * + * @param compressionCodecResolver the compression codec resolver used to decompress the JWT body. + * @return the parser builder for method chaining. + */ + JwtParserBuilder setCompressionCodecResolver(CompressionCodecResolver compressionCodecResolver); + + /** + * Perform Base64Url decoding with the specified Decoder + * + *

JJWT uses a spec-compliant decoder that works on all supported JDK versions, but you may call this method + * to specify a different decoder if you desire.

+ * + * @param base64UrlDecoder the decoder to use when Base64Url-decoding + * @return the parser builder for method chaining. + */ + JwtParserBuilder base64UrlDecodeWith(Decoder base64UrlDecoder); + + /** + * Uses the specified deserializer to convert JSON Strings (UTF-8 byte arrays) into Java Map objects. This is + * used by the parser after Base64Url-decoding to convert JWT/JWS/JWT JSON headers and claims into Java Map + * objects. + * + *

If this method is not called, JJWT will use whatever deserializer it can find at runtime, checking for the + * presence of well-known implementations such Jackson, Gson, and org.json. If one of these is not found + * in the runtime classpath, an exception will be thrown when one of the various {@code parse}* methods is + * invoked.

+ * + * @param deserializer the deserializer to use when converting JSON Strings (UTF-8 byte arrays) into Map objects. + * @return the builder for method chaining. + */ + JwtParserBuilder deserializeJsonWith(Deserializer> deserializer); + + /** + * Returns an immutable/thread-safe {@link JwtParser} created from the configuration from this JwtParserBuilder. + * @return an immutable/thread-safe JwtParser created from the configuration from this JwtParserBuilder. + */ + JwtParser build(); +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Jwts.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Jwts.java (.../Jwts.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/Jwts.java (.../Jwts.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,11 +15,7 @@ */ package io.jsonwebtoken; -import io.jsonwebtoken.impl.DefaultClaims; -import io.jsonwebtoken.impl.DefaultHeader; -import io.jsonwebtoken.impl.DefaultJwsHeader; -import io.jsonwebtoken.impl.DefaultJwtBuilder; -import io.jsonwebtoken.impl.DefaultJwtParser; +import io.jsonwebtoken.lang.Classes; import java.util.Map; @@ -31,8 +27,11 @@ */ public final class Jwts { - private Jwts(){} + private static final Class[] MAP_ARG = new Class[]{Map.class}; + private Jwts() { + } + /** * Creates a new {@link Header} instance suitable for plaintext (not digitally signed) JWTs. As this * is a less common use of JWTs, consider using the {@link #jwsHeader()} factory method instead if you will later @@ -41,7 +40,7 @@ * @return a new {@link Header} instance suitable for plaintext (not digitally signed) JWTs. */ public static Header header() { - return new DefaultHeader(); + return Classes.newInstance("io.jsonwebtoken.impl.DefaultHeader"); } /** @@ -52,7 +51,7 @@ * @return a new {@link Header} instance suitable for plaintext (not digitally signed) JWTs. */ public static Header header(Map header) { - return new DefaultHeader(header); + return Classes.newInstance("io.jsonwebtoken.impl.DefaultHeader", MAP_ARG, header); } /** @@ -62,7 +61,7 @@ * @see JwtBuilder#setHeader(Header) */ public static JwsHeader jwsHeader() { - return new DefaultJwsHeader(); + return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwsHeader"); } /** @@ -74,7 +73,7 @@ * @see JwtBuilder#setHeader(Header) */ public static JwsHeader jwsHeader(Map header) { - return new DefaultJwsHeader(header); + return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwsHeader", MAP_ARG, header); } /** @@ -83,7 +82,7 @@ * @return a new {@link Claims} instance to be used as a JWT body. */ public static Claims claims() { - return new DefaultClaims(); + return Classes.newInstance("io.jsonwebtoken.impl.DefaultClaims"); } /** @@ -93,26 +92,52 @@ * @return a new {@link Claims} instance populated with the specified name/value pairs. */ public static Claims claims(Map claims) { - return new DefaultClaims(claims); + return Classes.newInstance("io.jsonwebtoken.impl.DefaultClaims", MAP_ARG, claims); } /** * Returns a new {@link JwtParser} instance that can be configured and then used to parse JWT strings. * * @return a new {@link JwtParser} instance that can be configured and then used to parse JWT strings. + * @deprecated use {@link Jwts#parserBuilder()} instead. See {@link JwtParserBuilder} for usage details. + *

Migration to new method structure is minimal, for example: + *

Old code: + *

{@code
+     *     Jwts.parser()
+     *         .requireAudience("string")
+     *         .parse(jwtString)
+     * }
+ *

New code: + *

{@code
+     *     Jwts.parserBuilder()
+     *         .requireAudience("string")
+     *         .build()
+     *         .parse(jwtString)
+     * }
+ *

NOTE: this method will be removed before version 1.0 */ + @Deprecated public static JwtParser parser() { - return new DefaultJwtParser(); + return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwtParser"); } /** + * Returns a new {@link JwtParserBuilder} instance that can be configured to create an immutable/thread-safe {@link JwtParser). + * + * @return a new {@link JwtParser} instance that can be configured create an immutable/thread-safe {@link JwtParser). + */ + public static JwtParserBuilder parserBuilder() { + return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwtParserBuilder"); + } + + /** * Returns a new {@link JwtBuilder} instance that can be configured and then used to create JWT compact serialized * strings. * * @return a new {@link JwtBuilder} instance that can be configured and then used to create JWT compact serialized * strings. */ public static JwtBuilder builder() { - return new DefaultJwtBuilder(); + return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwtBuilder"); } } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/SignatureAlgorithm.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/SignatureAlgorithm.java (.../SignatureAlgorithm.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/SignatureAlgorithm.java (.../SignatureAlgorithm.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,8 +15,20 @@ */ package io.jsonwebtoken; -import io.jsonwebtoken.lang.RuntimeEnvironment; +import io.jsonwebtoken.security.InvalidKeyException; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; +import io.jsonwebtoken.security.WeakKeyException; +import javax.crypto.SecretKey; +import java.security.Key; +import java.security.PrivateKey; +import java.security.interfaces.ECKey; +import java.security.interfaces.RSAKey; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + /** * Type-safe representation of standard JWT signature algorithm names as defined in the * JSON Web Algorithms specification. @@ -25,85 +37,114 @@ */ public enum SignatureAlgorithm { - /** JWA name for {@code No digital signature or MAC performed} */ - NONE("none", "No digital signature or MAC performed", "None", null, false), + /** + * JWA name for {@code No digital signature or MAC performed} + */ + NONE("none", "No digital signature or MAC performed", "None", null, false, 0, 0), - /** JWA algorithm name for {@code HMAC using SHA-256} */ - HS256("HS256", "HMAC using SHA-256", "HMAC", "HmacSHA256", true), + /** + * JWA algorithm name for {@code HMAC using SHA-256} + */ + HS256("HS256", "HMAC using SHA-256", "HMAC", "HmacSHA256", true, 256, 256, "1.2.840.113549.2.9"), - /** JWA algorithm name for {@code HMAC using SHA-384} */ - HS384("HS384", "HMAC using SHA-384", "HMAC", "HmacSHA384", true), + /** + * JWA algorithm name for {@code HMAC using SHA-384} + */ + HS384("HS384", "HMAC using SHA-384", "HMAC", "HmacSHA384", true, 384, 384, "1.2.840.113549.2.10"), - /** JWA algorithm name for {@code HMAC using SHA-512} */ - HS512("HS512", "HMAC using SHA-512", "HMAC", "HmacSHA512", true), + /** + * JWA algorithm name for {@code HMAC using SHA-512} + */ + HS512("HS512", "HMAC using SHA-512", "HMAC", "HmacSHA512", true, 512, 512, "1.2.840.113549.2.11"), - /** JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-256} */ - RS256("RS256", "RSASSA-PKCS-v1_5 using SHA-256", "RSA", "SHA256withRSA", true), + /** + * JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-256} + */ + RS256("RS256", "RSASSA-PKCS-v1_5 using SHA-256", "RSA", "SHA256withRSA", true, 256, 2048), - /** JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-384} */ - RS384("RS384", "RSASSA-PKCS-v1_5 using SHA-384", "RSA", "SHA384withRSA", true), + /** + * JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-384} + */ + RS384("RS384", "RSASSA-PKCS-v1_5 using SHA-384", "RSA", "SHA384withRSA", true, 384, 2048), - /** JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-512} */ - RS512("RS512", "RSASSA-PKCS-v1_5 using SHA-512", "RSA", "SHA512withRSA", true), + /** + * JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-512} + */ + RS512("RS512", "RSASSA-PKCS-v1_5 using SHA-512", "RSA", "SHA512withRSA", true, 512, 2048), /** - * JWA algorithm name for {@code ECDSA using P-256 and SHA-256}. This is not a JDK standard algorithm and - * requires that a JCA provider like BouncyCastle be in the runtime classpath. BouncyCastle will be used - * automatically if found in the runtime classpath. + * JWA algorithm name for {@code ECDSA using P-256 and SHA-256} */ - ES256("ES256", "ECDSA using P-256 and SHA-256", "Elliptic Curve", "SHA256withECDSA", false), + ES256("ES256", "ECDSA using P-256 and SHA-256", "ECDSA", "SHA256withECDSA", true, 256, 256), /** - * JWA algorithm name for {@code ECDSA using P-384 and SHA-384}. This is not a JDK standard algorithm and - * requires that a JCA provider like BouncyCastle be in the runtime classpath. BouncyCastle will be used - * automatically if found in the runtime classpath. + * JWA algorithm name for {@code ECDSA using P-384 and SHA-384} */ - ES384("ES384", "ECDSA using P-384 and SHA-384", "Elliptic Curve", "SHA384withECDSA", false), + ES384("ES384", "ECDSA using P-384 and SHA-384", "ECDSA", "SHA384withECDSA", true, 384, 384), /** - * JWA algorithm name for {@code ECDSA using P-512 and SHA-512}. This is not a JDK standard algorithm and - * requires that a JCA provider like BouncyCastle be in the runtime classpath. BouncyCastle will be used - * automatically if found in the runtime classpath. + * JWA algorithm name for {@code ECDSA using P-521 and SHA-512} */ - ES512("ES512", "ECDSA using P-512 and SHA-512", "Elliptic Curve", "SHA512withECDSA", false), + ES512("ES512", "ECDSA using P-521 and SHA-512", "ECDSA", "SHA512withECDSA", true, 512, 521), /** - * JWA algorithm name for {@code RSASSA-PSS using SHA-256 and MGF1 with SHA-256}. This is not a JDK standard - * algorithm and requires that a JCA provider like BouncyCastle be in the runtime classpath. BouncyCastle - * will be used automatically if found in the runtime classpath. + * JWA algorithm name for {@code RSASSA-PSS using SHA-256 and MGF1 with SHA-256}. This algorithm requires + * Java 11 or later or a JCA provider like BouncyCastle to be in the runtime classpath. If on Java 10 or + * earlier, BouncyCastle will be used automatically if found in the runtime classpath. */ - PS256("PS256", "RSASSA-PSS using SHA-256 and MGF1 with SHA-256", "RSA", "SHA256withRSAandMGF1", false), + PS256("PS256", "RSASSA-PSS using SHA-256 and MGF1 with SHA-256", "RSA", "RSASSA-PSS", false, 256, 2048), /** - * JWA algorithm name for {@code RSASSA-PSS using SHA-384 and MGF1 with SHA-384}. This is not a JDK standard - * algorithm and requires that a JCA provider like BouncyCastle be in the runtime classpath. BouncyCastle - * will be used automatically if found in the runtime classpath. + * JWA algorithm name for {@code RSASSA-PSS using SHA-384 and MGF1 with SHA-384}. This algorithm requires + * Java 11 or later or a JCA provider like BouncyCastle to be in the runtime classpath. If on Java 10 or + * earlier, BouncyCastle will be used automatically if found in the runtime classpath. */ - PS384("PS384", "RSASSA-PSS using SHA-384 and MGF1 with SHA-384", "RSA", "SHA384withRSAandMGF1", false), + PS384("PS384", "RSASSA-PSS using SHA-384 and MGF1 with SHA-384", "RSA", "RSASSA-PSS", false, 384, 2048), /** - * JWA algorithm name for {@code RSASSA-PSS using SHA-512 and MGF1 with SHA-512}. This is not a JDK standard - * algorithm and requires that a JCA provider like BouncyCastle be in the classpath. BouncyCastle will be used - * automatically if found in the runtime classpath. + * JWA algorithm name for {@code RSASSA-PSS using SHA-512 and MGF1 with SHA-512}. This algorithm requires + * Java 11 or later or a JCA provider like BouncyCastle to be in the runtime classpath. If on Java 10 or + * earlier, BouncyCastle will be used automatically if found in the runtime classpath. */ - PS512("PS512", "RSASSA-PSS using SHA-512 and MGF1 with SHA-512", "RSA", "SHA512withRSAandMGF1", false); + PS512("PS512", "RSASSA-PSS using SHA-512 and MGF1 with SHA-512", "RSA", "RSASSA-PSS", false, 512, 2048); - static { - RuntimeEnvironment.enableBouncyCastleIfPossible(); - } + //purposefully ordered higher to lower: + private static final List PREFERRED_HMAC_ALGS = Collections.unmodifiableList(Arrays.asList( + SignatureAlgorithm.HS512, SignatureAlgorithm.HS384, SignatureAlgorithm.HS256)); + //purposefully ordered higher to lower: + private static final List PREFERRED_EC_ALGS = Collections.unmodifiableList(Arrays.asList( + SignatureAlgorithm.ES512, SignatureAlgorithm.ES384, SignatureAlgorithm.ES256)); - private final String value; - private final String description; - private final String familyName; - private final String jcaName; + private final String value; + private final String description; + private final String familyName; + private final String jcaName; private final boolean jdkStandard; + private final int digestLength; + private final int minKeyLength; + /** + * Algorithm name as given by {@link Key#getAlgorithm()} if the key was loaded from a pkcs12 Keystore. + * + * @deprecated This is just a workaround for https://bugs.openjdk.java.net/browse/JDK-8243551 + */ + @Deprecated + private final String pkcs12Name; - SignatureAlgorithm(String value, String description, String familyName, String jcaName, boolean jdkStandard) { + SignatureAlgorithm(String value, String description, String familyName, String jcaName, boolean jdkStandard, + int digestLength, int minKeyLength) { + this(value, description,familyName, jcaName, jdkStandard, digestLength, minKeyLength, jcaName); + } + + SignatureAlgorithm(String value, String description, String familyName, String jcaName, boolean jdkStandard, + int digestLength, int minKeyLength, String pkcs12Name) { this.value = value; this.description = description; this.familyName = familyName; this.jcaName = jcaName; this.jdkStandard = jdkStandard; + this.digestLength = digestLength; + this.minKeyLength = minKeyLength; + this.pkcs12Name = pkcs12Name; } /** @@ -130,67 +171,66 @@ * following table: * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * *
Crypto Family
SignatureAlgorithmFamily Name
HS256HMAC
HS384HMAC
HS512HMAC
RS256RSA
RS384RSA
RS512RSA
PS256RSA
PS384RSA
PS512RSA
ES256Elliptic Curve
ES384Elliptic Curve
ES512Elliptic Curve
Crypto Family
SignatureAlgorithmFamily Name
HS256HMAC
HS384HMAC
HS512HMAC
RS256RSA
RS384RSA
RS512RSA
PS256RSA
PS384RSA
PS512RSA
ES256ECDSA
ES384ECDSA
ES512ECDSA
* * @return Returns the cryptographic family name of the signature algorithm. - * * @since 0.5 */ public String getFamilyName() { @@ -225,7 +265,7 @@ * @return {@code true} if the enum instance represents an HMAC signature algorithm, {@code false} otherwise. */ public boolean isHmac() { - return name().startsWith("HS"); + return familyName.equals("HMAC"); } /** @@ -236,21 +276,364 @@ * {@code false} otherwise. */ public boolean isRsa() { - return getDescription().startsWith("RSASSA"); + return familyName.equals("RSA"); } /** - * Returns {@code true} if the enum instance represents an Elliptic Curve signature algorithm, {@code false} + * Returns {@code true} if the enum instance represents an Elliptic Curve ECDSA signature algorithm, {@code false} * otherwise. * - * @return {@code true} if the enum instance represents an Elliptic Curve signature algorithm, {@code false} + * @return {@code true} if the enum instance represents an Elliptic Curve ECDSA signature algorithm, {@code false} * otherwise. */ public boolean isEllipticCurve() { - return name().startsWith("ES"); + return familyName.equals("ECDSA"); } /** + * Returns the minimum key length in bits (not bytes) that may be used with this algorithm according to the + * JWT JWA Specification (RFC 7518). + * + * @return the minimum key length in bits (not bytes) that may be used with this algorithm according to the + * JWT JWA Specification (RFC 7518). + * @since 0.10.0 + */ + public int getMinKeyLength() { + return this.minKeyLength; + } + + /** + * Returns quietly if the specified key is allowed to create signatures using this algorithm + * according to the JWT JWA Specification (RFC 7518) or throws an + * {@link InvalidKeyException} if the key is not allowed or not secure enough for this algorithm. + * + * @param key the key to check for validity. + * @throws InvalidKeyException if the key is not allowed or not secure enough for this algorithm. + * @since 0.10.0 + */ + public void assertValidSigningKey(Key key) throws InvalidKeyException { + assertValid(key, true); + } + + /** + * Returns quietly if the specified key is allowed to verify signatures using this algorithm + * according to the JWT JWA Specification (RFC 7518) or throws an + * {@link InvalidKeyException} if the key is not allowed or not secure enough for this algorithm. + * + * @param key the key to check for validity. + * @throws InvalidKeyException if the key is not allowed or not secure enough for this algorithm. + * @since 0.10.0 + */ + public void assertValidVerificationKey(Key key) throws InvalidKeyException { + assertValid(key, false); + } + + /** + * @since 0.10.0 to support assertValid(Key, boolean) + */ + private static String keyType(boolean signing) { + return signing ? "signing" : "verification"; + } + + /** + * @since 0.10.0 + */ + private void assertValid(Key key, boolean signing) throws InvalidKeyException { + + if (this == NONE) { + + String msg = "The 'NONE' signature algorithm does not support cryptographic keys."; + throw new InvalidKeyException(msg); + + } else if (isHmac()) { + + if (!(key instanceof SecretKey)) { + String msg = this.familyName + " " + keyType(signing) + " keys must be SecretKey instances."; + throw new InvalidKeyException(msg); + } + SecretKey secretKey = (SecretKey) key; + + byte[] encoded = secretKey.getEncoded(); + if (encoded == null) { + throw new InvalidKeyException("The " + keyType(signing) + " key's encoded bytes cannot be null."); + } + + String alg = secretKey.getAlgorithm(); + if (alg == null) { + throw new InvalidKeyException("The " + keyType(signing) + " key's algorithm cannot be null."); + } + + // These next checks use equalsIgnoreCase per https://github.com/jwtk/jjwt/issues/381#issuecomment-412912272 + if (!HS256.jcaName.equalsIgnoreCase(alg) && + !HS384.jcaName.equalsIgnoreCase(alg) && + !HS512.jcaName.equalsIgnoreCase(alg) && + !HS256.pkcs12Name.equals(alg) && + !HS384.pkcs12Name.equals(alg) && + !HS512.pkcs12Name.equals(alg)) { + throw new InvalidKeyException("The " + keyType(signing) + " key's algorithm '" + alg + + "' does not equal a valid HmacSHA* algorithm name and cannot be used with " + name() + "."); + } + + int size = encoded.length * 8; //size in bits + if (size < this.minKeyLength) { + String msg = "The " + keyType(signing) + " key's size is " + size + " bits which " + + "is not secure enough for the " + name() + " algorithm. The JWT " + + "JWA Specification (RFC 7518, Section 3.2) states that keys used with " + name() + " MUST have a " + + "size >= " + minKeyLength + " bits (the key size must be greater than or equal to the hash " + + "output size). Consider using the " + Keys.class.getName() + " class's " + + "'secretKeyFor(SignatureAlgorithm." + name() + ")' method to create a key guaranteed to be " + + "secure enough for " + name() + ". See " + + "https://tools.ietf.org/html/rfc7518#section-3.2 for more information."; + throw new WeakKeyException(msg); + } + + } else { //EC or RSA + + if (signing) { + if (!(key instanceof PrivateKey)) { + String msg = familyName + " signing keys must be PrivateKey instances."; + throw new InvalidKeyException(msg); + } + } + + if (isEllipticCurve()) { + + if (!(key instanceof ECKey)) { + String msg = familyName + " " + keyType(signing) + " keys must be ECKey instances."; + throw new InvalidKeyException(msg); + } + + ECKey ecKey = (ECKey) key; + int size = ecKey.getParams().getOrder().bitLength(); + if (size < this.minKeyLength) { + String msg = "The " + keyType(signing) + " key's size (ECParameterSpec order) is " + size + + " bits which is not secure enough for the " + name() + " algorithm. The JWT " + + "JWA Specification (RFC 7518, Section 3.4) states that keys used with " + + name() + " MUST have a size >= " + this.minKeyLength + + " bits. Consider using the " + Keys.class.getName() + " class's " + + "'keyPairFor(SignatureAlgorithm." + name() + ")' method to create a key pair guaranteed " + + "to be secure enough for " + name() + ". See " + + "https://tools.ietf.org/html/rfc7518#section-3.4 for more information."; + throw new WeakKeyException(msg); + } + + } else { //RSA + + if (!(key instanceof RSAKey)) { + String msg = familyName + " " + keyType(signing) + " keys must be RSAKey instances."; + throw new InvalidKeyException(msg); + } + + RSAKey rsaKey = (RSAKey) key; + int size = rsaKey.getModulus().bitLength(); + if (size < this.minKeyLength) { + + String section = name().startsWith("P") ? "3.5" : "3.3"; + + String msg = "The " + keyType(signing) + " key's size is " + size + " bits which is not secure " + + "enough for the " + name() + " algorithm. The JWT JWA Specification (RFC 7518, Section " + + section + ") states that keys used with " + name() + " MUST have a size >= " + + this.minKeyLength + " bits. Consider using the " + Keys.class.getName() + " class's " + + "'keyPairFor(SignatureAlgorithm." + name() + ")' method to create a key pair guaranteed " + + "to be secure enough for " + name() + ". See " + + "https://tools.ietf.org/html/rfc7518#section-" + section + " for more information."; + throw new WeakKeyException(msg); + } + } + } + } + + /** + * Returns the recommended signature algorithm to be used with the specified key according to the following + * heuristics: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Key Signature Algorithm
If the Key is a:And:With a key size of:The returned SignatureAlgorithm will be:
{@link SecretKey}{@link Key#getAlgorithm() getAlgorithm()}.equals("HmacSHA256")1256 <= size <= 383 2{@link SignatureAlgorithm#HS256 HS256}
{@link SecretKey}{@link Key#getAlgorithm() getAlgorithm()}.equals("HmacSHA384")1384 <= size <= 511{@link SignatureAlgorithm#HS384 HS384}
{@link SecretKey}{@link Key#getAlgorithm() getAlgorithm()}.equals("HmacSHA512")1512 <= size{@link SignatureAlgorithm#HS512 HS512}
{@link ECKey}instanceof {@link PrivateKey}256 <= size <= 383 3{@link SignatureAlgorithm#ES256 ES256}
{@link ECKey}instanceof {@link PrivateKey}384 <= size <= 511{@link SignatureAlgorithm#ES384 ES384}
{@link ECKey}instanceof {@link PrivateKey}4096 <= size{@link SignatureAlgorithm#ES512 ES512}
{@link RSAKey}instanceof {@link PrivateKey}2048 <= size <= 3071 4,5{@link SignatureAlgorithm#RS256 RS256}
{@link RSAKey}instanceof {@link PrivateKey}3072 <= size <= 4095 5{@link SignatureAlgorithm#RS384 RS384}
{@link RSAKey}instanceof {@link PrivateKey}4096 <= size 5{@link SignatureAlgorithm#RS512 RS512}
+ *

Notes:

+ *
    + *
  1. {@code SecretKey} instances must have an {@link Key#getAlgorithm() algorithm} name equal + * to {@code HmacSHA256}, {@code HmacSHA384} or {@code HmacSHA512}. If not, the key bytes might not be + * suitable for HMAC signatures will be rejected with a {@link InvalidKeyException}.
  2. + *
  3. The JWT JWA Specification (RFC 7518, + * Section 3.2) mandates that HMAC-SHA-* signing keys MUST be 256 bits or greater. + * {@code SecretKey}s with key lengths less than 256 bits will be rejected with an + * {@link WeakKeyException}.
  4. + *
  5. The JWT JWA Specification (RFC 7518, + * Section 3.4) mandates that ECDSA signing key lengths MUST be 256 bits or greater. + * {@code ECKey}s with key lengths less than 256 bits will be rejected with a + * {@link WeakKeyException}.
  6. + *
  7. The JWT JWA Specification (RFC 7518, + * Section 3.3) mandates that RSA signing key lengths MUST be 2048 bits or greater. + * {@code RSAKey}s with key lengths less than 2048 bits will be rejected with a + * {@link WeakKeyException}.
  8. + *
  9. Technically any RSA key of length >= 2048 bits may be used with the {@link #RS256}, {@link #RS384}, and + * {@link #RS512} algorithms, so we assume an RSA signature algorithm based on the key length to + * parallel similar decisions in the JWT specification for HMAC and ECDSA signature algorithms. + * This is not required - just a convenience.
  10. + *
+ *

This implementation does not return the {@link #PS256}, {@link #PS256}, {@link #PS256} RSA variant for any + * specified {@link RSAKey} because: + *

    + *
  • The JWT JWA Specification (RFC 7518, + * Section 3.1) indicates that {@link #RS256}, {@link #RS384}, and {@link #RS512} are + * recommended algorithms while the {@code PS}* variants are simply marked as optional.
  • + *
  • The {@link #RS256}, {@link #RS384}, and {@link #RS512} algorithms are available in the JDK by default + * while the {@code PS}* variants require an additional JCA Provider (like BouncyCastle).
  • + *
+ *

+ * + *

Finally, this method will throw an {@link 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.

+ * + * @param key the key to inspect + * @return the recommended signature algorithm to be used with the specified key + * @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. + * @since 0.10.0 + */ + public static SignatureAlgorithm forSigningKey(Key key) throws InvalidKeyException { + + if (key == null) { + throw new InvalidKeyException("Key argument cannot be null."); + } + + if (!(key instanceof SecretKey || + (key instanceof PrivateKey && (key instanceof ECKey || key instanceof RSAKey)))) { + String msg = "JWT standard signing algorithms require either 1) a SecretKey for HMAC-SHA algorithms or " + + "2) a private RSAKey for RSA algorithms or 3) a private ECKey for Elliptic Curve algorithms. " + + "The specified key is of type " + key.getClass().getName(); + throw new InvalidKeyException(msg); + } + + if (key instanceof SecretKey) { + + SecretKey secretKey = (SecretKey)key; + int bitLength = io.jsonwebtoken.lang.Arrays.length(secretKey.getEncoded()) * Byte.SIZE; + + for(SignatureAlgorithm alg : PREFERRED_HMAC_ALGS) { + // ensure compatibility check is based on key length. See https://github.com/jwtk/jjwt/issues/381 + if (bitLength >= alg.minKeyLength) { + return alg; + } + } + + String msg = "The specified SecretKey is not strong enough to be used with JWT HMAC signature " + + "algorithms. The JWT specification requires HMAC keys to be >= 256 bits long. The specified " + + "key is " + bitLength + " bits. See https://tools.ietf.org/html/rfc7518#section-3.2 for more " + + "information."; + throw new WeakKeyException(msg); + } + + if (key instanceof RSAKey) { + + RSAKey rsaKey = (RSAKey) key; + int bitLength = rsaKey.getModulus().bitLength(); + + if (bitLength >= 4096) { + RS512.assertValidSigningKey(key); + return RS512; + } else if (bitLength >= 3072) { + RS384.assertValidSigningKey(key); + return RS384; + } else if (bitLength >= RS256.minKeyLength) { + RS256.assertValidSigningKey(key); + return RS256; + } + + String msg = "The specified RSA signing key is not strong enough to be used with JWT RSA signature " + + "algorithms. The JWT specification requires RSA keys to be >= 2048 bits long. The specified RSA " + + "key is " + bitLength + " bits. See https://tools.ietf.org/html/rfc7518#section-3.3 for more " + + "information."; + throw new WeakKeyException(msg); + } + + // if we've made it this far in the method, the key is an ECKey due to the instanceof assertions at the + // top of the method + + ECKey ecKey = (ECKey) key; + int bitLength = ecKey.getParams().getOrder().bitLength(); + + for (SignatureAlgorithm alg : PREFERRED_EC_ALGS) { + if (bitLength >= alg.minKeyLength) { + alg.assertValidSigningKey(key); + return alg; + } + } + + String msg = "The specified Elliptic Curve signing key is not strong enough to be used with JWT ECDSA " + + "signature algorithms. The JWT specification requires ECDSA keys to be >= 256 bits long. " + + "The specified ECDSA key is " + bitLength + " bits. See " + + "https://tools.ietf.org/html/rfc7518#section-3.4 for more information."; + throw new WeakKeyException(msg); + } + + /** * Looks up and returns the corresponding {@code SignatureAlgorithm} enum instance based on a * case-insensitive name comparison. * Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/SignatureException.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/SignatureException.java (.../SignatureException.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/SignatureException.java (.../SignatureException.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,12 +15,16 @@ */ package io.jsonwebtoken; +import io.jsonwebtoken.security.SecurityException; + /** * Exception indicating that either calculating a signature or verifying an existing signature of a JWT failed. * * @since 0.1 + * @deprecated in favor of {@link io.jsonwebtoken.security.SecurityException}; this class will be removed before 1.0 */ -public class SignatureException extends JwtException { +@Deprecated +public class SignatureException extends SecurityException { public SignatureException(String message) { super(message); Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/AbstractTextCodec.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/AbstractTextCodec.java (.../AbstractTextCodec.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/AbstractTextCodec.java (.../AbstractTextCodec.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,13 +15,19 @@ */ package io.jsonwebtoken.impl; +import io.jsonwebtoken.io.Decoder; +import io.jsonwebtoken.io.Encoder; import io.jsonwebtoken.lang.Assert; import java.nio.charset.Charset; +/** + * @deprecated since 0.10.0 - will be removed before 1.0.0. Use {@link Encoder} orr {@link Decoder} instead. + */ +@Deprecated public abstract class AbstractTextCodec implements TextCodec { - protected static final Charset UTF8 = Charset.forName("UTF-8"); + protected static final Charset UTF8 = Charset.forName("UTF-8"); protected static final Charset US_ASCII = Charset.forName("US-ASCII"); @Override Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/AndroidBase64Codec.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/AndroidBase64Codec.java (.../AndroidBase64Codec.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/AndroidBase64Codec.java (.../AndroidBase64Codec.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,16 +15,23 @@ */ package io.jsonwebtoken.impl; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.io.Encoders; + +/** + * @deprecated since 0.10.0 - will be removed before 1.0.0. Use {@code io.jsonwebtoken.io.Encoders#BASE64} + * or {@code io.jsonwebtoken.io.Decoders#BASE64} instead. + */ +@Deprecated public class AndroidBase64Codec extends AbstractTextCodec { @Override public String encode(byte[] data) { - int flags = android.util.Base64.NO_PADDING | android.util.Base64.NO_WRAP; - return android.util.Base64.encodeToString(data, flags); + return Encoders.BASE64.encode(data); } @Override public byte[] decode(String encoded) { - return android.util.Base64.decode(encoded, android.util.Base64.DEFAULT); + return Decoders.BASE64.decode(encoded); } } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/Base64Codec.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/Base64Codec.java (.../Base64Codec.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/Base64Codec.java (.../Base64Codec.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,14 +15,22 @@ */ package io.jsonwebtoken.impl; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.io.Encoders; + +/** + * @deprecated since 0.10.0 - will be removed before 1.0.0. Use {@code io.jsonwebtoken.io.Encoders#BASE64} + * or {@code io.jsonwebtoken.io.Decoders#BASE64} + */ +@Deprecated public class Base64Codec extends AbstractTextCodec { public String encode(byte[] data) { - return javax.xml.bind.DatatypeConverter.printBase64Binary(data); + return Encoders.BASE64.encode(data); } @Override public byte[] decode(String encoded) { - return javax.xml.bind.DatatypeConverter.parseBase64Binary(encoded); + return Decoders.BASE64.decode(encoded); } } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/Base64UrlCodec.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/Base64UrlCodec.java (.../Base64UrlCodec.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/Base64UrlCodec.java (.../Base64UrlCodec.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,90 +15,23 @@ */ package io.jsonwebtoken.impl; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.io.Encoders; + +/** + * @deprecated since 0.10.0 - will be removed before 1.0.0. Use {@link Encoders#BASE64URL Encoder.BASE64URL} + * or {@link Decoders#BASE64URL Decoder.BASE64URL} instead. + */ +@Deprecated public class Base64UrlCodec extends AbstractTextCodec { @Override public String encode(byte[] data) { - String base64Text = TextCodec.BASE64.encode(data); - byte[] bytes = base64Text.getBytes(US_ASCII); - - //base64url encoding doesn't use padding chars: - bytes = removePadding(bytes); - - //replace URL-unfriendly Base64 chars to url-friendly ones: - for (int i = 0; i < bytes.length; i++) { - if (bytes[i] == '+') { - bytes[i] = '-'; - } else if (bytes[i] == '/') { - bytes[i] = '_'; - } - } - - return new String(bytes, US_ASCII); + return Encoders.BASE64URL.encode(data); } - protected byte[] removePadding(byte[] bytes) { - - byte[] result = bytes; - - int paddingCount = 0; - for (int i = bytes.length - 1; i > 0; i--) { - if (bytes[i] == '=') { - paddingCount++; - } else { - break; - } - } - if (paddingCount > 0) { - result = new byte[bytes.length - paddingCount]; - System.arraycopy(bytes, 0, result, 0, bytes.length - paddingCount); - } - - return result; - } - @Override public byte[] decode(String encoded) { - char[] chars = encoded.toCharArray(); //always ASCII - one char == 1 byte - - //Base64 requires padding to be in place before decoding, so add it if necessary: - chars = ensurePadding(chars); - - //Replace url-friendly chars back to normal Base64 chars: - for (int i = 0; i < chars.length; i++) { - if (chars[i] == '-') { - chars[i] = '+'; - } else if (chars[i] == '_') { - chars[i] = '/'; - } - } - - String base64Text = new String(chars); - - return TextCodec.BASE64.decode(base64Text); + return Decoders.BASE64URL.decode(encoded); } - - protected char[] ensurePadding(char[] chars) { - - char[] result = chars; //assume argument in case no padding is necessary - - int paddingCount = 0; - - //fix for https://github.com/jwtk/jjwt/issues/31 - int remainder = chars.length % 4; - if (remainder == 2 || remainder == 3) { - paddingCount = 4 - remainder; - } - - if (paddingCount > 0) { - result = new char[chars.length + paddingCount]; - System.arraycopy(chars, 0, result, 0, chars.length); - for (int i = 0; i < paddingCount; i++) { - result[chars.length + i] = '='; - } - } - - return result; - } - } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultClaims.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultClaims.java (.../DefaultClaims.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultClaims.java (.../DefaultClaims.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -17,17 +17,24 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.RequiredTypeException; - import java.util.Date; import java.util.Map; public class DefaultClaims extends JwtMap implements Claims { + private static final String CONVERSION_ERROR_MSG = "Cannot convert existing claim value of type '%s' to desired type " + + "'%s'. JJWT only converts simple String, Date, Long, Integer, Short and Byte types automatically. " + + "Anything more complex is expected to be already converted to your desired type by the JSON Deserializer " + + "implementation. You may specify a custom Deserializer for a JwtParser with the desired conversion " + + "configuration via the JwtParserBuilder.deserializeJsonWith() method. " + + "See https://github.com/jwtk/jjwt#custom-json-processor for more information. If using Jackson, you can " + + "specify custom claim POJO types as described in https://github.com/jwtk/jjwt#json-jackson-custom-types"; + public DefaultClaims() { super(); } - public DefaultClaims(Map map) { + public DefaultClaims(Map map) { super(map); } @@ -71,7 +78,7 @@ @Override public Claims setExpiration(Date exp) { - setDate(Claims.EXPIRATION, exp); + setDateAsSeconds(Claims.EXPIRATION, exp); return this; } @@ -82,7 +89,7 @@ @Override public Claims setNotBefore(Date nbf) { - setDate(Claims.NOT_BEFORE, nbf); + setDateAsSeconds(Claims.NOT_BEFORE, nbf); return this; } @@ -93,7 +100,7 @@ @Override public Claims setIssuedAt(Date iat) { - setDate(Claims.ISSUED_AT, iat); + setDateAsSeconds(Claims.ISSUED_AT, iat); return this; } @@ -108,25 +115,44 @@ return this; } + /** + * @since 0.10.0 + */ + private static boolean isSpecDate(String claimName) { + return Claims.EXPIRATION.equals(claimName) || + Claims.ISSUED_AT.equals(claimName) || + Claims.NOT_BEFORE.equals(claimName); + } + @Override + public Object put(String s, Object o) { + if (o instanceof Date && isSpecDate(s)) { //since 0.10.0 + Date date = (Date)o; + return setDateAsSeconds(s, date); + } + return super.put(s, o); + } + + @Override public T get(String claimName, Class requiredType) { + Object value = get(claimName); - if (value == null) { return null; } + if (value == null) { + return null; + } - if (Claims.EXPIRATION.equals(claimName) || - Claims.ISSUED_AT.equals(claimName) || - Claims.NOT_BEFORE.equals(claimName) - ) { - value = getDate(claimName); + if (Date.class.equals(requiredType)) { + if (isSpecDate(claimName)) { + value = toSpecDate(value, claimName); + } else { + value = toDate(value, claimName); + } } return castClaimValue(value, requiredType); } private T castClaimValue(Object value, Class requiredType) { - if (requiredType == Date.class && value instanceof Long) { - value = new Date((Long)value); - } if (value instanceof Integer) { int intValue = (Integer) value; @@ -140,7 +166,7 @@ } if (!requiredType.isInstance(value)) { - throw new RequiredTypeException("Expected value to be of type: " + requiredType + ", but was " + value.getClass()); + throw new RequiredTypeException(String.format(CONVERSION_ERROR_MSG, value.getClass(), requiredType)); } return requiredType.cast(value); Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultClock.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultClock.java (.../DefaultClock.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultClock.java (.../DefaultClock.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jsonwebtoken.impl; import io.jsonwebtoken.Clock; Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultJwtBuilder.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultJwtBuilder.java (.../DefaultJwtBuilder.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultJwtBuilder.java (.../DefaultJwtBuilder.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,36 +15,62 @@ */ package io.jsonwebtoken.impl; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.jsonwebtoken.*; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.CompressionCodec; +import io.jsonwebtoken.Header; +import io.jsonwebtoken.JwsHeader; +import io.jsonwebtoken.JwtBuilder; +import io.jsonwebtoken.JwtParser; +import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.impl.crypto.DefaultJwtSigner; import io.jsonwebtoken.impl.crypto.JwtSigner; +import io.jsonwebtoken.impl.lang.LegacyServices; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.io.Encoder; +import io.jsonwebtoken.io.Encoders; +import io.jsonwebtoken.io.SerializationException; +import io.jsonwebtoken.io.Serializer; import io.jsonwebtoken.lang.Assert; import io.jsonwebtoken.lang.Collections; -import io.jsonwebtoken.lang.Objects; import io.jsonwebtoken.lang.Strings; +import io.jsonwebtoken.security.InvalidKeyException; +import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.util.Date; import java.util.Map; public class DefaultJwtBuilder implements JwtBuilder { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private Header header; private Claims claims; private String payload; private SignatureAlgorithm algorithm; - private Key key; - private byte[] keyBytes; + private Key key; + private Serializer> serializer; + + private Encoder base64UrlEncoder = Encoders.BASE64URL; + private CompressionCodec compressionCodec; @Override + public JwtBuilder serializeToJsonWith(Serializer> serializer) { + Assert.notNull(serializer, "Serializer cannot be null."); + this.serializer = serializer; + return this; + } + + @Override + public JwtBuilder base64UrlEncodeWith(Encoder base64UrlEncoder) { + Assert.notNull(base64UrlEncoder, "base64UrlEncoder cannot be null."); + this.base64UrlEncoder = base64UrlEncoder; + return this; + } + + @Override public JwtBuilder setHeader(Header header) { this.header = header; return this; @@ -83,30 +109,42 @@ } @Override - public JwtBuilder signWith(SignatureAlgorithm alg, byte[] secretKey) { + public JwtBuilder signWith(Key key) throws InvalidKeyException { + Assert.notNull(key, "Key argument cannot be null."); + SignatureAlgorithm alg = SignatureAlgorithm.forSigningKey(key); + return signWith(key, alg); + } + + @Override + public JwtBuilder signWith(Key key, SignatureAlgorithm alg) throws InvalidKeyException { + Assert.notNull(key, "Key argument cannot be null."); Assert.notNull(alg, "SignatureAlgorithm cannot be null."); - Assert.notEmpty(secretKey, "secret key byte array cannot be null or empty."); - Assert.isTrue(alg.isHmac(), "Key bytes may only be specified for HMAC signatures. If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead."); + alg.assertValidSigningKey(key); //since 0.10.0 for https://github.com/jwtk/jjwt/issues/334 this.algorithm = alg; - this.keyBytes = secretKey; + this.key = key; return this; } @Override - public JwtBuilder signWith(SignatureAlgorithm alg, String base64EncodedSecretKey) { + public JwtBuilder signWith(SignatureAlgorithm alg, byte[] secretKeyBytes) throws InvalidKeyException { + Assert.notNull(alg, "SignatureAlgorithm cannot be null."); + Assert.notEmpty(secretKeyBytes, "secret key byte array cannot be null or empty."); + Assert.isTrue(alg.isHmac(), "Key bytes may only be specified for HMAC signatures. If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead."); + SecretKey key = new SecretKeySpec(secretKeyBytes, alg.getJcaName()); + return signWith(key, alg); + } + + @Override + public JwtBuilder signWith(SignatureAlgorithm alg, String base64EncodedSecretKey) throws InvalidKeyException { Assert.hasText(base64EncodedSecretKey, "base64-encoded secret key cannot be null or empty."); Assert.isTrue(alg.isHmac(), "Base64-encoded key bytes may only be specified for HMAC signatures. If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead."); - byte[] bytes = TextCodec.BASE64.decode(base64EncodedSecretKey); + byte[] bytes = Decoders.BASE64.decode(base64EncodedSecretKey); return signWith(alg, bytes); } @Override public JwtBuilder signWith(SignatureAlgorithm alg, Key key) { - Assert.notNull(alg, "SignatureAlgorithm cannot be null."); - Assert.notNull(key, "Key argument cannot be null."); - this.algorithm = alg; - this.key = key; - return this; + return signWith(key, alg); } @Override @@ -136,8 +174,8 @@ } @Override - public JwtBuilder setClaims(Map claims) { - this.claims = Jwts.claims(claims); + public JwtBuilder setClaims(Map claims) { + this.claims = new DefaultClaims(claims); return this; } @@ -254,30 +292,29 @@ @Override public String compact() { + + if (this.serializer == 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 + // use the previous commented out line instead + this.serializer = LegacyServices.loadFirst(Serializer.class); + } + if (payload == null && Collections.isEmpty(claims)) { - throw new IllegalStateException("Either 'payload' or 'claims' must be specified."); + payload = ""; } if (payload != null && !Collections.isEmpty(claims)) { throw new IllegalStateException("Both 'payload' and 'claims' cannot both be specified. Choose either one."); } - if (key != null && keyBytes != null) { - throw new IllegalStateException("A key object and key bytes cannot both be specified. Choose either one."); - } - Header header = ensureHeader(); - Key key = this.key; - if (key == null && !Objects.isEmpty(keyBytes)) { - key = new SecretKeySpec(keyBytes, algorithm.getJcaName()); - } - JwsHeader jwsHeader; - if (header instanceof JwsHeader) { - jwsHeader = (JwsHeader)header; + jwsHeader = (JwsHeader) header; } else { + //noinspection unchecked jwsHeader = new DefaultJwsHeader(header); } @@ -294,25 +331,19 @@ String base64UrlEncodedHeader = base64UrlEncode(jwsHeader, "Unable to serialize header to json."); - String base64UrlEncodedBody; + byte[] bytes; + try { + bytes = this.payload != null ? payload.getBytes(Strings.UTF_8) : toJson(claims); + } catch (SerializationException e) { + throw new IllegalArgumentException("Unable to serialize claims object to json: " + e.getMessage(), e); + } if (compressionCodec != null) { - - byte[] bytes; - try { - bytes = this.payload != null ? payload.getBytes(Strings.UTF_8) : toJson(claims); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException("Unable to serialize claims object to json."); - } - - base64UrlEncodedBody = TextCodec.BASE64URL.encode(compressionCodec.compress(bytes)); - - } else { - base64UrlEncodedBody = this.payload != null ? - TextCodec.BASE64URL.encode(this.payload) : - base64UrlEncode(claims, "Unable to serialize claims object to json."); + bytes = compressionCodec.compress(bytes); } + String base64UrlEncodedBody = base64UrlEncoder.encode(bytes); + String jwt = base64UrlEncodedHeader + JwtParser.SEPARATOR_CHAR + base64UrlEncodedBody; if (key != null) { //jwt must be signed: @@ -335,22 +366,28 @@ * @since 0.5 mostly to allow testing overrides */ protected JwtSigner createSigner(SignatureAlgorithm alg, Key key) { - return new DefaultJwtSigner(alg, key); + return new DefaultJwtSigner(alg, key, base64UrlEncoder); } + @Deprecated // remove before 1.0 - call the serializer and base64UrlEncoder directly protected String base64UrlEncode(Object o, String errMsg) { + Assert.isInstanceOf(Map.class, o, "object argument must be a map."); + Map m = (Map)o; byte[] bytes; try { - bytes = toJson(o); - } catch (JsonProcessingException e) { + bytes = toJson(m); + } catch (SerializationException e) { throw new IllegalStateException(errMsg, e); } - return TextCodec.BASE64URL.encode(bytes); + return base64UrlEncoder.encode(bytes); } - - protected byte[] toJson(Object object) throws JsonProcessingException { - return OBJECT_MAPPER.writeValueAsBytes(object); + @SuppressWarnings("unchecked") + @Deprecated //remove before 1.0 - call the serializer directly + protected byte[] toJson(Object object) throws SerializationException { + Assert.isInstanceOf(Map.class, object, "object argument must be a map."); + Map m = (Map)object; + return serializer.serialize(m); } } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultJwtParser.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultJwtParser.java (.../DefaultJwtParser.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultJwtParser.java (.../DefaultJwtParser.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,7 +15,6 @@ */ package io.jsonwebtoken.impl; -import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.ClaimJwtException; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Clock; @@ -35,32 +34,35 @@ import io.jsonwebtoken.MissingClaimException; import io.jsonwebtoken.PrematureJwtException; import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.SigningKeyResolver; import io.jsonwebtoken.UnsupportedJwtException; import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver; import io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator; import io.jsonwebtoken.impl.crypto.JwtSignatureValidator; +import io.jsonwebtoken.impl.lang.LegacyServices; +import io.jsonwebtoken.io.Decoder; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.io.DeserializationException; +import io.jsonwebtoken.io.Deserializer; import io.jsonwebtoken.lang.Assert; +import io.jsonwebtoken.lang.DateFormats; import io.jsonwebtoken.lang.Objects; import io.jsonwebtoken.lang.Strings; +import io.jsonwebtoken.security.InvalidKeyException; +import io.jsonwebtoken.security.SignatureException; +import io.jsonwebtoken.security.WeakKeyException; import javax.crypto.spec.SecretKeySpec; -import java.io.IOException; import java.security.Key; -import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; @SuppressWarnings("unchecked") public class DefaultJwtParser implements JwtParser { - //don't need millis since JWT date fields are only second granularity: - private static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; private static final int MILLISECONDS_PER_SECOND = 1000; - private ObjectMapper objectMapper = new ObjectMapper(); - + // TODO: make the folling fields final for v1.0 private byte[] keyBytes; private Key key; @@ -69,13 +71,58 @@ private CompressionCodecResolver compressionCodecResolver = new DefaultCompressionCodecResolver(); - Claims expectedClaims = new DefaultClaims(); + private Decoder base64UrlDecoder = Decoders.BASE64URL; + private Deserializer> deserializer; + + private Claims expectedClaims = new DefaultClaims(); + private Clock clock = DefaultClock.INSTANCE; private long allowedClockSkewMillis = 0; + /** + * TODO: remove this constructor before 1.0 + * @deprecated for backward compatibility only, see other constructors. + */ + @Deprecated + public DefaultJwtParser() { } + + DefaultJwtParser(SigningKeyResolver signingKeyResolver, + Key key, + byte[] keyBytes, + Clock clock, + long allowedClockSkewMillis, + Claims expectedClaims, + Decoder base64UrlDecoder, + Deserializer> deserializer, + CompressionCodecResolver compressionCodecResolver) { + this.signingKeyResolver = signingKeyResolver; + this.key = key; + this.keyBytes = keyBytes; + this.clock = clock; + this.allowedClockSkewMillis = allowedClockSkewMillis; + this.expectedClaims = expectedClaims; + this.base64UrlDecoder = base64UrlDecoder; + this.deserializer = deserializer; + this.compressionCodecResolver = compressionCodecResolver; + } + @Override + public JwtParser deserializeJsonWith(Deserializer> deserializer) { + Assert.notNull(deserializer, "deserializer cannot be null."); + this.deserializer = deserializer; + return this; + } + + @Override + public JwtParser base64UrlDecodeWith(Decoder base64UrlDecoder) { + Assert.notNull(base64UrlDecoder, "base64UrlDecoder cannot be null."); + this.base64UrlDecoder = base64UrlDecoder; + return this; + } + + @Override public JwtParser requireIssuedAt(Date issuedAt) { expectedClaims.setIssuedAt(issuedAt); return this; @@ -133,7 +180,8 @@ } @Override - public JwtParser setAllowedClockSkewSeconds(long seconds) { + public JwtParser setAllowedClockSkewSeconds(long seconds) throws IllegalArgumentException { + Assert.isTrue(seconds <= DefaultJwtParserBuilder.MAX_CLOCK_SKEW_MILLIS, DefaultJwtParserBuilder.MAX_CLOCK_SKEW_ILLEGAL_MSG); this.allowedClockSkewMillis = Math.max(0, seconds * MILLISECONDS_PER_SECOND); return this; } @@ -146,9 +194,9 @@ } @Override - public JwtParser setSigningKey(String base64EncodedKeyBytes) { - Assert.hasText(base64EncodedKeyBytes, "signing key cannot be null or empty."); - this.keyBytes = TextCodec.BASE64.decode(base64EncodedKeyBytes); + public JwtParser setSigningKey(String base64EncodedSecretKey) { + Assert.hasText(base64EncodedSecretKey, "signing key cannot be null or empty."); + this.keyBytes = Decoders.BASE64.decode(base64EncodedSecretKey); return this; } @@ -200,8 +248,21 @@ @Override public Jwt parse(String jwt) 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 + this.deserializer = LegacyServices.loadFirst(Deserializer.class); + } + Assert.hasText(jwt, "JWT String argument cannot be null or empty."); + if ("..".equals(jwt)) { + String msg = "JWT string '..' is missing a header."; + throw new MalformedJwtException(msg); + } + String base64UrlEncodedHeader = null; String base64UrlEncodedPayload = null; String base64UrlEncodedDigest = null; @@ -215,7 +276,7 @@ if (c == SEPARATOR_CHAR) { CharSequence tokenSeq = Strings.clean(sb); - String token = tokenSeq!=null?tokenSeq.toString():null; + String token = tokenSeq != null ? tokenSeq.toString() : null; if (delimiterCount == 0) { base64UrlEncodedHeader = token; @@ -238,18 +299,16 @@ base64UrlEncodedDigest = sb.toString(); } - if (base64UrlEncodedPayload == null) { - throw new MalformedJwtException("JWT string '" + jwt + "' is missing a body/payload."); - } // =============== Header ================= Header header = null; CompressionCodec compressionCodec = null; if (base64UrlEncodedHeader != null) { - String origValue = TextCodec.BASE64URL.decodeToString(base64UrlEncodedHeader); - Map m = readValue(origValue); + byte[] bytes = base64UrlDecoder.decode(base64UrlEncodedHeader); + String origValue = new String(bytes, Strings.UTF_8); + Map m = (Map) readValue(origValue); if (base64UrlEncodedDigest != null) { header = new DefaultJwsHeader(m); @@ -261,18 +320,19 @@ } // =============== Body ================= - String payload; - if (compressionCodec != null) { - byte[] decompressed = compressionCodec.decompress(TextCodec.BASE64URL.decode(base64UrlEncodedPayload)); - payload = new String(decompressed, Strings.UTF_8); - } else { - payload = TextCodec.BASE64URL.decodeToString(base64UrlEncodedPayload); + String payload = ""; // https://github.com/jwtk/jjwt/pull/540 + if (base64UrlEncodedPayload != null) { + byte[] bytes = base64UrlDecoder.decode(base64UrlEncodedPayload); + if (compressionCodec != null) { + bytes = compressionCodec.decompress(bytes); + } + payload = new String(bytes, Strings.UTF_8); } Claims claims = null; - if (payload.charAt(0) == '{' && payload.charAt(payload.length() - 1) == '}') { //likely to be json, parse it: - Map claimsMap = readValue(payload); + if (!payload.isEmpty() && payload.charAt(0) == '{' && payload.charAt(payload.length() - 1) == '}') { //likely to be json, parse it: + Map claimsMap = (Map) readValue(payload); claims = new DefaultClaims(claimsMap); } @@ -293,7 +353,7 @@ if (algorithm == null || algorithm == SignatureAlgorithm.NONE) { //it is plaintext, but it has a signature. This is invalid: String msg = "JWT string has a digest/signature, but the header does not reference a valid signature " + - "algorithm."; + "algorithm."; throw new MalformedJwtException(msg); } @@ -322,7 +382,7 @@ if (!Objects.isEmpty(keyBytes)) { Assert.isTrue(algorithm.isHmac(), - "Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance."); + "Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance."); key = new SecretKeySpec(keyBytes, algorithm.getJcaName()); } @@ -331,26 +391,32 @@ Assert.notNull(key, "A signing key must be specified if the specified JWT is digitally signed."); //re-create the jwt part without the signature. This is what needs to be signed for verification: - String jwtWithoutSignature = base64UrlEncodedHeader + SEPARATOR_CHAR + base64UrlEncodedPayload; + String jwtWithoutSignature = base64UrlEncodedHeader + SEPARATOR_CHAR; + if (base64UrlEncodedPayload != null) { + jwtWithoutSignature += base64UrlEncodedPayload; + } JwtSignatureValidator validator; try { + algorithm.assertValidVerificationKey(key); //since 0.10.0: https://github.com/jwtk/jjwt/issues/334 validator = createSignatureValidator(algorithm, key); - } catch (IllegalArgumentException e) { + } catch (WeakKeyException e) { + throw e; + } catch (InvalidKeyException | IllegalArgumentException e) { String algName = algorithm.getValue(); - String msg = "The parsed JWT indicates it was signed with the " + algName + " signature " + - "algorithm, but the specified signing key of type " + key.getClass().getName() + - " may not be used to validate " + algName + " signatures. Because the specified " + - "signing key reflects a specific and expected algorithm, and the JWT does not reflect " + - "this algorithm, it is likely that the JWT was not expected and therefore should not be " + - "trusted. Another possibility is that the parser was configured with the incorrect " + - "signing key, but this cannot be assumed for security reasons."; + String msg = "The parsed JWT indicates it was signed with the " + algName + " signature " + + "algorithm, but the specified signing key of type " + key.getClass().getName() + + " may not be used to validate " + algName + " signatures. Because the specified " + + "signing key reflects a specific and expected algorithm, and the JWT does not reflect " + + "this algorithm, it is likely that the JWT was not expected and therefore should not be " + + "trusted. Another possibility is that the parser was configured with the incorrect " + + "signing key, but this cannot be assumed for security reasons."; throw new UnsupportedJwtException(msg, e); } if (!validator.isValid(jwtWithoutSignature, base64UrlEncodedDigest)) { String msg = "JWT signature does not match locally computed signature. JWT validity cannot be " + - "asserted and should not be trusted."; + "asserted and should not be trusted."; throw new SignatureException(msg); } } @@ -360,8 +426,6 @@ //since 0.3: if (claims != null) { - SimpleDateFormat sdf; - final Date now = this.clock.now(); long nowTime = now.getTime(); @@ -373,9 +437,8 @@ long maxTime = nowTime - this.allowedClockSkewMillis; Date max = allowSkew ? new Date(maxTime) : now; if (max.after(exp)) { - sdf = new SimpleDateFormat(ISO_8601_FORMAT); - String expVal = sdf.format(exp); - String nowVal = sdf.format(now); + String expVal = DateFormats.formatIso8601(exp, false); + String nowVal = DateFormats.formatIso8601(now, false); long differenceMillis = maxTime - exp.getTime(); @@ -394,9 +457,8 @@ long minTime = nowTime + this.allowedClockSkewMillis; Date min = allowSkew ? new Date(minTime) : now; if (min.before(nbf)) { - sdf = new SimpleDateFormat(ISO_8601_FORMAT); - String nbfVal = sdf.format(nbf); - String nowVal = sdf.format(now); + String nbfVal = DateFormats.formatIso8601(nbf, false); + String nowVal = DateFormats.formatIso8601(now, false); long differenceMillis = nbf.getTime() - minTime; @@ -414,46 +476,53 @@ Object body = claims != null ? claims : payload; if (base64UrlEncodedDigest != null) { - return new DefaultJws((JwsHeader) header, body, base64UrlEncodedDigest); + return new DefaultJws<>((JwsHeader) header, body, base64UrlEncodedDigest); } else { - return new DefaultJwt(header, body); + return new DefaultJwt<>(header, body); } } + /** + * @since 0.10.0 + */ + private static Object normalize(Object o) { + if (o instanceof Integer) { + o = ((Integer) o).longValue(); + } + return o; + } + private void validateExpectedClaims(Header header, Claims claims) { + for (String expectedClaimName : expectedClaims.keySet()) { - Object expectedClaimValue = expectedClaims.get(expectedClaimName); - Object actualClaimValue = claims.get(expectedClaimName); + Object expectedClaimValue = normalize(expectedClaims.get(expectedClaimName)); + Object actualClaimValue = normalize(claims.get(expectedClaimName)); - if ( - Claims.ISSUED_AT.equals(expectedClaimName) || - Claims.EXPIRATION.equals(expectedClaimName) || - Claims.NOT_BEFORE.equals(expectedClaimName) - ) { - expectedClaimValue = expectedClaims.get(expectedClaimName, Date.class); - actualClaimValue = claims.get(expectedClaimName, Date.class); - } else if ( - expectedClaimValue instanceof Date && - actualClaimValue != null && - actualClaimValue instanceof Long - ) { - actualClaimValue = new Date((Long)actualClaimValue); + if (expectedClaimValue instanceof Date) { + try { + actualClaimValue = claims.get(expectedClaimName, Date.class); + } catch (Exception e) { + String msg = "JWT Claim '" + expectedClaimName + "' was expected to be a Date, but its value " + + "cannot be converted to a Date using current heuristics. Value: " + actualClaimValue; + throw new IncorrectClaimException(header, claims, msg); + } } InvalidClaimException invalidClaimException = null; if (actualClaimValue == null) { - String msg = String.format( - ClaimJwtException.MISSING_EXPECTED_CLAIM_MESSAGE_TEMPLATE, - expectedClaimName, expectedClaimValue - ); + + String msg = String.format(ClaimJwtException.MISSING_EXPECTED_CLAIM_MESSAGE_TEMPLATE, + expectedClaimName, expectedClaimValue); + invalidClaimException = new MissingClaimException(header, claims, msg); + } else if (!expectedClaimValue.equals(actualClaimValue)) { - String msg = String.format( - ClaimJwtException.INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE, - expectedClaimName, expectedClaimValue, actualClaimValue - ); + + String msg = String.format(ClaimJwtException.INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE, + expectedClaimName, expectedClaimValue, actualClaimValue); + invalidClaimException = new IncorrectClaimException(header, claims, msg); } @@ -469,7 +538,7 @@ * @since 0.5 mostly to allow testing overrides */ protected JwtSignatureValidator createSignatureValidator(SignatureAlgorithm alg, Key key) { - return new DefaultJwtSignatureValidator(alg, key); + return new DefaultJwtSignatureValidator(alg, key, base64UrlDecoder); } @Override @@ -547,10 +616,11 @@ } @SuppressWarnings("unchecked") - protected Map readValue(String val) { + protected Map readValue(String val) { try { - return objectMapper.readValue(val, Map.class); - } catch (IOException e) { + byte[] bytes = val.getBytes(Strings.UTF_8); + return deserializer.deserialize(bytes); + } catch (DeserializationException e) { throw new MalformedJwtException("Unable to read JSON value: " + val, e); } } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultJwtParserBuilder.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultJwtParserBuilder.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultJwtParserBuilder.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2019 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.impl; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Clock; +import io.jsonwebtoken.CompressionCodecResolver; +import io.jsonwebtoken.JwtParser; +import io.jsonwebtoken.JwtParserBuilder; +import io.jsonwebtoken.SigningKeyResolver; +import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver; +import io.jsonwebtoken.io.Decoder; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.io.Deserializer; +import io.jsonwebtoken.lang.Assert; +import io.jsonwebtoken.impl.lang.Services; + +import java.security.Key; +import java.util.Date; +import java.util.Map; + +/** + * @since 0.11.0 + */ +public class DefaultJwtParserBuilder implements JwtParserBuilder { + + private static final int MILLISECONDS_PER_SECOND = 1000; + + /** + * To prevent overflow per Issue 583. + * + * Package-protected on purpose to allow use in backwards-compatible {@link DefaultJwtParser} implementation. + * TODO: enable private modifier on these two variables when deleting DefaultJwtParser + */ + static final long MAX_CLOCK_SKEW_MILLIS = Long.MAX_VALUE / MILLISECONDS_PER_SECOND; + static final String MAX_CLOCK_SKEW_ILLEGAL_MSG = "Illegal allowedClockSkewMillis value: multiplying this " + + "value by 1000 to obtain the number of milliseconds would cause a numeric overflow."; + + private byte[] keyBytes; + + private Key key; + + private SigningKeyResolver signingKeyResolver; + + private CompressionCodecResolver compressionCodecResolver = new DefaultCompressionCodecResolver(); + + private Decoder base64UrlDecoder = Decoders.BASE64URL; + + private Deserializer> deserializer; + + private Claims expectedClaims = new DefaultClaims(); + + private Clock clock = DefaultClock.INSTANCE; + + private long allowedClockSkewMillis = 0; + + + @Override + public JwtParserBuilder deserializeJsonWith(Deserializer> deserializer) { + Assert.notNull(deserializer, "deserializer cannot be null."); + this.deserializer = deserializer; + return this; + } + + @Override + public JwtParserBuilder base64UrlDecodeWith(Decoder base64UrlDecoder) { + Assert.notNull(base64UrlDecoder, "base64UrlDecoder cannot be null."); + this.base64UrlDecoder = base64UrlDecoder; + return this; + } + + @Override + public JwtParserBuilder requireIssuedAt(Date issuedAt) { + expectedClaims.setIssuedAt(issuedAt); + return this; + } + + @Override + public JwtParserBuilder requireIssuer(String issuer) { + expectedClaims.setIssuer(issuer); + return this; + } + + @Override + public JwtParserBuilder requireAudience(String audience) { + expectedClaims.setAudience(audience); + return this; + } + + @Override + public JwtParserBuilder requireSubject(String subject) { + expectedClaims.setSubject(subject); + return this; + } + + @Override + public JwtParserBuilder requireId(String id) { + expectedClaims.setId(id); + return this; + } + + @Override + public JwtParserBuilder requireExpiration(Date expiration) { + expectedClaims.setExpiration(expiration); + return this; + } + + @Override + public JwtParserBuilder requireNotBefore(Date notBefore) { + expectedClaims.setNotBefore(notBefore); + return this; + } + + @Override + public JwtParserBuilder require(String claimName, Object value) { + Assert.hasText(claimName, "claim name cannot be null or empty."); + Assert.notNull(value, "The value cannot be null for claim name: " + claimName); + expectedClaims.put(claimName, value); + return this; + } + + @Override + public JwtParserBuilder setClock(Clock clock) { + Assert.notNull(clock, "Clock instance cannot be null."); + this.clock = clock; + return this; + } + + @Override + public JwtParserBuilder setAllowedClockSkewSeconds(long seconds) throws IllegalArgumentException { + Assert.isTrue(seconds <= MAX_CLOCK_SKEW_MILLIS, MAX_CLOCK_SKEW_ILLEGAL_MSG); + this.allowedClockSkewMillis = Math.max(0, seconds * MILLISECONDS_PER_SECOND); + return this; + } + + @Override + public JwtParserBuilder setSigningKey(byte[] key) { + Assert.notEmpty(key, "signing key cannot be null or empty."); + this.keyBytes = key; + return this; + } + + @Override + public JwtParserBuilder setSigningKey(String base64EncodedSecretKey) { + Assert.hasText(base64EncodedSecretKey, "signing key cannot be null or empty."); + this.keyBytes = Decoders.BASE64.decode(base64EncodedSecretKey); + return this; + } + + @Override + public JwtParserBuilder setSigningKey(Key key) { + Assert.notNull(key, "signing key cannot be null."); + this.key = key; + return this; + } + + @Override + public JwtParserBuilder setSigningKeyResolver(SigningKeyResolver signingKeyResolver) { + Assert.notNull(signingKeyResolver, "SigningKeyResolver cannot be null."); + this.signingKeyResolver = signingKeyResolver; + return this; + } + + @Override + public JwtParserBuilder setCompressionCodecResolver(CompressionCodecResolver compressionCodecResolver) { + Assert.notNull(compressionCodecResolver, "compressionCodecResolver cannot be null."); + this.compressionCodecResolver = compressionCodecResolver; + return this; + } + + @Override + public JwtParser build() { + + // Only lookup the deserializer IF it is null. It is possible a Deserializer implementation was set + // that is NOT exposed as a service and no other implementations are available for lookup. + if (this.deserializer == null) { + // try to find one based on the services available: + this.deserializer = Services.loadFirst(Deserializer.class); + } + + return new ImmutableJwtParser( + new DefaultJwtParser(signingKeyResolver, + key, + keyBytes, + clock, + allowedClockSkewMillis, + expectedClaims, + base64UrlDecoder, + deserializer, + compressionCodecResolver)); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultTextCodecFactory.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultTextCodecFactory.java (.../DefaultTextCodecFactory.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/DefaultTextCodecFactory.java (.../DefaultTextCodecFactory.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,6 +15,10 @@ */ package io.jsonwebtoken.impl; +/** + * @deprecated since 0.10.0 + */ +@Deprecated //remove just before 1.0.0 release public class DefaultTextCodecFactory implements TextCodecFactory { protected String getSystemProperty(String key) { Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/FixedClock.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/FixedClock.java (.../FixedClock.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/FixedClock.java (.../FixedClock.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jsonwebtoken.impl; import io.jsonwebtoken.Clock; Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/ImmutableJwtParser.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/ImmutableJwtParser.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/ImmutableJwtParser.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2019 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.impl; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Clock; +import io.jsonwebtoken.CompressionCodecResolver; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Header; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwt; +import io.jsonwebtoken.JwtHandler; +import io.jsonwebtoken.JwtParser; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.SigningKeyResolver; +import io.jsonwebtoken.UnsupportedJwtException; +import io.jsonwebtoken.io.Decoder; +import io.jsonwebtoken.io.Deserializer; +import io.jsonwebtoken.security.SignatureException; + +import java.security.Key; +import java.util.Date; +import java.util.Map; + +/** + * This JwtParser implementation exists as a stop gap until the mutable methods are removed from JwtParser. + * TODO: remove this class BEFORE 1.0 + * @since 0.11.0 + */ +class ImmutableJwtParser implements JwtParser { + + private final JwtParser jwtParser; + + ImmutableJwtParser(JwtParser jwtParser) { + this.jwtParser = jwtParser; + } + + private IllegalStateException doNotMutate() { + return new IllegalStateException("Cannot mutate a JwtParser created from JwtParserBuilder.build(), " + + "the mutable methods in JwtParser will be removed before version 1.0"); + } + + @Override + public JwtParser requireId(String id) { + throw doNotMutate(); + } + + @Override + public JwtParser requireSubject(String subject) { + throw doNotMutate(); + } + + @Override + public JwtParser requireAudience(String audience) { + throw doNotMutate(); + } + + @Override + public JwtParser requireIssuer(String issuer) { + throw doNotMutate(); + } + + @Override + public JwtParser requireIssuedAt(Date issuedAt) { + throw doNotMutate(); + } + + @Override + public JwtParser requireExpiration(Date expiration) { + throw doNotMutate(); + } + + @Override + public JwtParser requireNotBefore(Date notBefore) { + throw doNotMutate(); + } + + @Override + public JwtParser require(String claimName, Object value) { + throw doNotMutate(); + } + + @Override + public JwtParser setClock(Clock clock) { + throw doNotMutate(); + } + + @Override + public JwtParser setAllowedClockSkewSeconds(long seconds) { + throw doNotMutate(); + } + + @Override + public JwtParser setSigningKey(byte[] key) { + throw doNotMutate(); + } + + @Override + public JwtParser setSigningKey(String base64EncodedSecretKey) { + throw doNotMutate(); + } + + @Override + public JwtParser setSigningKey(Key key) { + throw doNotMutate(); + } + + @Override + public JwtParser setSigningKeyResolver(SigningKeyResolver signingKeyResolver) { + throw doNotMutate(); + } + + @Override + public JwtParser setCompressionCodecResolver(CompressionCodecResolver compressionCodecResolver) { + throw doNotMutate(); + } + + @Override + public JwtParser base64UrlDecodeWith(Decoder base64UrlDecoder) { + throw doNotMutate(); + } + + @Override + public JwtParser deserializeJsonWith(Deserializer> deserializer) { + throw doNotMutate(); + } + + @Override + public boolean isSigned(String jwt) { + return this.jwtParser.isSigned(jwt); + } + + @Override + public Jwt parse(String jwt) throws ExpiredJwtException, MalformedJwtException, SignatureException, IllegalArgumentException { + return this.jwtParser.parse(jwt); + } + + @Override + public T parse(String jwt, JwtHandler handler) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException { + return this.jwtParser.parse(jwt, handler); + } + + @Override + public Jwt parsePlaintextJwt(String plaintextJwt) throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException { + return this.jwtParser.parsePlaintextJwt(plaintextJwt); + } + + @Override + public Jwt parseClaimsJwt(String claimsJwt) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException { + return this.jwtParser.parseClaimsJwt(claimsJwt); + } + + @Override + public Jws parsePlaintextJws(String plaintextJws) throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException { + return this.jwtParser.parsePlaintextJws(plaintextJws); + } + + @Override + public Jws parseClaimsJws(String claimsJws) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException { + return this.jwtParser.parseClaimsJws(claimsJws); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/JwtMap.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/JwtMap.java (.../JwtMap.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/JwtMap.java (.../JwtMap.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -16,24 +16,27 @@ package io.jsonwebtoken.impl; import io.jsonwebtoken.lang.Assert; - +import io.jsonwebtoken.lang.DateFormats; +import java.text.ParseException; +import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -public class JwtMap implements Map { +public class JwtMap implements Map { private final Map map; public JwtMap() { - this(new LinkedHashMap()); + this.map = new LinkedHashMap<>(); } - public JwtMap(Map map) { + public JwtMap(Map map) { + this(); Assert.notNull(map, "Map argument cannot be null."); - this.map = map; + putAll(map); } protected String getString(String name) { @@ -46,23 +49,55 @@ return null; } else if (v instanceof Date) { return (Date) v; + } else if (v instanceof Calendar) { //since 0.10.0 + return ((Calendar) v).getTime(); } else if (v instanceof Number) { + //assume millis: + long millis = ((Number) v).longValue(); + return new Date(millis); + } else if (v instanceof String) { + return parseIso8601Date((String) v, name); //ISO-8601 parsing since 0.10.0 + } else { + throw new IllegalStateException("Cannot create Date from '" + name + "' value '" + v + "'."); + } + } + + /** + * @since 0.10.0 + */ + private static Date parseIso8601Date(String s, String name) throws IllegalArgumentException { + try { + return DateFormats.parseIso8601Date(s); + } catch (ParseException e) { + String msg = "'" + name + "' value does not appear to be ISO-8601-formatted: " + s; + throw new IllegalArgumentException(msg, e); + } + } + + /** + * @since 0.10.0 + */ + protected static Date toSpecDate(Object v, String name) { + if (v == null) { + return null; + } else if (v instanceof Number) { // https://github.com/jwtk/jjwt/issues/122: // The JWT RFC *mandates* NumericDate values are represented as seconds. // Because Because java.util.Date requires milliseconds, we need to multiply by 1000: long seconds = ((Number) v).longValue(); - long millis = seconds * 1000; - return new Date(millis); + v = seconds * 1000; } else if (v instanceof String) { // https://github.com/jwtk/jjwt/issues/122 // The JWT RFC *mandates* NumericDate values are represented as seconds. // Because Because java.util.Date requires milliseconds, we need to multiply by 1000: - long seconds = Long.parseLong((String) v); - long millis = seconds * 1000; - return new Date(millis); - } else { - throw new IllegalStateException("Cannot convert '" + name + "' value [" + v + "] to Date instance."); + try { + long seconds = Long.parseLong((String) v); + v = seconds * 1000; + } catch (NumberFormatException ignored) { + } } + //v would have been normalized to milliseconds if it was a number value, so perform normal date conversion: + return toDate(v, name); } protected void setValue(String name, Object v) { @@ -73,11 +108,7 @@ } } - protected Date getDate(String name) { - Object v = map.get(name); - return toDate(v, name); - } - + @Deprecated //remove just before 1.0.0 protected void setDate(String name, Date d) { if (d == null) { map.remove(name); @@ -87,6 +118,15 @@ } } + protected Object setDateAsSeconds(String name, Date d) { + if (d == null) { + return map.remove(name); + } else { + long seconds = d.getTime() / 1000; + return map.put(name, seconds); + } + } + @Override public int size() { return map.size(); @@ -133,7 +173,7 @@ return; } for (String s : m.keySet()) { - map.put(s, m.get(s)); + put(s, m.get(s)); } } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/TextCodec.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/TextCodec.java (.../TextCodec.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/TextCodec.java (.../TextCodec.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,11 +15,30 @@ */ package io.jsonwebtoken.impl; +import io.jsonwebtoken.io.Decoder; +import io.jsonwebtoken.io.Encoder; + +/** + * @deprecated since 0.10.0. Use an {@link Encoder} or {@link Decoder} + * as needed. This class will be removed before 1.0.0 + */ +@Deprecated public interface TextCodec { - public static final TextCodec BASE64 = new DefaultTextCodecFactory().getTextCodec(); - public static final TextCodec BASE64URL = new Base64UrlCodec(); + /** + * @deprecated since 0.10.0. Use {@code io.jsonwebtoken.io.Encoders#BASE64} or + * {@code io.jsonwebtoken.io.Decoders#BASE64} instead. This class will be removed before 1.0.0 + */ + @Deprecated + TextCodec BASE64 = new Base64Codec(); + /** + * @deprecated since 0.10.0. Use {@code io.jsonwebtoken.io.Encoders#BASE64URL} or + * {@code io.jsonwebtoken.io.Decoders#BASE64URL} instead. This class will be removed before 1.0.0 + */ + @Deprecated + TextCodec BASE64URL = new Base64UrlCodec(); + String encode(String data); String encode(byte[] data); Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/TextCodecFactory.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/TextCodecFactory.java (.../TextCodecFactory.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/TextCodecFactory.java (.../TextCodecFactory.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,6 +15,10 @@ */ package io.jsonwebtoken.impl; +/** + * @deprecated since 0.10.0 + */ +@Deprecated public interface TextCodecFactory { TextCodec getTextCodec(); Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/AbstractCompressionCodec.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/AbstractCompressionCodec.java (.../AbstractCompressionCodec.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/AbstractCompressionCodec.java (.../AbstractCompressionCodec.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -18,8 +18,12 @@ import io.jsonwebtoken.CompressionCodec; import io.jsonwebtoken.CompressionException; import io.jsonwebtoken.lang.Assert; +import io.jsonwebtoken.lang.Objects; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; /** * Abstract class that asserts arguments and wraps IOException with CompressionException. @@ -28,6 +32,44 @@ */ public abstract class AbstractCompressionCodec implements CompressionCodec { + //package-protected for a point release. This can be made protected on a minor release (0.11.0, 0.12.0, 1.0, etc). + //TODO: make protected on a minor release + interface StreamWrapper { + OutputStream wrap(OutputStream out) throws IOException; + } + + //package-protected for a point release. This can be made protected on a minor release (0.11.0, 0.12.0, 1.0, etc). + //TODO: make protected on a minor release + byte[] readAndClose(InputStream input) throws IOException { + byte[] buffer = new byte[512]; + ByteArrayOutputStream out = new ByteArrayOutputStream(buffer.length); + int read; + try { + read = input.read(buffer); //assignment separate from loop invariant check for code coverage checks + while (read != -1) { + out.write(buffer, 0, read); + read = input.read(buffer); + } + } finally { + Objects.nullSafeClose(input); + } + return out.toByteArray(); + } + + //package-protected for a point release. This can be made protected on a minor release (0.11.0, 0.12.0, 1.0, etc). + //TODO: make protected on a minor release + byte[] writeAndClose(byte[] payload, StreamWrapper wrapper) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(512); + OutputStream compressionStream = wrapper.wrap(outputStream); + try { + compressionStream.write(payload); + compressionStream.flush(); + } finally { + Objects.nullSafeClose(compressionStream); + } + return outputStream.toByteArray(); + } + /** * Implement this method to do the actual work of compressing the payload * Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/DefaultCompressionCodecResolver.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/DefaultCompressionCodecResolver.java (.../DefaultCompressionCodecResolver.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/DefaultCompressionCodecResolver.java (.../DefaultCompressionCodecResolver.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -17,20 +17,26 @@ import io.jsonwebtoken.CompressionCodec; import io.jsonwebtoken.CompressionCodecResolver; +import io.jsonwebtoken.CompressionCodecs; import io.jsonwebtoken.CompressionException; import io.jsonwebtoken.Header; import io.jsonwebtoken.lang.Assert; +import io.jsonwebtoken.impl.lang.Services; import io.jsonwebtoken.lang.Strings; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + /** * Default implementation of {@link CompressionCodecResolver} that supports the following: *

*

    - *
  • If the specified JWT {@link Header} does not have a {@code calg} header, this implementation does + *
  • If the specified JWT {@link Header} does not have a {@code zip} header, this implementation does * nothing and returns {@code null} to the caller, indicating no compression was used.
  • - *
  • If the header has a {@code calg} value of {@code DEF}, a {@link DeflateCompressionCodec} will be returned.
  • - *
  • If the header has a {@code calg} value of {@code GZIP}, a {@link GzipCompressionCodec} will be returned.
  • - *
  • If the header has any other {@code calg} value, a {@link CompressionException} is thrown to reflect an + *
  • If the header has a {@code zip} value of {@code DEF}, a {@link DeflateCompressionCodec} will be returned.
  • + *
  • If the header has a {@code zip} value of {@code GZIP}, a {@link GzipCompressionCodec} will be returned.
  • + *
  • If the header has any other {@code zip} value, a {@link CompressionException} is thrown to reflect an * unrecognized algorithm.
  • *
* @@ -45,6 +51,22 @@ */ public class DefaultCompressionCodecResolver implements CompressionCodecResolver { + private static final String MISSING_COMPRESSION_MESSAGE = "Unable to find an implementation for compression algorithm [%s] using java.util.ServiceLoader. Ensure you include a backing implementation .jar in the classpath, for example jjwt-impl.jar, or your own .jar for custom implementations."; + + private final Map codecs; + + public DefaultCompressionCodecResolver() { + Map codecMap = new HashMap<>(); + for (CompressionCodec codec : Services.loadAll(CompressionCodec.class)) { + codecMap.put(codec.getAlgorithmName().toUpperCase(), codec); + } + + codecMap.put(CompressionCodecs.DEFLATE.getAlgorithmName().toUpperCase(), CompressionCodecs.DEFLATE); + codecMap.put(CompressionCodecs.GZIP.getAlgorithmName().toUpperCase(), CompressionCodecs.GZIP); + + codecs = Collections.unmodifiableMap(codecMap); + } + @Override public CompressionCodec resolveCompressionCodec(Header header) { String cmpAlg = getAlgorithmFromHeader(header); @@ -54,19 +76,23 @@ if (!hasCompressionAlgorithm) { return null; } - if (CompressionCodecs.DEFLATE.getAlgorithmName().equalsIgnoreCase(cmpAlg)) { - return CompressionCodecs.DEFLATE; - } - if (CompressionCodecs.GZIP.getAlgorithmName().equalsIgnoreCase(cmpAlg)) { - return CompressionCodecs.GZIP; - } - - throw new CompressionException("Unsupported compression algorithm '" + cmpAlg + "'"); + return byName(cmpAlg); } private String getAlgorithmFromHeader(Header header) { Assert.notNull(header, "header cannot be null."); return header.getCompressionAlgorithm(); } + + private CompressionCodec byName(String name) { + Assert.hasText(name, "'name' must not be empty"); + + CompressionCodec codec = codecs.get(name.toUpperCase()); + if (codec == null) { + throw new CompressionException(String.format(MISSING_COMPRESSION_MESSAGE, name)); + } + + return codec; + } } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/DeflateCompressionCodec.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/DeflateCompressionCodec.java (.../DeflateCompressionCodec.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/DeflateCompressionCodec.java (.../DeflateCompressionCodec.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -17,10 +17,13 @@ import io.jsonwebtoken.lang.Objects; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.zip.Deflater; +import java.io.InputStream; +import java.io.OutputStream; import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; import java.util.zip.InflaterOutputStream; /** @@ -32,32 +35,47 @@ private static final String DEFLATE = "DEF"; + private static final StreamWrapper WRAPPER = new StreamWrapper() { + @Override + public OutputStream wrap(OutputStream out) { + return new DeflaterOutputStream(out); + } + }; + @Override public String getAlgorithmName() { return DEFLATE; } @Override - public byte[] doCompress(byte[] payload) throws IOException { + protected byte[] doCompress(byte[] payload) throws IOException { + return writeAndClose(payload, WRAPPER); + } - Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION); - - ByteArrayOutputStream outputStream = null; - DeflaterOutputStream deflaterOutputStream = null; + @Override + protected byte[] doDecompress(final byte[] compressed) throws IOException { try { - outputStream = new ByteArrayOutputStream(); - deflaterOutputStream = new DeflaterOutputStream(outputStream, deflater, true); - - deflaterOutputStream.write(payload, 0, payload.length); - deflaterOutputStream.flush(); - return outputStream.toByteArray(); - } finally { - Objects.nullSafeClose(outputStream, deflaterOutputStream); + return readAndClose(new InflaterInputStream(new ByteArrayInputStream(compressed))); + } catch (IOException e1) { + try { + return doDecompressBackCompat(compressed); + } catch (IOException e2) { + throw e1; //retain/report original exception + } } } - @Override - public byte[] doDecompress(byte[] compressed) throws IOException { + /** + * This implementation was in 0.10.6 and earlier - it will be used as a fallback for backwards compatibility if + * {@link #readAndClose(InputStream)} fails per Issue 536. + * + * @param compressed the compressed byte array + * @return decompressed bytes + * @throws IOException if unable to decompress using the 0.10.6 and earlier logic + * @since 0.10.8 + */ + // package protected on purpose + byte[] doDecompressBackCompat(byte[] compressed) throws IOException { InflaterOutputStream inflaterOutputStream = null; ByteArrayOutputStream decompressedOutputStream = null; Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/GzipCompressionCodec.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/GzipCompressionCodec.java (.../GzipCompressionCodec.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/compression/GzipCompressionCodec.java (.../GzipCompressionCodec.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -16,11 +16,10 @@ package io.jsonwebtoken.impl.compression; import io.jsonwebtoken.CompressionCodec; -import io.jsonwebtoken.lang.Objects; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -33,43 +32,25 @@ private static final String GZIP = "GZIP"; + private static final StreamWrapper WRAPPER = new StreamWrapper() { + @Override + public OutputStream wrap(OutputStream out) throws IOException { + return new GZIPOutputStream(out); + } + }; + @Override public String getAlgorithmName() { return GZIP; } @Override - protected byte[] doDecompress(byte[] compressed) throws IOException { - byte[] buffer = new byte[512]; - - ByteArrayOutputStream outputStream = null; - GZIPInputStream gzipInputStream = null; - ByteArrayInputStream inputStream = null; - - try { - inputStream = new ByteArrayInputStream(compressed); - gzipInputStream = new GZIPInputStream(inputStream); - outputStream = new ByteArrayOutputStream(); - int read = gzipInputStream.read(buffer); - while (read != -1) { - outputStream.write(buffer, 0, read); - read = gzipInputStream.read(buffer); - } - return outputStream.toByteArray(); - } finally { - Objects.nullSafeClose(inputStream, gzipInputStream, outputStream); - } + protected byte[] doCompress(byte[] payload) throws IOException { + return writeAndClose(payload, WRAPPER); } - protected byte[] doCompress(byte[] payload) throws IOException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - GZIPOutputStream compressorOutputStream = new GZIPOutputStream(outputStream, true); - try { - compressorOutputStream.write(payload, 0, payload.length); - compressorOutputStream.finish(); - return outputStream.toByteArray(); - } finally { - Objects.nullSafeClose(compressorOutputStream, outputStream); - } + @Override + protected byte[] doDecompress(byte[] compressed) throws IOException { + return readAndClose(new GZIPInputStream(new ByteArrayInputStream(compressed))); } } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/DefaultJwtSignatureValidator.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/DefaultJwtSignatureValidator.java (.../DefaultJwtSignatureValidator.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/DefaultJwtSignatureValidator.java (.../DefaultJwtSignatureValidator.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -16,7 +16,8 @@ package io.jsonwebtoken.impl.crypto; import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.impl.TextCodec; +import io.jsonwebtoken.io.Decoder; +import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.lang.Assert; import java.nio.charset.Charset; @@ -27,22 +28,35 @@ private static final Charset US_ASCII = Charset.forName("US-ASCII"); private final SignatureValidator signatureValidator; + private final Decoder base64UrlDecoder; + @Deprecated public DefaultJwtSignatureValidator(SignatureAlgorithm alg, Key key) { - this(DefaultSignatureValidatorFactory.INSTANCE, alg, key); + this(DefaultSignatureValidatorFactory.INSTANCE, alg, key, Decoders.BASE64URL); } + public DefaultJwtSignatureValidator(SignatureAlgorithm alg, Key key, Decoder base64UrlDecoder) { + this(DefaultSignatureValidatorFactory.INSTANCE, alg, key, base64UrlDecoder); + } + + @Deprecated public DefaultJwtSignatureValidator(SignatureValidatorFactory factory, SignatureAlgorithm alg, Key key) { + this(factory, alg, key, Decoders.BASE64URL); + } + + public DefaultJwtSignatureValidator(SignatureValidatorFactory factory, SignatureAlgorithm alg, Key key, Decoder base64UrlDecoder) { Assert.notNull(factory, "SignerFactory argument cannot be null."); + Assert.notNull(base64UrlDecoder, "Base64Url decoder argument cannot be null."); this.signatureValidator = factory.createSignatureValidator(alg, key); + this.base64UrlDecoder = base64UrlDecoder; } @Override public boolean isValid(String jwtWithoutSignature, String base64UrlEncodedSignature) { byte[] data = jwtWithoutSignature.getBytes(US_ASCII); - byte[] signature = TextCodec.BASE64URL.decode(base64UrlEncodedSignature); + byte[] signature = base64UrlDecoder.decode(base64UrlEncodedSignature); return this.signatureValidator.isValid(data, signature); } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/DefaultJwtSigner.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/DefaultJwtSigner.java (.../DefaultJwtSigner.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/DefaultJwtSigner.java (.../DefaultJwtSigner.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -16,7 +16,8 @@ package io.jsonwebtoken.impl.crypto; import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.impl.TextCodec; +import io.jsonwebtoken.io.Encoder; +import io.jsonwebtoken.io.Encoders; import io.jsonwebtoken.lang.Assert; import java.nio.charset.Charset; @@ -27,13 +28,26 @@ private static final Charset US_ASCII = Charset.forName("US-ASCII"); private final Signer signer; + private final Encoder base64UrlEncoder; + @Deprecated public DefaultJwtSigner(SignatureAlgorithm alg, Key key) { - this(DefaultSignerFactory.INSTANCE, alg, key); + this(DefaultSignerFactory.INSTANCE, alg, key, Encoders.BASE64URL); } + public DefaultJwtSigner(SignatureAlgorithm alg, Key key, Encoder base64UrlEncoder) { + this(DefaultSignerFactory.INSTANCE, alg, key, base64UrlEncoder); + } + + @Deprecated public DefaultJwtSigner(SignerFactory factory, SignatureAlgorithm alg, Key key) { + this(factory, alg, key, Encoders.BASE64URL); + } + + public DefaultJwtSigner(SignerFactory factory, SignatureAlgorithm alg, Key key, Encoder base64UrlEncoder) { Assert.notNull(factory, "SignerFactory argument cannot be null."); + Assert.notNull(base64UrlEncoder, "Base64Url Encoder cannot be null."); + this.base64UrlEncoder = base64UrlEncoder; this.signer = factory.createSigner(alg, key); } @@ -44,6 +58,6 @@ byte[] signature = signer.sign(bytesToSign); - return TextCodec.BASE64URL.encode(signature); + return base64UrlEncoder.encode(signature); } } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/EllipticCurveProvider.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/EllipticCurveProvider.java (.../EllipticCurveProvider.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/EllipticCurveProvider.java (.../EllipticCurveProvider.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,17 +15,19 @@ */ package io.jsonwebtoken.impl.crypto; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.lang.Assert; +import io.jsonwebtoken.lang.Strings; + import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.SecureRandom; +import java.security.spec.ECGenParameterSpec; import java.util.HashMap; import java.util.Map; -import io.jsonwebtoken.JwtException; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.lang.Assert; - /** * ElliptiCurve crypto provider. * @@ -78,17 +80,15 @@ * @see #generateKeyPair(String, String, SignatureAlgorithm, SecureRandom) */ public static KeyPair generateKeyPair(SignatureAlgorithm alg) { - return generateKeyPair(alg, SignatureProvider.DEFAULT_SECURE_RANDOM); + return generateKeyPair(alg, DEFAULT_SECURE_RANDOM); } /** * Generates a new secure-random key pair of sufficient strength for the specified Elliptic Curve {@link * SignatureAlgorithm} (must be one of {@code ES256}, {@code ES384} or {@code ES512}) using the specified {@link * SecureRandom} random number generator. This is a convenience method that immediately delegates to {@link - * #generateKeyPair(String, String, SignatureAlgorithm, SecureRandom)} using {@code "ECDSA"} as the {@code - * jcaAlgorithmName} and {@code "BC"} as the {@code jcaProviderName} since EllipticCurve requires the use of an - * external JCA provider ({@code BC stands for BouncyCastle}. This will work as expected as long as the - * BouncyCastle dependency is in the runtime classpath. + * #generateKeyPair(String, String, SignatureAlgorithm, SecureRandom)} using {@code "EC"} as the {@code + * jcaAlgorithmName}. * * @param alg alg the algorithm indicating strength, must be one of {@code ES256}, {@code ES384} or {@code * ES512} @@ -101,7 +101,7 @@ * @see #generateKeyPair(String, String, SignatureAlgorithm, SecureRandom) */ public static KeyPair generateKeyPair(SignatureAlgorithm alg, SecureRandom random) { - return generateKeyPair("ECDSA", "BC", alg, random); + return generateKeyPair("EC", null, alg, random); } /** @@ -111,8 +111,8 @@ * * @param jcaAlgorithmName the JCA name of the algorithm to use for key pair generation, for example, {@code * ECDSA}. - * @param jcaProviderName the JCA provider name of the algorithm implementation, for example {@code BC} for - * BouncyCastle. + * @param jcaProviderName the JCA provider name of the algorithm implementation (for example {@code "BC"} for + * BouncyCastle) or {@code null} if the default provider should be used. * @param alg alg the algorithm indicating strength, must be one of {@code ES256}, {@code ES384} or * {@code ES512} * @param random the SecureRandom generator to use during key generation. @@ -128,9 +128,17 @@ Assert.notNull(alg, "SignatureAlgorithm argument cannot be null."); Assert.isTrue(alg.isEllipticCurve(), "SignatureAlgorithm argument must represent an Elliptic Curve algorithm."); try { - KeyPairGenerator g = KeyPairGenerator.getInstance(jcaAlgorithmName, jcaProviderName); + KeyPairGenerator g; + + if (Strings.hasText(jcaProviderName)) { + g = KeyPairGenerator.getInstance(jcaAlgorithmName, jcaProviderName); + } else { + g = KeyPairGenerator.getInstance(jcaAlgorithmName); + } + String paramSpecCurveName = EC_CURVE_NAMES.get(alg); - g.initialize(org.bouncycastle.jce.ECNamedCurveTable.getParameterSpec(paramSpecCurveName), random); + ECGenParameterSpec spec = new ECGenParameterSpec(paramSpecCurveName); + g.initialize(spec, random); return g.generateKeyPair(); } catch (Exception e) { throw new IllegalStateException("Unable to generate Elliptic Curve KeyPair: " + e.getMessage(), e); @@ -143,18 +151,19 @@ * * @param alg The ECDSA algorithm. Must be supported and not * {@code null}. - * * @return The expected byte array length for the signature. - * * @throws JwtException If the algorithm is not supported. */ public static int getSignatureByteArrayLength(final SignatureAlgorithm alg) - throws JwtException { + throws JwtException { switch (alg) { - case ES256: return 64; - case ES384: return 96; - case ES512: return 132; + case ES256: + return 64; + case ES384: + return 96; + case ES512: + return 132; default: throw new JwtException("Unsupported Algorithm: " + alg.name()); } @@ -167,13 +176,10 @@ * * @param derSignature The ASN1./DER-encoded. Must not be {@code null}. * @param outputLength The expected length of the ECDSA JWS signature. - * * @return The ECDSA JWS encoded signature. - * * @throws JwtException If the ASN.1/DER signature format is invalid. */ - public static byte[] transcodeSignatureToConcat(final byte[] derSignature, int outputLength) - throws JwtException { + public static byte[] transcodeSignatureToConcat(final byte[] derSignature, int outputLength) throws JwtException { if (derSignature.length < 8 || derSignature[0] != 48) { throw new JwtException("Invalid ECDSA signature format"); @@ -191,24 +197,24 @@ byte rLength = derSignature[offset + 1]; int i = rLength; - while ((i > 0) - && (derSignature[(offset + 2 + rLength) - i] == 0)) + while ((i > 0) && (derSignature[(offset + 2 + rLength) - i] == 0)) { i--; + } byte sLength = derSignature[offset + 2 + rLength + 1]; int j = sLength; - while ((j > 0) - && (derSignature[(offset + 2 + rLength + 2 + sLength) - j] == 0)) + while ((j > 0) && (derSignature[(offset + 2 + rLength + 2 + sLength) - j] == 0)) { j--; + } int rawLen = Math.max(i, j); rawLen = Math.max(rawLen, outputLength / 2); if ((derSignature[offset - 1] & 0xff) != derSignature.length - offset - || (derSignature[offset - 1] & 0xff) != 2 + rLength + 2 + sLength - || derSignature[offset] != 2 - || derSignature[offset + 2 + rLength] != 2) { + || (derSignature[offset - 1] & 0xff) != 2 + rLength + 2 + sLength + || derSignature[offset] != 2 + || derSignature[offset + 2 + rLength] != 2) { throw new JwtException("Invalid ECDSA signature format"); } @@ -221,29 +227,25 @@ } - /** * Transcodes the ECDSA JWS signature into ASN.1/DER format for use by * the JCA verifier. * * @param jwsSignature The JWS signature, consisting of the * concatenated R and S values. Must not be * {@code null}. - * * @return The ASN.1/DER encoded signature. - * * @throws JwtException If the ECDSA JWS signature format is invalid. */ - public static byte[] transcodeSignatureToDER(byte[] jwsSignature) - throws JwtException { + public static byte[] transcodeSignatureToDER(byte[] jwsSignature) throws JwtException { int rawLen = jwsSignature.length / 2; int i = rawLen; - while((i > 0) - && (jwsSignature[rawLen - i] == 0)) + while ((i > 0) && (jwsSignature[rawLen - i] == 0)) { i--; + } int j = i; @@ -253,9 +255,9 @@ int k = rawLen; - while ((k > 0) - && (jwsSignature[2 * rawLen - k] == 0)) + while ((k > 0) && (jwsSignature[2 * rawLen - k] == 0)) { k--; + } int l = k; Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/EllipticCurveSignatureValidator.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/EllipticCurveSignatureValidator.java (.../EllipticCurveSignatureValidator.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/EllipticCurveSignatureValidator.java (.../EllipticCurveSignatureValidator.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,16 +15,16 @@ */ package io.jsonwebtoken.impl.crypto; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.lang.Assert; +import io.jsonwebtoken.security.SignatureException; + import java.security.InvalidKeyException; import java.security.Key; import java.security.PublicKey; import java.security.Signature; import java.security.interfaces.ECPublicKey; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.SignatureException; -import io.jsonwebtoken.lang.Assert; - public class EllipticCurveSignatureValidator extends EllipticCurveProvider implements SignatureValidator { private static final String EC_PUBLIC_KEY_REQD_MSG = Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/EllipticCurveSigner.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/EllipticCurveSigner.java (.../EllipticCurveSigner.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/EllipticCurveSigner.java (.../EllipticCurveSigner.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,17 +15,16 @@ */ package io.jsonwebtoken.impl.crypto; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.SignatureException; + import java.security.InvalidKeyException; import java.security.Key; import java.security.PrivateKey; import java.security.Signature; import java.security.interfaces.ECKey; -import java.security.interfaces.ECPrivateKey; -import io.jsonwebtoken.JwtException; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.SignatureException; - public class EllipticCurveSigner extends EllipticCurveProvider implements Signer { public EllipticCurveSigner(SignatureAlgorithm alg, Key key) { Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/MacProvider.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/MacProvider.java (.../MacProvider.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/MacProvider.java (.../MacProvider.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -18,9 +18,10 @@ import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.lang.Assert; +import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import java.security.Key; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; public abstract class MacProvider extends SignatureProvider { @@ -31,11 +32,11 @@ } /** - * Generates a new secure-random 512 bit secret key suitable for creating and verifying HMAC signatures. This is a - * convenience method that immediately delegates to {@link #generateKey(SignatureAlgorithm)} using {@link + * Generates a new secure-random 512 bit secret key suitable for creating and verifying HMAC-SHA signatures. This + * is a convenience method that immediately delegates to {@link #generateKey(SignatureAlgorithm)} using {@link * SignatureAlgorithm#HS512} as the method argument. * - * @return a new secure-random 512 bit secret key suitable for creating and verifying HMAC signatures. + * @return a new secure-random 512 bit secret key suitable for creating and verifying HMAC-SHA signatures. * @see #generateKey(SignatureAlgorithm) * @see #generateKey(SignatureAlgorithm, SecureRandom) * @since 0.5 @@ -59,7 +60,7 @@ * @since 0.5 */ public static SecretKey generateKey(SignatureAlgorithm alg) { - return generateKey(alg, SignatureProvider.DEFAULT_SECURE_RANDOM); + return generateKey(alg, DEFAULT_SECURE_RANDOM); } /** @@ -78,26 +79,22 @@ * @see #generateKey() * @see #generateKey(SignatureAlgorithm) * @since 0.5 + * @deprecated since 0.10.0 - use {@link #generateKey(SignatureAlgorithm)} instead. */ + @Deprecated public static SecretKey generateKey(SignatureAlgorithm alg, SecureRandom random) { Assert.isTrue(alg.isHmac(), "SignatureAlgorithm argument must represent an HMAC algorithm."); - byte[] bytes; + KeyGenerator gen; - switch (alg) { - case HS256: - bytes = new byte[32]; - break; - case HS384: - bytes = new byte[48]; - break; - default: - bytes = new byte[64]; + try { + gen = KeyGenerator.getInstance(alg.getJcaName()); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("The " + alg.getJcaName() + " algorithm is not available. " + + "This should never happen on JDK 7 or later - please report this to the JJWT developers.", e); } - random.nextBytes(bytes); - - return new SecretKeySpec(bytes, alg.getJcaName()); + return gen.generateKey(); } } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/MacSigner.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/MacSigner.java (.../MacSigner.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/MacSigner.java (.../MacSigner.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -16,8 +16,8 @@ package io.jsonwebtoken.impl.crypto; import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.lang.Assert; +import io.jsonwebtoken.security.SignatureException; import javax.crypto.Mac; import javax.crypto.SecretKey; Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/MacValidator.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/MacValidator.java (.../MacValidator.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/MacValidator.java (.../MacValidator.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -19,7 +19,6 @@ import java.security.Key; import java.security.MessageDigest; -import java.util.Arrays; public class MacValidator implements SignatureValidator { Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/RsaProvider.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/RsaProvider.java (.../RsaProvider.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/RsaProvider.java (.../RsaProvider.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -16,8 +16,9 @@ package io.jsonwebtoken.impl.crypto; import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.lang.Assert; +import io.jsonwebtoken.lang.RuntimeEnvironment; +import io.jsonwebtoken.security.SignatureException; import java.security.InvalidAlgorithmParameterException; import java.security.Key; @@ -54,6 +55,10 @@ return m; } + static { + RuntimeEnvironment.enableBouncyCastleIfPossible(); //PS256, PS384, PS512 on <= JDK 10 require BC + } + protected RsaProvider(SignatureAlgorithm alg, Key key) { super(alg, key); Assert.isTrue(alg.isRsa(), "SignatureAlgorithm must be an RSASSA or RSASSA-PSS algorithm."); @@ -111,10 +116,40 @@ * @since 0.5 */ public static KeyPair generateKeyPair(int keySizeInBits) { - return generateKeyPair(keySizeInBits, SignatureProvider.DEFAULT_SECURE_RANDOM); + return generateKeyPair(keySizeInBits, DEFAULT_SECURE_RANDOM); } /** + * Generates a new RSA secure-randomly key pair suitable for the specified SignatureAlgorithm using JJWT's + * default {@link SignatureProvider#DEFAULT_SECURE_RANDOM SecureRandom instance}. This is a convenience method + * that immediately delegates to {@link #generateKeyPair(int)} based on the relevant key size for the specified + * algorithm. + * + * @param alg the signature algorithm to inspect to determine a size in bits. + * @return a new RSA secure-random key pair of the specified size. + * @see #generateKeyPair() + * @see #generateKeyPair(int, SecureRandom) + * @see #generateKeyPair(String, int, SecureRandom) + * @since 0.10.0 + */ + @SuppressWarnings("unused") //used by io.jsonwebtoken.security.Keys + public static KeyPair generateKeyPair(SignatureAlgorithm alg) { + Assert.isTrue(alg.isRsa(), "Only RSA algorithms are supported by this method."); + int keySizeInBits = 4096; + switch (alg) { + case RS256: + case PS256: + keySizeInBits = 2048; + break; + case RS384: + case PS384: + keySizeInBits = 3072; + break; + } + return generateKeyPair(keySizeInBits, DEFAULT_SECURE_RANDOM); + } + + /** * Generates a new RSA secure-random key pair of the specified size using the given SecureRandom number generator. * This is a convenience method that immediately delegates to {@link #generateKeyPair(String, int, SecureRandom)} * using {@code RSA} as the {@code jcaAlgorithmName} argument. Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/RsaSignatureValidator.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/RsaSignatureValidator.java (.../RsaSignatureValidator.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/RsaSignatureValidator.java (.../RsaSignatureValidator.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -16,11 +16,12 @@ package io.jsonwebtoken.impl.crypto; import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.lang.Assert; +import io.jsonwebtoken.security.SignatureException; import java.security.InvalidKeyException; import java.security.Key; +import java.security.MessageDigest; import java.security.PublicKey; import java.security.Signature; import java.security.interfaces.RSAPrivateKey; @@ -52,7 +53,7 @@ } else { Assert.notNull(this.SIGNER, "RSA Signer instance cannot be null. This is a bug. Please report it."); byte[] computed = this.SIGNER.sign(data); - return Arrays.equals(computed, signature); + return MessageDigest.isEqual(computed, signature); } } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/RsaSigner.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/RsaSigner.java (.../RsaSigner.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/RsaSigner.java (.../RsaSigner.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -16,7 +16,7 @@ package io.jsonwebtoken.impl.crypto; import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.SignatureException; +import io.jsonwebtoken.security.SignatureException; import java.security.InvalidKeyException; import java.security.Key; Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/SignatureProvider.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/SignatureProvider.java (.../SignatureProvider.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/SignatureProvider.java (.../SignatureProvider.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,16 +15,16 @@ */ package io.jsonwebtoken.impl.crypto; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.lang.Assert; +import io.jsonwebtoken.lang.RuntimeEnvironment; +import io.jsonwebtoken.security.SignatureException; + import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.Signature; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.SignatureException; -import io.jsonwebtoken.lang.Assert; -import io.jsonwebtoken.lang.RuntimeEnvironment; - abstract class SignatureProvider { /** Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/Signer.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/Signer.java (.../Signer.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/crypto/Signer.java (.../Signer.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -15,7 +15,7 @@ */ package io.jsonwebtoken.impl.crypto; -import io.jsonwebtoken.SignatureException; +import io.jsonwebtoken.security.SignatureException; public interface Signer { Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/lang/LegacyServices.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/lang/LegacyServices.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/lang/LegacyServices.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,26 @@ +package io.jsonwebtoken.impl.lang; + +import io.jsonwebtoken.lang.Classes; +import io.jsonwebtoken.lang.UnknownClassException; + +/** + * A backward compatibility {@link Services} utility to help migrate away from {@link Classes#newInstance(String)}. + * TODO: remove before v1.0 + * @deprecated use {@link Services} directly + */ +@Deprecated +public final class LegacyServices { + + /** + * Wraps {@code Services.loadFirst} and throws a {@link UnknownClassException} instead of a + * {@link UnavailableImplementationException} to retain the previous behavior. This method should be used when + * to retain the previous behavior of methods that throw an unchecked UnknownClassException. + */ + public static T loadFirst(Class spi) { + try { + return Services.loadFirst(spi); + } catch (UnavailableImplementationException e) { + throw new UnknownClassException(e.getMessage(), e); + } + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/lang/Services.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/lang/Services.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/lang/Services.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.impl.lang; + +import io.jsonwebtoken.lang.Assert; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ServiceLoader; + +import static io.jsonwebtoken.lang.Collections.arrayToList; + +/** + * Helper class for loading services from the classpath, using a {@link ServiceLoader}. Decouples loading logic for + * better separation of concerns and testability. + */ +public final class Services { + + private static final List CLASS_LOADER_ACCESSORS = arrayToList(new ClassLoaderAccessor[] { + new ClassLoaderAccessor() { + @Override + public ClassLoader getClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + }, + new ClassLoaderAccessor() { + @Override + public ClassLoader getClassLoader() { + return Services.class.getClassLoader(); + } + }, + new ClassLoaderAccessor() { + @Override + public ClassLoader getClassLoader() { + return ClassLoader.getSystemClassLoader(); + } + } + }); + + private Services() {} + + /** + * Loads and instantiates all service implementation of the given SPI class and returns them as a List. + * + * @param spi The class of the Service Provider Interface + * @param The type of the SPI + * @return An unmodifiable list with an instance of all available implementations of the SPI. No guarantee is given + * on the order of implementations, if more than one. + */ + public static List loadAll(Class spi) { + Assert.notNull(spi, "Parameter 'spi' must not be null."); + + for (ClassLoaderAccessor classLoaderAccessor : CLASS_LOADER_ACCESSORS) { + List implementations = loadAll(spi, classLoaderAccessor.getClassLoader()); + if (!implementations.isEmpty()) { + return Collections.unmodifiableList(implementations); + } + } + + throw new UnavailableImplementationException(spi); + } + + private static List loadAll(Class spi, ClassLoader classLoader) { + ServiceLoader serviceLoader = ServiceLoader.load(spi, classLoader); + List implementations = new ArrayList<>(); + for (T implementation : serviceLoader) { + implementations.add(implementation); + } + return implementations; + } + + /** + * Loads the first available implementation the given SPI class from the classpath. Uses the {@link ServiceLoader} + * to find implementations. When multiple implementations are available it will return the first one that it + * encounters. There is no guarantee with regard to ordering. + * + * @param spi The class of the Service Provider Interface + * @param The type of the SPI + * @return A new instance of the service. + * @throws UnavailableImplementationException When no implementation the SPI is available on the classpath. + */ + public static T loadFirst(Class spi) { + Assert.notNull(spi, "Parameter 'spi' must not be null."); + + for (ClassLoaderAccessor classLoaderAccessor : CLASS_LOADER_ACCESSORS) { + T result = loadFirst(spi, classLoaderAccessor.getClassLoader()); + if (result != null) { + return result; + } + } + throw new UnavailableImplementationException(spi); + } + + private static T loadFirst(Class spi, ClassLoader classLoader) { + ServiceLoader serviceLoader = ServiceLoader.load(spi, classLoader); + if (serviceLoader.iterator().hasNext()) { + return serviceLoader.iterator().next(); + } + return null; + } + + private interface ClassLoaderAccessor { + ClassLoader getClassLoader(); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/lang/UnavailableImplementationException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/lang/UnavailableImplementationException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/impl/lang/UnavailableImplementationException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.impl.lang; + +/** + * Exception indicating that no implementation of an jjwt-api SPI was found on the classpath. + * @since 0.11.0 + */ +public final class UnavailableImplementationException extends RuntimeException { + + private static final String DEFAULT_NOT_FOUND_MESSAGE = "Unable to find an implementation for %s using java.util.ServiceLoader. Ensure you include a backing implementation .jar in the classpath, for example jjwt-impl.jar, or your own .jar for custom implementations."; + + UnavailableImplementationException(final Class klass) { + super(String.format(DEFAULT_NOT_FOUND_MESSAGE, klass)); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,680 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +import java.util.Arrays; + +/** + * A very fast and memory efficient class to encode and decode to and from BASE64 or BASE64URL in full accordance + * with RFC 4648. + * + *

Based initially on MigBase64 with continued modifications for Base64 URL support and JDK-standard code formatting.

+ * + *

This encode/decode algorithm doesn't create any temporary arrays as many other codecs do, it only + * allocates the resulting array. This produces less garbage and it is possible to handle arrays twice + * as large as algorithms that create a temporary array.

+ * + *

There is also a "fast" version of all decode methods that works the same way as the normal ones, but + * has a few demands on the decoded input. Normally though, these fast versions should be used if the source if + * the input is known and it hasn't bee tampered with.

+ * + * @author Mikael Grev + * @author Les Hazlewood + * @since 0.10.0 + */ +@SuppressWarnings("Duplicates") +final class Base64 { //final and package-protected on purpose + + private static final char[] BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); + private static final char[] BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray(); + private static final int[] BASE64_IALPHABET = new int[256]; + private static final int[] BASE64URL_IALPHABET = new int[256]; + private static final int IALPHABET_MAX_INDEX = BASE64_IALPHABET.length - 1; + + static { + Arrays.fill(BASE64_IALPHABET, -1); + System.arraycopy(BASE64_IALPHABET, 0, BASE64URL_IALPHABET, 0, BASE64_IALPHABET.length); + for (int i = 0, iS = BASE64_ALPHABET.length; i < iS; i++) { + BASE64_IALPHABET[BASE64_ALPHABET[i]] = i; + BASE64URL_IALPHABET[BASE64URL_ALPHABET[i]] = i; + } + BASE64_IALPHABET['='] = 0; + BASE64URL_IALPHABET['='] = 0; + } + + static final Base64 DEFAULT = new Base64(false); + static final Base64 URL_SAFE = new Base64(true); + + private final boolean urlsafe; + private final char[] ALPHABET; + private final int[] IALPHABET; + + private Base64(boolean urlsafe) { + this.urlsafe = urlsafe; + this.ALPHABET = urlsafe ? BASE64URL_ALPHABET : BASE64_ALPHABET; + this.IALPHABET = urlsafe ? BASE64URL_IALPHABET : BASE64_IALPHABET; + } + + // **************************************************************************************** + // * char[] version + // **************************************************************************************** + + private String getName() { + return urlsafe ? "base64url" : "base64"; // RFC 4648 codec names are all lowercase + } + + /** + * Encodes a raw byte array into a BASE64 char[] representation in accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + private char[] encodeToChar(byte[] sArr, boolean lineSep) { + + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) { + return new char[0]; + } + + int eLen = (sLen / 3) * 3; // # of bytes that can encode evenly into 24-bit chunks + int left = sLen - eLen; // # of bytes that remain after 24-bit chunking. Always 0, 1 or 2 + + int cCnt = (((sLen - 1) / 3 + 1) << 2); // # of base64-encoded characters including padding + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned char array with padding and any line separators + + int padCount = 0; + if (left == 2) { + padCount = 1; + } else if (left == 1) { + padCount = 2; + } + + char[] dArr = new char[urlsafe ? (dLen - padCount) : dLen]; + + // Encode even 24-bits + for (int s = 0, d = 0, cc = 0; s < eLen; ) { + + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = ALPHABET[(i >>> 18) & 0x3f]; + dArr[d++] = ALPHABET[(i >>> 12) & 0x3f]; + dArr[d++] = ALPHABET[(i >>> 6) & 0x3f]; + dArr[d++] = ALPHABET[i & 0x3f]; + + // Add optional line separator + if (lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't even 24 bits. + if (left > 0) { + // Prepare the int + int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = ALPHABET[i >> 12]; + dArr[dLen - 3] = ALPHABET[(i >>> 6) & 0x3f]; + //dArr[dLen - 2] = left == 2 ? ALPHABET[i & 0x3f] : '='; + //dArr[dLen - 1] = '='; + if (left == 2) { + dArr[dLen - 2] = ALPHABET[i & 0x3f]; + } else if (!urlsafe) { // if not urlsafe, we need to include the padding characters + dArr[dLen - 2] = '='; + } + if (!urlsafe) { // include padding + dArr[dLen - 1] = '='; + } + } + return dArr; + } + + /* + * Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * + * @param sArr The source array. null or length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + * + public final byte[] decode(char[] sArr) { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) { + return new byte[0]; + } + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) { // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IALPHABET[sArr[i]] < 0) { + sepCnt++; + } + } + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) { + return null; + } + + int pad = 0; + for (int i = sLen; i > 1 && IALPHABET[sArr[--i]] <= 0; ) { + if (sArr[i] == '=') { + pad++; + } + } + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IALPHABET[sArr[s++]]; + if (c >= 0) { + i |= c << (18 - j * 6); + } else { + j--; + } + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) { + dArr[d++] = (byte) i; + } + } + } + return dArr; + } + */ + + private int ctoi(char c) { + int i = c > IALPHABET_MAX_INDEX ? -1 : IALPHABET[c]; + if (i < 0) { + String msg = "Illegal " + getName() + " character: '" + c + "'"; + throw new DecodingException(msg); + } + return i; + } + + /** + * Decodes a BASE64 encoded char array that is known to be reasonably well formatted. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + * @throws DecodingException on illegal input + */ + final byte[] decodeFast(char[] sArr) throws DecodingException { + + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) { + return new byte[0]; + } + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IALPHABET[sArr[sIx]] < 0) { + sIx++; + } + + // Trim illegal chars from end + while (eIx > 0 && IALPHABET[sArr[eIx]] < 0) { + eIx--; + } + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + + // Assemble three bytes into an int from four "valid" characters. + int i = ctoi(sArr[sIx++]) << 18 | ctoi(sArr[sIx++]) << 12 | ctoi(sArr[sIx++]) << 6 | ctoi(sArr[sIx++]); + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) { + i |= ctoi(sArr[sIx++]) << (18 - j * 6); + } + + for (int r = 16; d < len; r -= 8) { + dArr[d++] = (byte) (i >> r); + } + } + + return dArr; + } + + // **************************************************************************************** + // * byte[] version + // **************************************************************************************** + + /* + * Encodes a raw byte array into a BASE64 byte[] representation i accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + * + public final byte[] encodeToByte(byte[] sArr, boolean lineSep) { + return encodeToByte(sArr, 0, sArr != null ? sArr.length : 0, lineSep); + } + + /** + * Encodes a raw byte array into a BASE64 byte[] representation i accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null an empty array will be returned. + * @param sOff The starting position in the bytes to convert. + * @param sLen The number of bytes to convert. If 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + * + public final byte[] encodeToByte(byte[] sArr, int sOff, int sLen, boolean lineSep) { + + // Check special case + if (sArr == null || sLen == 0) { + return new byte[0]; + } + + int eLen = (sLen / 3) * 3; // Length of even 24-bits. + int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array + byte[] dArr = new byte[dLen]; + + // Encode even 24-bits + for (int s = sOff, d = 0, cc = 0; s < sOff + eLen; ) { + + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = (byte) ALPHABET[(i >>> 18) & 0x3f]; + dArr[d++] = (byte) ALPHABET[(i >>> 12) & 0x3f]; + dArr[d++] = (byte) ALPHABET[(i >>> 6) & 0x3f]; + dArr[d++] = (byte) ALPHABET[i & 0x3f]; + + // Add optional line separator + if (lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't an even 24 bits. + int left = sLen - eLen; // 0 - 2. + if (left > 0) { + // Prepare the int + int i = ((sArr[sOff + eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sOff + sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = (byte) ALPHABET[i >> 12]; + dArr[dLen - 3] = (byte) ALPHABET[(i >>> 6) & 0x3f]; + dArr[dLen - 2] = left == 2 ? (byte) ALPHABET[i & 0x3f] : (byte) '='; + dArr[dLen - 1] = '='; + } + return dArr; + } + + /** + * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + * + public final byte[] decode(byte[] sArr) { + return decode(sArr, 0, sArr.length); + } + + /** + * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * + * @param sArr The source array. null will throw an exception. + * @param sOff The starting position in the source array. + * @param sLen The number of bytes to decode from the source array. Length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + * + public final byte[] decode(byte[] sArr, int sOff, int sLen) { + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) { // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IALPHABET[sArr[sOff + i] & 0xff] < 0) { + sepCnt++; + } + } + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) { + return null; + } + + int pad = 0; + for (int i = sLen; i > 1 && IALPHABET[sArr[sOff + --i] & 0xff] <= 0; ) { + if (sArr[sOff + i] == '=') { + pad++; + } + } + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IALPHABET[sArr[sOff + s++] & 0xff]; + if (c >= 0) { + i |= c << (18 - j * 6); + } else { + j--; + } + } + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) { + dArr[d++] = (byte) i; + } + } + } + + return dArr; + } + + + /* + * Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as + * fast as {@link #decode(byte[])}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + * + public final byte[] decodeFast(byte[] sArr) { + + // Check special case + int sLen = sArr.length; + if (sLen == 0) { + return new byte[0]; + } + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IALPHABET[sArr[sIx] & 0xff] < 0) { + sIx++; + } + + // Trim illegal chars from end + while (eIx > 0 && IALPHABET[sArr[eIx] & 0xff] < 0) { + eIx--; + } + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + + // Assemble three bytes into an int from four "valid" characters. + int i = IALPHABET[sArr[sIx++]] << 18 | IALPHABET[sArr[sIx++]] << 12 | IALPHABET[sArr[sIx++]] << 6 | IALPHABET[sArr[sIx++]]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) { + i |= IALPHABET[sArr[sIx++]] << (18 - j * 6); + } + + for (int r = 16; d < len; r -= 8) { + dArr[d++] = (byte) (i >> r); + } + } + + return dArr; + } + */ + + // **************************************************************************************** + // * String version + // **************************************************************************************** + + /** + * Encodes a raw byte array into a BASE64 String representation i accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + final String encodeToString(byte[] sArr, boolean lineSep) { + // Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower. + return new String(encodeToChar(sArr, lineSep)); + } + + /* + * Decodes a BASE64 encoded String. All illegal characters will be ignored and can handle both strings with + * and without line separators.
+ * Note! It can be up to about 2x the speed to call decode(str.toCharArray()) instead. That + * will create a temporary array though. This version will use str.charAt(i) to iterate the string. + * + * @param str The source string. null or length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + * + public final byte[] decode(String str) { + + // Check special case + int sLen = str != null ? str.length() : 0; + if (sLen == 0) { + return new byte[0]; + } + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) { // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IALPHABET[str.charAt(i)] < 0) { + sepCnt++; + } + } + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) { + return null; + } + + // Count '=' at end + int pad = 0; + for (int i = sLen; i > 1 && IALPHABET[str.charAt(--i)] <= 0; ) { + if (str.charAt(i) == '=') { + pad++; + } + } + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IALPHABET[str.charAt(s++)]; + if (c >= 0) { + i |= c << (18 - j * 6); + } else { + j--; + } + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) { + dArr[d++] = (byte) i; + } + } + } + return dArr; + } + + /** + * Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as + * fast as {@link #decode(String)}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param s The source string. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + * + public final byte[] decodeFast(String s) { + + // Check special case + int sLen = s.length(); + if (sLen == 0) { + return new byte[0]; + } + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IALPHABET[s.charAt(sIx) & 0xff] < 0) { + sIx++; + } + + // Trim illegal chars from end + while (eIx > 0 && IALPHABET[s.charAt(eIx) & 0xff] < 0) { + eIx--; + } + + // get the padding count (=) (0, 1 or 2) + int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = IALPHABET[s.charAt(sIx++)] << 18 | IALPHABET[s.charAt(sIx++)] << 12 | IALPHABET[s.charAt(sIx++)] << 6 | IALPHABET[s.charAt(sIx++)]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) { + i |= IALPHABET[s.charAt(sIx++)] << (18 - j * 6); + } + + for (int r = 16; d < len; r -= 8) { + dArr[d++] = (byte) (i >> r); + } + } + + return dArr; + } + */ +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64Decoder.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64Decoder.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64Decoder.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +import io.jsonwebtoken.lang.Assert; + +/** + * @since 0.10.0 + */ +class Base64Decoder extends Base64Support implements Decoder { + + Base64Decoder() { + super(Base64.DEFAULT); + } + + Base64Decoder(Base64 base64) { + super(base64); + } + + @Override + public byte[] decode(String s) throws DecodingException { + Assert.notNull(s, "String argument cannot be null"); + return this.base64.decodeFast(s.toCharArray()); + } +} \ No newline at end of file Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64Encoder.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64Encoder.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64Encoder.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +import io.jsonwebtoken.lang.Assert; + +/** + * @since 0.10.0 + */ +class Base64Encoder extends Base64Support implements Encoder { + + Base64Encoder() { + super(Base64.DEFAULT); + } + + Base64Encoder(Base64 base64) { + super(base64); + } + + @Override + public String encode(byte[] bytes) throws EncodingException { + Assert.notNull(bytes, "byte array argument cannot be null"); + return this.base64.encodeToString(bytes, false); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64Support.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64Support.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64Support.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +import io.jsonwebtoken.lang.Assert; + +/** + * @since 0.10.0 + */ +class Base64Support { + + protected final Base64 base64; + + Base64Support(Base64 base64) { + Assert.notNull(base64, "Base64 argument cannot be null"); + this.base64 = base64; + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64UrlDecoder.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64UrlDecoder.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64UrlDecoder.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +class Base64UrlDecoder extends Base64Decoder { + + Base64UrlDecoder() { + super(Base64.URL_SAFE); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64UrlEncoder.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64UrlEncoder.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Base64UrlEncoder.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +class Base64UrlEncoder extends Base64Encoder { + + Base64UrlEncoder() { + super(Base64.URL_SAFE); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/CodecException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/CodecException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/CodecException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public class CodecException extends IOException { + + public CodecException(String message) { + super(message); + } + + public CodecException(String message, Throwable cause) { + super(message, cause); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Decoder.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Decoder.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Decoder.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public interface Decoder { + + R decode(T t) throws DecodingException; +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Decoders.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Decoders.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Decoders.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public final class Decoders { + + public static final Decoder BASE64 = new ExceptionPropagatingDecoder<>(new Base64Decoder()); + public static final Decoder BASE64URL = new ExceptionPropagatingDecoder<>(new Base64UrlDecoder()); + + private Decoders() { //prevent instantiation + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/DecodingException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/DecodingException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/DecodingException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public class DecodingException extends CodecException { + + public DecodingException(String message) { + super(message); + } + + public DecodingException(String message, Throwable cause) { + super(message, cause); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/DeserializationException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/DeserializationException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/DeserializationException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public class DeserializationException extends SerialException { + + public DeserializationException(String msg) { + super(msg); + } + + public DeserializationException(String message, Throwable cause) { + super(message, cause); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Deserializer.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Deserializer.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Deserializer.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public interface Deserializer { + + T deserialize(byte[] bytes) throws DeserializationException; +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Encoder.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Encoder.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Encoder.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public interface Encoder { + + R encode(T t) throws EncodingException; +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Encoders.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Encoders.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Encoders.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public final class Encoders { + + public static final Encoder BASE64 = new ExceptionPropagatingEncoder<>(new Base64Encoder()); + public static final Encoder BASE64URL = new ExceptionPropagatingEncoder<>(new Base64UrlEncoder()); + + private Encoders() { //prevent instantiation + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/EncodingException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/EncodingException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/EncodingException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public class EncodingException extends CodecException { + + public EncodingException(String message, Throwable cause) { + super(message, cause); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/ExceptionPropagatingDecoder.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/ExceptionPropagatingDecoder.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/ExceptionPropagatingDecoder.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +import io.jsonwebtoken.lang.Assert; + +/** + * @since 0.10.0 + */ +class ExceptionPropagatingDecoder implements Decoder { + + private final Decoder decoder; + + ExceptionPropagatingDecoder(Decoder decoder) { + Assert.notNull(decoder, "Decoder cannot be null."); + this.decoder = decoder; + } + + @Override + public R decode(T t) throws DecodingException { + Assert.notNull(t, "Decode argument cannot be null."); + try { + return decoder.decode(t); + } catch (DecodingException e) { + throw e; //propagate + } catch (Exception e) { + String msg = "Unable to decode input: " + e.getMessage(); + throw new DecodingException(msg, e); + } + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/ExceptionPropagatingEncoder.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/ExceptionPropagatingEncoder.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/ExceptionPropagatingEncoder.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +import io.jsonwebtoken.lang.Assert; + +/** + * @since 0.10.0 + */ +class ExceptionPropagatingEncoder implements Encoder { + + private final Encoder encoder; + + ExceptionPropagatingEncoder(Encoder encoder) { + Assert.notNull(encoder, "Encoder cannot be null."); + this.encoder = encoder; + } + + @Override + public R encode(T t) throws EncodingException { + Assert.notNull(t, "Encode argument cannot be null."); + try { + return this.encoder.encode(t); + } catch (EncodingException e) { + throw e; //propagate + } catch (Exception e) { + String msg = "Unable to encode input: " + e.getMessage(); + throw new EncodingException(msg, e); + } + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/IOException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/IOException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/IOException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +import io.jsonwebtoken.JwtException; + +/** + * @since 0.10.0 + */ +public class IOException extends JwtException { + + public IOException(String msg) { + super(msg); + } + + public IOException(String message, Throwable cause) { + super(message, cause); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/SerialException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/SerialException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/SerialException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public class SerialException extends IOException { + + public SerialException(String msg) { + super(msg); + } + + public SerialException(String message, Throwable cause) { + super(message, cause); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/SerializationException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/SerializationException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/SerializationException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public class SerializationException extends SerialException { + + public SerializationException(String msg) { + super(msg); + } + + public SerializationException(String message, Throwable cause) { + super(message, cause); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Serializer.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Serializer.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/io/Serializer.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.io; + +/** + * @since 0.10.0 + */ +public interface Serializer { + + byte[] serialize(T t) throws SerializationException; + +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/jackson/io/JacksonDeserializer.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/jackson/io/JacksonDeserializer.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/jackson/io/JacksonDeserializer.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.jackson.io; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.ObjectMapper; + +import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import io.jsonwebtoken.io.DeserializationException; +import io.jsonwebtoken.io.Deserializer; +import io.jsonwebtoken.lang.Assert; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; + +/** + * @since 0.10.0 + */ +public class JacksonDeserializer implements Deserializer { + + private final Class returnType; + private final ObjectMapper objectMapper; + + @SuppressWarnings("unused") //used via reflection by RuntimeClasspathDeserializerLocator + public JacksonDeserializer() { + this(JacksonSerializer.DEFAULT_OBJECT_MAPPER); + } + + /** + * Creates a new JacksonDeserializer where the values of the claims can be parsed into given types. A common usage + * example is to parse custom User object out of a claim, for example the claims: + *
{@code
+     * {
+     *     "issuer": "https://issuer.example.com",
+     *     "user": {
+     *         "firstName": "Jill",
+     *         "lastName": "Coder"
+     *     }
+     * }}
+ * Passing a map of {@code ["user": User.class]} to this constructor would result in the {@code user} claim being + * transformed to an instance of your custom {@code User} class, instead of the default of {@code Map}. + *

+ * Because custom type parsing requires modifying the state of a Jackson {@code ObjectMapper}, this + * constructor creates a new internal {@code ObjectMapper} instance and customizes it to support the + * specified {@code claimTypeMap}. This ensures that the JJWT parsing behavior does not unexpectedly + * modify the state of another application-specific {@code ObjectMapper}. + *

+ * If you would like to use your own {@code ObjectMapper} instance that also supports custom types for + * JWT {@code Claims}, you will need to first customize your {@code ObjectMapper} instance by registering + * your custom types and then use the {@link #JacksonDeserializer(ObjectMapper)} constructor instead. + * + * @param claimTypeMap The claim name-to-class map used to deserialize claims into the given type + */ + public JacksonDeserializer(Map claimTypeMap) { + // DO NOT reuse JacksonSerializer.DEFAULT_OBJECT_MAPPER as this could result in sharing the custom deserializer + // between instances + this(new ObjectMapper()); + Assert.notNull(claimTypeMap, "Claim type map cannot be null."); + // register a new Deserializer + SimpleModule module = new SimpleModule(); + module.addDeserializer(Object.class, new MappedTypeDeserializer(Collections.unmodifiableMap(claimTypeMap))); + objectMapper.registerModule(module); + } + + @SuppressWarnings({"unchecked", "WeakerAccess", "unused"}) // for end-users providing a custom ObjectMapper + public JacksonDeserializer(ObjectMapper objectMapper) { + this(objectMapper, (Class) Object.class); + } + + private JacksonDeserializer(ObjectMapper objectMapper, Class returnType) { + Assert.notNull(objectMapper, "ObjectMapper cannot be null."); + Assert.notNull(returnType, "Return type cannot be null."); + this.objectMapper = objectMapper; + this.returnType = returnType; + } + + @Override + public T deserialize(byte[] bytes) throws DeserializationException { + try { + return readValue(bytes); + } catch (IOException e) { + String msg = "Unable to deserialize bytes into a " + returnType.getName() + " instance: " + e.getMessage(); + throw new DeserializationException(msg, e); + } + } + + protected T readValue(byte[] bytes) throws IOException { + return objectMapper.readValue(bytes, returnType); + } + + /** + * A Jackson {@link com.fasterxml.jackson.databind.JsonDeserializer JsonDeserializer}, that will convert claim + * values to types based on {@code claimTypeMap}. + */ + private static class MappedTypeDeserializer extends UntypedObjectDeserializer { + + private final Map claimTypeMap; + + private MappedTypeDeserializer(Map claimTypeMap) { + super(null, null); + this.claimTypeMap = claimTypeMap; + } + + @Override + public Object deserialize(JsonParser parser, DeserializationContext context) throws IOException { + // check if the current claim key is mapped, if so traverse it's value + String name = parser.currentName(); + if (claimTypeMap != null && name != null && claimTypeMap.containsKey(name)) { + Class type = claimTypeMap.get(name); + return parser.readValueAsTree().traverse(parser.getCodec()).readValueAs(type); + } + // otherwise default to super + return super.deserialize(parser, context); + } + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/jackson/io/JacksonSerializer.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/jackson/io/JacksonSerializer.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/jackson/io/JacksonSerializer.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.jackson.io; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.jsonwebtoken.io.SerializationException; +import io.jsonwebtoken.io.Serializer; +import io.jsonwebtoken.lang.Assert; + +/** + * @since 0.10.0 + */ +public class JacksonSerializer implements Serializer { + + static final ObjectMapper DEFAULT_OBJECT_MAPPER = new ObjectMapper(); + + private final ObjectMapper objectMapper; + + @SuppressWarnings("unused") //used via reflection by RuntimeClasspathDeserializerLocator + public JacksonSerializer() { + this(DEFAULT_OBJECT_MAPPER); + } + + @SuppressWarnings("WeakerAccess") //intended for end-users to use when providing a custom ObjectMapper + public JacksonSerializer(ObjectMapper objectMapper) { + Assert.notNull(objectMapper, "ObjectMapper cannot be null."); + this.objectMapper = objectMapper; + } + + @Override + public byte[] serialize(T t) throws SerializationException { + Assert.notNull(t, "Object to serialize cannot be null."); + try { + return writeValueAsBytes(t); + } catch (JsonProcessingException e) { + String msg = "Unable to serialize object: " + e.getMessage(); + throw new SerializationException(msg, e); + } + } + + @SuppressWarnings("WeakerAccess") //for testing + protected byte[] writeValueAsBytes(T t) throws JsonProcessingException { + return this.objectMapper.writeValueAsBytes(t); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Arrays.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Arrays.java (.../Arrays.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Arrays.java (.../Arrays.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -1,15 +1,27 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jsonwebtoken.lang; /** * @since 0.6 */ public final class Arrays { - //for code coverage - private static final Arrays INSTANCE = new Arrays(); + private Arrays(){} //prevent instantiation - private Arrays(){} - public static int length(byte[] bytes) { return bytes != null ? bytes.length : 0; } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Assert.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Assert.java (.../Assert.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Assert.java (.../Assert.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -20,11 +20,8 @@ public final class Assert { - //for code coverage - private static final Assert INSTANCE = new Assert(); + private Assert(){} //prevent instantiation - private Assert(){} - /** * Assert a boolean expression, throwing IllegalArgumentException * if the test result is false. Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Classes.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Classes.java (.../Classes.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Classes.java (.../Classes.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -17,16 +17,15 @@ import java.io.InputStream; import java.lang.reflect.Constructor; +import java.lang.reflect.Method; /** * @since 0.1 */ public final class Classes { - private static final Classes INSTANCE = new Classes(); + private Classes() {} //prevent instantiation - private Classes() {} - /** * @since 0.1 */ @@ -69,7 +68,8 @@ * @return the located class * @throws UnknownClassException if the class cannot be found. */ - public static Class forName(String fqcn) throws UnknownClassException { + @SuppressWarnings("unchecked") + public static Class forName(String fqcn) throws UnknownClassException { Class clazz = THREAD_CL_ACCESSOR.loadClass(fqcn); @@ -85,8 +85,8 @@ String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " + "system/application ClassLoaders. All heuristics have been exhausted. Class could not be found."; - if (fqcn != null && fqcn.startsWith("com.stormpath.sdk.impl")) { - msg += " Have you remembered to include the stormpath-sdk-impl .jar in your runtime classpath?"; + if (fqcn != null && fqcn.startsWith("io.jsonwebtoken.impl")) { + msg += " Have you remembered to include the jjwt-impl.jar in your runtime classpath?"; } throw new UnknownClassException(msg); @@ -132,13 +132,19 @@ } @SuppressWarnings("unchecked") - public static Object newInstance(String fqcn) { - return newInstance(forName(fqcn)); + public static T newInstance(String fqcn) { + return (T)newInstance(forName(fqcn)); } + public static T newInstance(String fqcn, Class[] ctorArgTypes, Object... args) { + Class clazz = forName(fqcn); + Constructor ctor = getConstructor(clazz, ctorArgTypes); + return instantiate(ctor, args); + } + @SuppressWarnings("unchecked") - public static Object newInstance(String fqcn, Object... args) { - return newInstance(forName(fqcn), args); + public static T newInstance(String fqcn, Object... args) { + return (T)newInstance(forName(fqcn), args); } public static T newInstance(Class clazz) { @@ -181,6 +187,23 @@ } /** + * @since 0.10.0 + */ + @SuppressWarnings("unchecked") + public static T invokeStatic(String fqcn, String methodName, Class[] argTypes, Object... args) { + try { + Class clazz = Classes.forName(fqcn); + Method method = clazz.getDeclaredMethod(methodName, argTypes); + method.setAccessible(true); + return(T)method.invoke(null, args); + } catch (Exception e) { + String msg = "Unable to invoke class method " + fqcn + "#" + methodName + ". Ensure the necessary " + + "implementation is in the runtime classpath."; + throw new IllegalStateException(msg, e); + } + } + + /** * @since 1.0 */ private static interface ClassLoaderAccessor { Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Collections.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Collections.java (.../Collections.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Collections.java (.../Collections.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -26,11 +26,8 @@ public final class Collections { - //for code coverage - private static final Collections INSTANCE = new Collections(); + private Collections(){} //prevent instantiation - private Collections(){} - /** * Return true if the supplied Collection is null * or empty. Otherwise, return false. Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/DateFormats.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/DateFormats.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/DateFormats.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.lang; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +/** + * @since 0.10.0 + */ +public class DateFormats { + + private static final String ISO_8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + + private static final String ISO_8601_MILLIS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + + private static final ThreadLocal ISO_8601 = new ThreadLocal() { + @Override + protected DateFormat initialValue() { + SimpleDateFormat format = new SimpleDateFormat(ISO_8601_PATTERN); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format; + } + }; + + private static final ThreadLocal ISO_8601_MILLIS = new ThreadLocal() { + @Override + protected DateFormat initialValue() { + SimpleDateFormat format = new SimpleDateFormat(ISO_8601_MILLIS_PATTERN); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format; + } + }; + + public static String formatIso8601(Date date) { + return formatIso8601(date, true); + } + + public static String formatIso8601(Date date, boolean includeMillis) { + if (includeMillis) { + return ISO_8601_MILLIS.get().format(date); + } + return ISO_8601.get().format(date); + } + + public static Date parseIso8601Date(String s) throws ParseException { + Assert.notNull(s, "String argument cannot be null."); + if (s.lastIndexOf('.') > -1) { //assume ISO-8601 with milliseconds + return ISO_8601_MILLIS.get().parse(s); + } else { //assume ISO-8601 without millis: + return ISO_8601.get().parse(s); + } + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Maps.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Maps.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Maps.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2019 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.lang; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Utility class to help with the manipulation of working with Maps. + * @since 0.11.0 + */ +public final class Maps { + + private Maps() {} //prevent instantiation + + /** + * Creates a new map builder with a single entry. + *

Typical usage:

{@code
+     * Map result = Maps.of("key1", value1)
+     *     .and("key2", value2)
+     *     // ...
+     *     .build();
+     * }
+ * @param key the key of an map entry to be added + * @param value the value of map entry to be added + * @param the maps key type + * @param the maps value type + * Creates a new map builder with a single entry. + */ + public static MapBuilder of(K key, V value) { + return new HashMapBuilder().and(key, value); + } + + /** + * Utility Builder class for fluently building maps: + *

Typical usage:

{@code
+     * Map result = Maps.of("key1", value1)
+     *     .and("key2", value2)
+     *     // ...
+     *     .build();
+     * }
+ * @param the maps key type + * @param the maps value type + */ + public interface MapBuilder { + /** + * Add a new entry to this map builder + * @param key the key of an map entry to be added + * @param value the value of map entry to be added + * @return the current MapBuilder to allow for method chaining. + */ + MapBuilder and(K key, V value); + + /** + * Returns a the resulting Map object from this MapBuilder. + * @return Returns a the resulting Map object from this MapBuilder. + */ + Map build(); + } + + private static class HashMapBuilder implements MapBuilder { + + private final Map data = new HashMap<>(); + + public MapBuilder and(K key, V value) { + data.put(key, value); + return this; + } + public Map build() { + return Collections.unmodifiableMap(data); + } + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Objects.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Objects.java (.../Objects.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Objects.java (.../Objects.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -22,11 +22,8 @@ public final class Objects { - //for code coverage - private static final Objects INSTANCE = new Objects(); + private Objects(){} //prevent instantiation - private Objects(){} - private static final int INITIAL_HASH = 7; private static final int MULTIPLIER = 31; Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/RuntimeEnvironment.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/RuntimeEnvironment.java (.../RuntimeEnvironment.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/RuntimeEnvironment.java (.../RuntimeEnvironment.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -21,10 +21,8 @@ public final class RuntimeEnvironment { - private static final RuntimeEnvironment INSTANCE = new RuntimeEnvironment(); + private RuntimeEnvironment(){} //prevent instantiation - private RuntimeEnvironment(){} - private static final String BC_PROVIDER_CLASS_NAME = "org.bouncycastle.jce.provider.BouncyCastleProvider"; private static final AtomicBoolean bcLoaded = new AtomicBoolean(false); @@ -33,7 +31,7 @@ public static void enableBouncyCastleIfPossible() { - if (bcLoaded.get()) { + if (!BOUNCY_CASTLE_AVAILABLE || bcLoaded.get()) { return; } Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Strings.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Strings.java (.../Strings.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/Strings.java (.../Strings.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -31,8 +31,6 @@ public final class Strings { - private static final Strings INSTANCE = new Strings(); //for code coverage - private static final String FOLDER_SEPARATOR = "/"; private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; @@ -45,7 +43,7 @@ public static final Charset UTF_8 = Charset.forName("UTF-8"); - private Strings(){} + private Strings(){} //prevent instantiation //--------------------------------------------------------------------- // General convenience methods for working with Strings Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/UnknownClassException.java =================================================================== diff -u -rdd64f16fdf89f789b8c2179d421290dcabf15835 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/UnknownClassException.java (.../UnknownClassException.java) (revision dd64f16fdf89f789b8c2179d421290dcabf15835) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/lang/UnknownClassException.java (.../UnknownClassException.java) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -48,16 +48,17 @@ public UnknownClassException(Throwable cause) { super(cause); } + */ /** * Constructs a new UnknownClassException. * * @param message the reason for the exception * @param cause the underlying Throwable that caused this exception to be thrown. - * + */ public UnknownClassException(String message, Throwable cause) { + // TODO: remove in v1.0, this constructor is only exposed to allow for backward compatible behavior super(message, cause); } - */ } \ No newline at end of file Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/InvalidKeyException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/InvalidKeyException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/InvalidKeyException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.security; + +/** + * @since 0.10.0 + */ +public class InvalidKeyException extends KeyException { + + public InvalidKeyException(String message) { + super(message); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/KeyException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/KeyException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/KeyException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.security; + +/** + * @since 0.10.0 + */ +public class KeyException extends SecurityException { + + public KeyException(String message) { + super(message); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/Keys.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/Keys.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/Keys.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.security; + +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.lang.Assert; +import io.jsonwebtoken.lang.Classes; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.KeyPair; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Utility class for securely generating {@link SecretKey}s and {@link KeyPair}s. + * + * @since 0.10.0 + */ +public final class Keys { + + private static final String MAC = "io.jsonwebtoken.impl.crypto.MacProvider"; + private static final String RSA = "io.jsonwebtoken.impl.crypto.RsaProvider"; + private static final String EC = "io.jsonwebtoken.impl.crypto.EllipticCurveProvider"; + + private static final Class[] SIG_ARG_TYPES = new Class[]{SignatureAlgorithm.class}; + + //purposefully ordered higher to lower: + private static final List PREFERRED_HMAC_ALGS = Collections.unmodifiableList(Arrays.asList( + SignatureAlgorithm.HS512, SignatureAlgorithm.HS384, SignatureAlgorithm.HS256)); + + //prevent instantiation + private Keys() { + } + + /* + public static final int bitLength(Key key) throws IllegalArgumentException { + Assert.notNull(key, "Key cannot be null."); + if (key instanceof SecretKey) { + byte[] encoded = key.getEncoded(); + return Arrays.length(encoded) * 8; + } else if (key instanceof RSAKey) { + return ((RSAKey)key).getModulus().bitLength(); + } else if (key instanceof ECKey) { + return ((ECKey)key).getParams().getOrder().bitLength(); + } + + throw new IllegalArgumentException("Unsupported key type: " + key.getClass().getName()); + } + */ + + /** + * Creates a new SecretKey instance for use with HMAC-SHA algorithms based on the specified key byte array. + * + * @param bytes the key byte array + * @return a new SecretKey instance for use with HMAC-SHA algorithms based on the specified key byte array. + * @throws WeakKeyException if the key byte array length is less than 256 bits (32 bytes) as mandated by the + * JWT JWA Specification + * (RFC 7518, Section 3.2) + */ + public static SecretKey hmacShaKeyFor(byte[] bytes) throws WeakKeyException { + + if (bytes == null) { + throw new InvalidKeyException("SecretKey byte array cannot be null."); + } + + int bitLength = bytes.length * 8; + + for (SignatureAlgorithm alg : PREFERRED_HMAC_ALGS) { + if (bitLength >= alg.getMinKeyLength()) { + return new SecretKeySpec(bytes, alg.getJcaName()); + } + } + + String msg = "The specified key byte array is " + bitLength + " bits which " + + "is not secure enough for any JWT HMAC-SHA algorithm. The JWT " + + "JWA Specification (RFC 7518, Section 3.2) states that keys used with HMAC-SHA algorithms MUST have a " + + "size >= 256 bits (the key size must be greater than or equal to the hash " + + "output size). Consider using the " + Keys.class.getName() + "#secretKeyFor(SignatureAlgorithm) method " + + "to create a key guaranteed to be secure enough for your preferred HMAC-SHA algorithm. See " + + "https://tools.ietf.org/html/rfc7518#section-3.2 for more information."; + throw new WeakKeyException(msg); + } + + /** + * Returns a new {@link SecretKey} with a key length suitable for use with the specified {@link SignatureAlgorithm}. + * + *

JWA Specification (RFC 7518), Section 3.2 + * requires minimum key lengths to be used for each respective Signature Algorithm. This method returns a + * secure-random generated SecretKey that adheres to the required minimum key length. The lengths are:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AlgorithmKey Length
HS256256 bits (32 bytes)
HS384384 bits (48 bytes)
HS512512 bits (64 bytes)
+ * + * @param alg the {@code SignatureAlgorithm} to inspect to determine which key length to use. + * @return a new {@link SecretKey} instance suitable for use with the specified {@link SignatureAlgorithm}. + * @throws IllegalArgumentException for any input value other than {@link SignatureAlgorithm#HS256}, + * {@link SignatureAlgorithm#HS384}, or {@link SignatureAlgorithm#HS512} + */ + public static SecretKey secretKeyFor(SignatureAlgorithm alg) throws IllegalArgumentException { + Assert.notNull(alg, "SignatureAlgorithm cannot be null."); + switch (alg) { + case HS256: + case HS384: + case HS512: + return Classes.invokeStatic(MAC, "generateKey", SIG_ARG_TYPES, alg); + default: + String msg = "The " + alg.name() + " algorithm does not support shared secret keys."; + throw new IllegalArgumentException(msg); + } + } + + /** + * Returns a new {@link KeyPair} suitable for use with the specified asymmetric algorithm. + * + *

If the {@code alg} argument is an RSA algorithm, a KeyPair is generated based on the following:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
JWA AlgorithmKey Size
RS2562048 bits
PS2562048 bits
RS3843072 bits
PS3843072 bits
RS5124096 bits
PS5124096 bits
+ * + *

If the {@code alg} argument is an Elliptic Curve algorithm, a KeyPair is generated based on the following:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
JWA AlgorithmKey SizeJWA Curve NameASN1 OID Curve Name
EC256256 bits{@code P-256}{@code secp256r1}
EC384384 bits{@code P-384}{@code secp384r1}
EC512512 bits{@code P-521}{@code secp521r1}
+ * + * @param alg the {@code SignatureAlgorithm} to inspect to determine which asymmetric algorithm to use. + * @return a new {@link KeyPair} suitable for use with the specified asymmetric algorithm. + * @throws IllegalArgumentException if {@code alg} is not an asymmetric algorithm + */ + public static KeyPair keyPairFor(SignatureAlgorithm alg) throws IllegalArgumentException { + Assert.notNull(alg, "SignatureAlgorithm cannot be null."); + switch (alg) { + case RS256: + case PS256: + case RS384: + case PS384: + case RS512: + case PS512: + return Classes.invokeStatic(RSA, "generateKeyPair", SIG_ARG_TYPES, alg); + case ES256: + case ES384: + case ES512: + return Classes.invokeStatic(EC, "generateKeyPair", SIG_ARG_TYPES, alg); + default: + String msg = "The " + alg.name() + " algorithm does not support Key Pairs."; + throw new IllegalArgumentException(msg); + } + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/SecurityException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/SecurityException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/SecurityException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.security; + +import io.jsonwebtoken.JwtException; + +/** + * @since 0.10.0 + */ +public class SecurityException extends JwtException { + + public SecurityException(String message) { + super(message); + } + + public SecurityException(String message, Throwable cause) { + super(message, cause); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/SignatureException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/SignatureException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/SignatureException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.security; + +/** + * @since 0.10.0 + */ +public class SignatureException extends io.jsonwebtoken.SignatureException { + + public SignatureException(String message) { + super(message); + } + + public SignatureException(String message, Throwable cause) { + super(message, cause); + } +} Index: 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/WeakKeyException.java =================================================================== diff -u --- 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/WeakKeyException.java (revision 0) +++ 3rdParty_sources/jsonwebtoken/io/jsonwebtoken/security/WeakKeyException.java (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken.security; + +/** + * @since 0.10.0 + */ +public class WeakKeyException extends InvalidKeyException { + + public WeakKeyException(String message) { + super(message); + } +} Index: 3rdParty_sources/versions.txt =================================================================== diff -u -r22d20e45f4a7cbb2a9477c66f75e2a162b0d3f06 -r0761df64c22d4bca1649836fda23e4526282498b --- 3rdParty_sources/versions.txt (.../versions.txt) (revision 22d20e45f4a7cbb2a9477c66f75e2a162b0d3f06) +++ 3rdParty_sources/versions.txt (.../versions.txt) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -39,7 +39,7 @@ jLaTexMath 1.0.6 -jsonwebtoken 0.9.0 +jsonwebtoken 0.11.2 JSP API 2.3 1.0.3 Index: lams_build/3rdParty.userlibraries =================================================================== diff -u -r8ba50cf0cbd248f016f5827b4dfd2d8a16b3d80e -r0761df64c22d4bca1649836fda23e4526282498b --- lams_build/3rdParty.userlibraries (.../3rdParty.userlibraries) (revision 8ba50cf0cbd248f016f5827b4dfd2d8a16b3d80e) +++ lams_build/3rdParty.userlibraries (.../3rdParty.userlibraries) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -23,7 +23,6 @@ - @@ -45,5 +44,8 @@ + + + Index: lams_build/lib/json/jjwt-0.9.0.jar =================================================================== diff -u -r33d7aa31deb6606940bcd51544b17d62387777cf -r0761df64c22d4bca1649836fda23e4526282498b Binary files differ Index: lams_build/lib/json/jjwt-api-0.11.2.jar =================================================================== diff -u Binary files differ Index: lams_build/lib/json/jjwt-impl-0.11.2.jar =================================================================== diff -u Binary files differ Index: lams_build/lib/json/jjwt-jackson-0.11.2.jar =================================================================== diff -u Binary files differ Index: lams_build/lib/json/jjwt.module.xml =================================================================== diff -u -r33d7aa31deb6606940bcd51544b17d62387777cf -r0761df64c22d4bca1649836fda23e4526282498b --- lams_build/lib/json/jjwt.module.xml (.../jjwt.module.xml) (revision 33d7aa31deb6606940bcd51544b17d62387777cf) +++ lams_build/lib/json/jjwt.module.xml (.../jjwt.module.xml) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -24,7 +24,9 @@ - + + + Index: lams_build/liblist.txt =================================================================== diff -u -r8ba50cf0cbd248f016f5827b4dfd2d8a16b3d80e -r0761df64c22d4bca1649836fda23e4526282498b --- lams_build/liblist.txt (.../liblist.txt) (revision 8ba50cf0cbd248f016f5827b4dfd2d8a16b3d80e) +++ lams_build/liblist.txt (.../liblist.txt) (revision 0761df64c22d4bca1649836fda23e4526282498b) @@ -48,7 +48,9 @@ json-simple json-simple-1.1.1.jar 1.1.1 Apache License 2.0 fangyidong A simple Java toolkit for JSON -jsonwebtoken jjwt-0.9.0.jar 0.9.0 JWTK Apache +jsonwebtoken jjwt-api-0.11.2.jar 0.11.2 JWTK Apache + jjwt-impl-0.11.2.jar + jjwt-jackson-0.11.2.jar kotlin-stdlib kotlin-stdlib-1.3.11.jar 1.3.11 Apache License 2.0 JetBrains Kotlin runtime