diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index a99f0fa0..d72fd5c2 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -3,13 +3,19 @@ import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTCreationException; import com.auth0.jwt.exceptions.SignatureGenerationException; -import com.auth0.jwt.impl.*; +import com.auth0.jwt.impl.HeaderClaimsHolder; +import com.auth0.jwt.impl.HeaderSerializer; +import com.auth0.jwt.impl.PayloadClaimsHolder; +import com.auth0.jwt.impl.PayloadSerializer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import java.nio.charset.StandardCharsets; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.Security; import java.time.Instant; import java.util.*; import java.util.Map.Entry; @@ -23,10 +29,6 @@ @SuppressWarnings("WeakerAccess") public final class JWTCreator { - private final Algorithm algorithm; - private final String headerJson; - private final String payloadJson; - private static final ObjectMapper mapper; private static final SimpleModule module; @@ -39,6 +41,10 @@ public final class JWTCreator { mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); } + private final Algorithm algorithm; + private final String headerJson; + private final String payloadJson; + private JWTCreator(Algorithm algorithm, Map headerClaims, Map payloadClaims) throws JWTCreationException { this.algorithm = algorithm; @@ -60,6 +66,19 @@ static JWTCreator.Builder init() { return new Builder(); } + private String sign(Provider cryptoProvider) throws SignatureGenerationException { + String header = Base64.getUrlEncoder().withoutPadding() + .encodeToString(headerJson.getBytes(StandardCharsets.UTF_8)); + String payload = Base64.getUrlEncoder().withoutPadding() + .encodeToString(payloadJson.getBytes(StandardCharsets.UTF_8)); + + byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), + payload.getBytes(StandardCharsets.UTF_8), cryptoProvider); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString((signatureBytes)); + + return String.format("%s.%s.%s", header, payload, signature); + } + /** * The Builder class holds the Claims that defines the JWT to be created. */ @@ -72,6 +91,55 @@ public static class Builder { this.headerClaims = new HashMap<>(); } + private static boolean validateClaim(Map map) { + // do not accept null values in maps + for (Entry entry : map.entrySet()) { + Object value = entry.getValue(); + if (!isSupportedType(value)) { + return false; + } + + if (entry.getKey() == null || !(entry.getKey() instanceof String)) { + return false; + } + } + return true; + } + + private static boolean validateClaim(List list) { + // accept null values in list + for (Object object : list) { + if (!isSupportedType(object)) { + return false; + } + } + return true; + } + + private static boolean isSupportedType(Object value) { + if (value instanceof List) { + return validateClaim((List) value); + } else if (value instanceof Map) { + return validateClaim((Map) value); + } else { + return isBasicType(value); + } + } + + private static boolean isBasicType(Object value) { + if (value == null) { + return true; + } else { + Class c = value.getClass(); + + if (c.isArray()) { + return c == Integer[].class || c == Long[].class || c == String[].class; + } + return c == String.class || c == Integer.class || c == Long.class || c == Double.class + || c == Date.class || c == Instant.class || c == Boolean.class; + } + } + /** * Add specific Claims to set as the Header. * If provided map is null then nothing is changed @@ -481,65 +549,58 @@ private boolean validatePayload(Map payload) { return true; } - private static boolean validateClaim(Map map) { - // do not accept null values in maps - for (Entry entry : map.entrySet()) { - Object value = entry.getValue(); - if (!isSupportedType(value)) { - return false; - } + /** + * Creates a new JWT and signs is with the given algorithm. + * + * @param algorithm used to sign the JWT + * @return a new JWT token + * @throws IllegalArgumentException if the provided algorithm is null. + * @throws JWTCreationException if the claims could not be converted to a valid JSON + * or there was a problem with the signing key. + */ + public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException { - if (entry.getKey() == null || !(entry.getKey() instanceof String)) { - return false; - } - } - return true; + return this.sign(algorithm, (Provider) null); } - private static boolean validateClaim(List list) { - // accept null values in list - for (Object object : list) { - if (!isSupportedType(object)) { - return false; - } + /** + * Creates a new JWT and signs is with the given algorithm. + * + * @param algorithm used to sign the JWT + * @param providerName the provider to use for crypto operations + * @return a new JWT token + * @throws IllegalArgumentException if the provided algorithm is null. + * @throws JWTCreationException if the claims could not be converted to a valid JSON + * or there was a problem with the signing key. + * @throws NoSuchProviderException if a provider with providerName name cannot be found with + * {@link Security#getProvider(String)} + */ + public String sign(Algorithm algorithm, String providerName) + throws IllegalArgumentException, JWTCreationException, NoSuchProviderException { + if (providerName == null) { + throw new IllegalArgumentException("providerName cannot be null"); } - return true; - } - private static boolean isSupportedType(Object value) { - if (value instanceof List) { - return validateClaim((List) value); - } else if (value instanceof Map) { - return validateClaim((Map) value); - } else { - return isBasicType(value); + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); } - } - - private static boolean isBasicType(Object value) { - if (value == null) { - return true; - } else { - Class c = value.getClass(); - if (c.isArray()) { - return c == Integer[].class || c == Long[].class || c == String[].class; - } - return c == String.class || c == Integer.class || c == Long.class || c == Double.class - || c == Date.class || c == Instant.class || c == Boolean.class; - } + return new JWTCreator(algorithm, headerClaims, payloadClaims).sign(provider); } /** * Creates a new JWT and signs is with the given algorithm. * - * @param algorithm used to sign the JWT + * @param algorithm used to sign the JWT + * @param cryptoProvider the provider to use for crypto operations * @return a new JWT token * @throws IllegalArgumentException if the provided algorithm is null. * @throws JWTCreationException if the claims could not be converted to a valid JSON * or there was a problem with the signing key. */ - public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException { + public String sign(Algorithm algorithm, Provider cryptoProvider) + throws IllegalArgumentException, JWTCreationException { if (algorithm == null) { throw new IllegalArgumentException("The Algorithm cannot be null."); } @@ -551,7 +612,7 @@ public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCrea if (signingKeyId != null) { withKeyId(signingKeyId); } - return new JWTCreator(algorithm, headerClaims, payloadClaims).sign(); + return new JWTCreator(algorithm, headerClaims, payloadClaims).sign(cryptoProvider); } private void assertNonNull(String name) { @@ -563,18 +624,6 @@ private void assertNonNull(String name) { private void addClaim(String name, Object value) { payloadClaims.put(name, value); } - } - private String sign() throws SignatureGenerationException { - String header = Base64.getUrlEncoder().withoutPadding() - .encodeToString(headerJson.getBytes(StandardCharsets.UTF_8)); - String payload = Base64.getUrlEncoder().withoutPadding() - .encodeToString(payloadJson.getBytes(StandardCharsets.UTF_8)); - - byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), - payload.getBytes(StandardCharsets.UTF_8)); - String signature = Base64.getUrlEncoder().withoutPadding().encodeToString((signatureBytes)); - - return String.format("%s.%s.%s", header, payload, signature); } } diff --git a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java index 07c86a4c..5e3b04ef 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java +++ b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java @@ -2,10 +2,10 @@ import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.*; +import com.auth0.jwt.impl.ExpectedCheckHolder; import com.auth0.jwt.impl.JWTParser; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; -import com.auth0.jwt.impl.ExpectedCheckHolder; import com.auth0.jwt.interfaces.Verification; import java.time.Clock; @@ -24,8 +24,8 @@ * @see com.auth0.jwt.interfaces.JWTVerifier */ public final class JWTVerifier implements com.auth0.jwt.interfaces.JWTVerifier { - private final Algorithm algorithm; final List expectedChecks; + private final Algorithm algorithm; private final JWTParser parser; JWTVerifier(Algorithm algorithm, List expectedChecks) { @@ -45,18 +45,82 @@ static Verification init(Algorithm algorithm) throws IllegalArgumentException { return new BaseVerification(algorithm); } + /** + * Perform the verification against the given Token, using any previous configured options. + * + * @param token to verify. + * @return a verified and decoded JWT. + * @throws AlgorithmMismatchException if the algorithm stated in the token's header is not equal to + * the one defined in the {@link JWTVerifier}. + * @throws SignatureVerificationException if the signature is invalid. + * @throws TokenExpiredException if the token has expired. + * @throws MissingClaimException if a claim to be verified is missing. + * @throws IncorrectClaimException if a claim contained a different value than the expected one. + */ + @Override + public DecodedJWT verify(String token) throws JWTVerificationException { + DecodedJWT jwt = new JWTDecoder(parser, token); + return verify(jwt); + } + + /** + * Perform the verification against the given decoded JWT, using any previous configured options. + * + * @param jwt to verify. + * @return a verified and decoded JWT. + * @throws AlgorithmMismatchException if the algorithm stated in the token's header is not equal to + * the one defined in the {@link JWTVerifier}. + * @throws SignatureVerificationException if the signature is invalid. + * @throws TokenExpiredException if the token has expired. + * @throws MissingClaimException if a claim to be verified is missing. + * @throws IncorrectClaimException if a claim contained a different value than the expected one. + */ + @Override + public DecodedJWT verify(DecodedJWT jwt) throws JWTVerificationException { + verifyAlgorithm(jwt, algorithm); + algorithm.verify(jwt); + verifyClaims(jwt, expectedChecks); + return jwt; + } + + private void verifyAlgorithm(DecodedJWT jwt, Algorithm expectedAlgorithm) throws AlgorithmMismatchException { + if (!expectedAlgorithm.getName().equals(jwt.getAlgorithm())) { + throw new AlgorithmMismatchException( + "The provided Algorithm doesn't match the one defined in the JWT's Header."); + } + } + + private void verifyClaims(DecodedJWT jwt, List expectedChecks) + throws TokenExpiredException, InvalidClaimException { + for (ExpectedCheckHolder expectedCheck : expectedChecks) { + boolean isValid; + String claimName = expectedCheck.getClaimName(); + Claim claim = jwt.getClaim(claimName); + + isValid = expectedCheck.verify(claim, jwt); + + if (!isValid) { + throw new IncorrectClaimException( + String.format("The Claim '%s' value doesn't match the required one.", claimName), + claimName, + claim + ); + } + } + } + /** * {@link Verification} implementation that accepts all the expected Claim values for verification, and * builds a {@link com.auth0.jwt.interfaces.JWTVerifier} used to verify a JWT's signature and expected claims. - * + *

* Note that this class is not thread-safe. Calling {@link #build()} returns an instance of * {@link com.auth0.jwt.interfaces.JWTVerifier} which can be reused. */ public static class BaseVerification implements Verification { private final Algorithm algorithm; private final List expectedChecks; - private long defaultLeeway; private final Map customLeeways; + private long defaultLeeway; private boolean ignoreIssuedAt; private Clock clock; @@ -425,69 +489,4 @@ private boolean isNullOrEmpty(String[] args) { return isAllNull; } } - - - /** - * Perform the verification against the given Token, using any previous configured options. - * - * @param token to verify. - * @return a verified and decoded JWT. - * @throws AlgorithmMismatchException if the algorithm stated in the token's header is not equal to - * the one defined in the {@link JWTVerifier}. - * @throws SignatureVerificationException if the signature is invalid. - * @throws TokenExpiredException if the token has expired. - * @throws MissingClaimException if a claim to be verified is missing. - * @throws IncorrectClaimException if a claim contained a different value than the expected one. - */ - @Override - public DecodedJWT verify(String token) throws JWTVerificationException { - DecodedJWT jwt = new JWTDecoder(parser, token); - return verify(jwt); - } - - /** - * Perform the verification against the given decoded JWT, using any previous configured options. - * - * @param jwt to verify. - * @return a verified and decoded JWT. - * @throws AlgorithmMismatchException if the algorithm stated in the token's header is not equal to - * the one defined in the {@link JWTVerifier}. - * @throws SignatureVerificationException if the signature is invalid. - * @throws TokenExpiredException if the token has expired. - * @throws MissingClaimException if a claim to be verified is missing. - * @throws IncorrectClaimException if a claim contained a different value than the expected one. - */ - @Override - public DecodedJWT verify(DecodedJWT jwt) throws JWTVerificationException { - verifyAlgorithm(jwt, algorithm); - algorithm.verify(jwt); - verifyClaims(jwt, expectedChecks); - return jwt; - } - - private void verifyAlgorithm(DecodedJWT jwt, Algorithm expectedAlgorithm) throws AlgorithmMismatchException { - if (!expectedAlgorithm.getName().equals(jwt.getAlgorithm())) { - throw new AlgorithmMismatchException( - "The provided Algorithm doesn't match the one defined in the JWT's Header."); - } - } - - private void verifyClaims(DecodedJWT jwt, List expectedChecks) - throws TokenExpiredException, InvalidClaimException { - for (ExpectedCheckHolder expectedCheck : expectedChecks) { - boolean isValid; - String claimName = expectedCheck.getClaimName(); - Claim claim = jwt.getClaim(claimName); - - isValid = expectedCheck.verify(claim, jwt); - - if (!isValid) { - throw new IncorrectClaimException( - String.format("The Claim '%s' value doesn't match the required one.", claimName), - claimName, - claim - ); - } - } - } } diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java index 27d79909..faba9adc 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java @@ -6,6 +6,9 @@ import com.auth0.jwt.interfaces.ECDSAKeyProvider; import com.auth0.jwt.interfaces.RSAKeyProvider; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.Security; import java.security.interfaces.*; /** @@ -19,6 +22,11 @@ public abstract class Algorithm { private final String name; private final String description; + protected Algorithm(String name, String description) { + this.name = name; + this.description = description; + } + /** * Creates a new Algorithm instance using SHA256withRSA. Tokens specify this as "RS256". * @@ -205,7 +213,6 @@ public static Algorithm HMAC512(byte[] secret) throws IllegalArgumentException { return new HMACAlgorithm("HS512", "HmacSHA512", secret); } - /** * Creates a new Algorithm instance using SHA256withECDSA. Tokens specify this as "ES256". * @@ -314,16 +321,10 @@ public static Algorithm ECDSA512(ECKey key) throws IllegalArgumentException { return ECDSA512(publicKey, privateKey); } - public static Algorithm none() { return new NoneAlgorithm(); } - protected Algorithm(String name, String description) { - this.name = name; - this.description = description; - } - /** * Getter for the Id of the Private Key used to sign the tokens. * This is usually specified as the `kid` claim in the Header. @@ -366,7 +367,46 @@ public String toString() { * meaning that it doesn't match the signatureBytes, * or if the Key is invalid. */ - public abstract void verify(DecodedJWT jwt) throws SignatureVerificationException; + public void verify(DecodedJWT jwt) throws SignatureVerificationException { + this.verify(jwt, (Provider) null); + } + + /** + * Verify the given token using this Algorithm instance. + * + * @param jwt the already decoded JWT that it's going to be verified. + * @param providerName the crypto provider to use. + * @throws SignatureVerificationException if the Token's Signature is invalid, + * meaning that it doesn't match the signatureBytes, + * or if the Key is invalid. + * @throws NoSuchProviderException if a provider with the given name is not registered + */ + + public void verify(DecodedJWT jwt, String providerName) + throws SignatureVerificationException, NoSuchProviderException { + if (providerName == null) { + throw new IllegalArgumentException("providerName cannot be null"); + } + + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); + } + + this.verify(jwt, provider); + } + + /** + * Verify the given token using this Algorithm instance. + * + * @param jwt the already decoded JWT that it's going to be verified. + * @param cryptoProvider the crypto provider to use. + * @throws SignatureVerificationException if the Token's Signature is invalid, + * meaning that it doesn't match the signatureBytes, + * or if the Key is invalid. + */ + + public abstract void verify(DecodedJWT jwt, Provider cryptoProvider) throws SignatureVerificationException; /** * Sign the given content using this Algorithm instance. @@ -380,13 +420,57 @@ public String toString() { */ public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException { // default implementation; keep around until sign(byte[]) method is removed + + return this.sign(headerBytes, payloadBytes, (Provider) null); + } + + /** + * Sign the given content using this Algorithm instance. + * + * @param headerBytes an array of bytes representing the base64 encoded header content + * to be verified against the signature. + * @param payloadBytes an array of bytes representing the base64 encoded payload content + * to be verified against the signature. + * @param providerName the Cryptographic provider name + * @return the signature in a base64 encoded array of bytes + * @throws SignatureGenerationException if the Key is invalid. + * @throws NoSuchProviderException if no provider with the given name is installed + */ + public byte[] sign(byte[] headerBytes, byte[] payloadBytes, String providerName) + throws SignatureGenerationException, NoSuchProviderException { + if (providerName == null) { + throw new IllegalArgumentException("providerName cannot be null"); + } + + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); + } + + return sign(headerBytes, payloadBytes, provider); + } + + /** + * Sign the given content using this Algorithm instance. + * + * @param headerBytes an array of bytes representing the base64 encoded header content + * to be verified against the signature. + * @param payloadBytes an array of bytes representing the base64 encoded payload content + * to be verified against the signature. + * @param cryptoProvider the Cryptographic Provider to use (see {@link Provider}) + * @return the signature in a base64 encoded array of bytes + * @throws SignatureGenerationException if the Key is invalid. + */ + public byte[] sign(byte[] headerBytes, byte[] payloadBytes, Provider cryptoProvider) + throws SignatureGenerationException { + // default implementation; keep around until sign(byte[]) method is removed byte[] contentBytes = new byte[headerBytes.length + 1 + payloadBytes.length]; System.arraycopy(headerBytes, 0, contentBytes, 0, headerBytes.length); contentBytes[headerBytes.length] = (byte) '.'; System.arraycopy(payloadBytes, 0, contentBytes, headerBytes.length + 1, payloadBytes.length); - return sign(contentBytes); + return sign(contentBytes, cryptoProvider); } /** @@ -398,7 +482,44 @@ public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGene * @return the signature in a base64 encoded array of bytes * @throws SignatureGenerationException if the Key is invalid. */ + public byte[] sign(byte[] contentBytes) throws SignatureGenerationException { + return this.sign(contentBytes, (Provider) null); + } - public abstract byte[] sign(byte[] contentBytes) throws SignatureGenerationException; + /** + * Sign the given content using this Algorithm instance. + * To get the correct JWT Signature, ensure the content is in the format {HEADER}.{PAYLOAD} + * + * @param contentBytes an array of bytes representing the base64 encoded content + * to be verified against the signature. + * @param providerName the Cryptographic Provider's name/ID as provided by {@link Provider#getName()} + * @return the signature in a base64 encoded array of bytes + * @throws SignatureGenerationException if the Key is invalid. + * @throws NoSuchProviderException if a provider with the given name is installed + */ + public byte[] sign(byte[] contentBytes, String providerName) + throws SignatureGenerationException, NoSuchProviderException { + if (providerName == null) { + throw new IllegalArgumentException("providerName cannot be null"); + } + + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); + } + + return this.sign(contentBytes, provider); + } + /** + * Sign the given content using this Algorithm instance. + * To get the correct JWT Signature, ensure the content is in the format {HEADER}.{PAYLOAD} + * + * @param contentBytes an array of bytes representing the base64 encoded content + * to be verified against the signature. + * @param cryptoProvider the Cryptographic Provider to use (see {@link Provider}) + * @return the signature in a base64 encoded array of bytes + * @throws SignatureGenerationException if the Key is invalid. + */ + public abstract byte[] sign(byte[] contentBytes, Provider cryptoProvider) throws SignatureGenerationException; } diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/CryptoHelper.java b/lib/src/main/java/com/auth0/jwt/algorithms/CryptoHelper.java index 7b8c5c2a..f92e6962 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/CryptoHelper.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/CryptoHelper.java @@ -33,9 +33,67 @@ boolean verifySignatureFor( String header, String payload, byte[] signatureBytes - ) throws NoSuchAlgorithmException, InvalidKeyException { - return verifySignatureFor(algorithm, secretBytes, - header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8), signatureBytes); + ) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException { + return this.verifySignatureFor(algorithm, secretBytes, header.getBytes(StandardCharsets.UTF_8), + payload.getBytes(StandardCharsets.UTF_8), signatureBytes, (Provider) null); + } + + /** + * Verify signature for JWT header and payload. + * + * @param algorithm algorithm name. + * @param secretBytes algorithm secret. + * @param header JWT header. + * @param payload JWT payload. + * @param signatureBytes JWT signature. + * @param providerName the crypto provider to use + * @return true if signature is valid. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + + boolean verifySignatureFor( + String algorithm, + byte[] secretBytes, + String header, + String payload, + byte[] signatureBytes, + String providerName + ) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException { + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); + } + + return this.verifySignatureFor(algorithm, secretBytes, + header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8), signatureBytes, + provider); + } + + /** + * Verify signature for JWT header and payload. + * + * @param algorithm algorithm name. + * @param secretBytes algorithm secret. + * @param header JWT header. + * @param payload JWT payload. + * @param signatureBytes JWT signature. + * @param cryptoProvider the crypto provider to use + * @return true if signature is valid. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + + boolean verifySignatureFor( + String algorithm, + byte[] secretBytes, + String header, + String payload, + byte[] signatureBytes, + Provider cryptoProvider + ) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException { + return verifySignatureFor(algorithm, secretBytes, header.getBytes(StandardCharsets.UTF_8), + payload.getBytes(StandardCharsets.UTF_8), signatureBytes, cryptoProvider); } /** @@ -58,10 +116,67 @@ boolean verifySignatureFor( byte[] payloadBytes, byte[] signatureBytes ) throws NoSuchAlgorithmException, InvalidKeyException { - return MessageDigest.isEqual(createSignatureFor(algorithm, secretBytes, headerBytes, payloadBytes), + return MessageDigest.isEqual(createSignatureFor(algorithm, secretBytes, headerBytes, payloadBytes, + (Provider) null), signatureBytes); + } + + /** + * Verify signature for JWT header and payload. + * + * @param algorithm algorithm name. + * @param secretBytes algorithm secret. + * @param headerBytes JWT header. + * @param payloadBytes JWT payload. + * @param signatureBytes JWT signature. + * @param providerName the crypto provider to use + * @return true if signature is valid. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + + boolean verifySignatureFor( + String algorithm, + byte[] secretBytes, + byte[] headerBytes, + byte[] payloadBytes, + byte[] signatureBytes, + String providerName + ) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException { + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); + } + + return MessageDigest.isEqual(createSignatureFor(algorithm, secretBytes, headerBytes, payloadBytes, provider), signatureBytes); } + /** + * Verify signature for JWT header and payload. + * + * @param algorithm algorithm name. + * @param secretBytes algorithm secret. + * @param headerBytes JWT header. + * @param payloadBytes JWT payload. + * @param signatureBytes JWT signature. + * @param cryptoProvider the crypto provider. + * @return true if signature is valid. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + + boolean verifySignatureFor( + String algorithm, + byte[] secretBytes, + byte[] headerBytes, + byte[] payloadBytes, + byte[] signatureBytes, + Provider cryptoProvider + ) throws NoSuchAlgorithmException, InvalidKeyException { + return MessageDigest.isEqual(createSignatureFor(algorithm, secretBytes, headerBytes, payloadBytes, + cryptoProvider), signatureBytes); + } + /** * Verify signature for JWT header and payload. * @@ -82,7 +197,62 @@ boolean verifySignatureFor( byte[] signatureBytes ) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { return verifySignatureFor(algorithm, publicKey, header.getBytes(StandardCharsets.UTF_8), - payload.getBytes(StandardCharsets.UTF_8), signatureBytes); + payload.getBytes(StandardCharsets.UTF_8), signatureBytes, (Provider) null); + } + + /** + * Verify signature for JWT header and payload. + * + * @param algorithm algorithm name. + * @param publicKey algorithm public key. + * @param header JWT header. + * @param payload JWT payload. + * @param signatureBytes JWT signature. + * @param providerName the crypto provider to use + * @return true if signature is valid. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + boolean verifySignatureFor( + String algorithm, + PublicKey publicKey, + String header, + String payload, + byte[] signatureBytes, + String providerName + ) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, NoSuchProviderException { + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); + } + + return verifySignatureFor(algorithm, publicKey, header.getBytes(StandardCharsets.UTF_8), + payload.getBytes(StandardCharsets.UTF_8), signatureBytes, provider); + } + + /** + * Verify signature for JWT header and payload. + * + * @param algorithm algorithm name. + * @param publicKey algorithm public key. + * @param header JWT header. + * @param payload JWT payload. + * @param signatureBytes JWT signature. + * @param cryptoProvider the crypto provider to use + * @return true if signature is valid. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + boolean verifySignatureFor( + String algorithm, + PublicKey publicKey, + String header, + String payload, + byte[] signatureBytes, + Provider cryptoProvider + ) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + return verifySignatureFor(algorithm, publicKey, header.getBytes(StandardCharsets.UTF_8), + payload.getBytes(StandardCharsets.UTF_8), signatureBytes, cryptoProvider); } /** @@ -104,7 +274,63 @@ boolean verifySignatureFor( byte[] payloadBytes, byte[] signatureBytes ) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { - final Signature s = Signature.getInstance(algorithm); + return this.verifySignatureFor(algorithm, publicKey, headerBytes, payloadBytes, signatureBytes, + (Provider) null); + } + + /** + * Verify signature for JWT header and payload using a public key. + * + * @param algorithm algorithm name. + * @param publicKey the public key to use for verification. + * @param headerBytes JWT header. + * @param payloadBytes JWT payload. + * @param signatureBytes JWT signature. + * @param providerName the crypto provider to use + * @return true if signature is valid. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + boolean verifySignatureFor( + String algorithm, + PublicKey publicKey, + byte[] headerBytes, + byte[] payloadBytes, + byte[] signatureBytes, + String providerName + ) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, NoSuchProviderException { + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); + } + + return this.verifySignatureFor(algorithm, publicKey, headerBytes, payloadBytes, signatureBytes, provider); + } + + /** + * Verify signature for JWT header and payload using a public key. + * + * @param algorithm algorithm name. + * @param publicKey the public key to use for verification. + * @param headerBytes JWT header. + * @param payloadBytes JWT payload. + * @param signatureBytes JWT signature. + * @param cryptoProvider the crypto provider to use + * @return true if signature is valid. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + boolean verifySignatureFor( + String algorithm, + PublicKey publicKey, + byte[] headerBytes, + byte[] payloadBytes, + byte[] signatureBytes, + Provider cryptoProvider + ) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + final Signature s = cryptoProvider != null + ? Signature.getInstance(algorithm, cryptoProvider) + : Signature.getInstance(algorithm); s.initVerify(publicKey); s.update(headerBytes); s.update(JWT_PART_SEPARATOR); @@ -131,7 +357,64 @@ byte[] createSignatureFor( byte[] headerBytes, byte[] payloadBytes ) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { - final Signature s = Signature.getInstance(algorithm); + return this.createSignatureFor(algorithm, privateKey, headerBytes, payloadBytes, (Provider) null); + } + + /** + * Create signature for JWT header and payload using a private key. + * + * @param algorithm algorithm name. + * @param privateKey the private key to use for signing. + * @param headerBytes JWT header. + * @param payloadBytes JWT payload. + * @return the signature bytes. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + * @throws SignatureException if this signature object is not initialized properly + * or if this signature algorithm is unable to process the input data provided. + */ + byte[] createSignatureFor( + String algorithm, + PrivateKey privateKey, + byte[] headerBytes, + byte[] payloadBytes, + String providerName + ) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); + } + + return this.createSignatureFor(algorithm, privateKey, headerBytes, payloadBytes, provider); + } + + /** + * Create signature for JWT header and payload using a private key. + * + * @param algorithm algorithm name. + * @param privateKey the private key to use for signing. + * @param headerBytes JWT header. + * @param payloadBytes JWT payload. + * @param cryptoProvider The crypto provider to use. If null is given, then the default security provider + * will be used ({@link Security#getProviders()}). + * @return the signature bytes. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + * @throws SignatureException if this signature object is not initialized properly + * or if this signature algorithm is unable to process the input data provided. + */ + byte[] createSignatureFor( + String algorithm, + PrivateKey privateKey, + byte[] headerBytes, + byte[] payloadBytes, + Provider cryptoProvider + ) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + final Signature s; + + s = cryptoProvider != null ? Signature.getInstance(algorithm, cryptoProvider) + : Signature.getInstance(algorithm); + s.initSign(privateKey); s.update(headerBytes); s.update(JWT_PART_SEPARATOR); @@ -156,7 +439,59 @@ byte[] createSignatureFor( byte[] headerBytes, byte[] payloadBytes ) throws NoSuchAlgorithmException, InvalidKeyException { - final Mac mac = Mac.getInstance(algorithm); + + return this.createSignatureFor(algorithm, secretBytes, headerBytes, payloadBytes, (Provider) null); + } + + /** + * Create signature for JWT header and payload. + * + * @param algorithm algorithm name. + * @param secretBytes algorithm secret. + * @param headerBytes JWT header. + * @param payloadBytes JWT payload. + * @param providerName the crypto provider to use + * @return the signature bytes. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + byte[] createSignatureFor( + String algorithm, + byte[] secretBytes, + byte[] headerBytes, + byte[] payloadBytes, + String providerName + ) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException { + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); + } + + return this.createSignatureFor(algorithm, secretBytes, headerBytes, payloadBytes, + provider); + } + + /** + * Create signature for JWT header and payload. + * + * @param algorithm algorithm name. + * @param secretBytes algorithm secret. + * @param headerBytes JWT header. + * @param payloadBytes JWT payload. + * @param cryptoProvider the crypto provider to use + * @return the signature bytes. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + byte[] createSignatureFor( + String algorithm, + byte[] secretBytes, + byte[] headerBytes, + byte[] payloadBytes, + Provider cryptoProvider + ) throws NoSuchAlgorithmException, InvalidKeyException { + final Mac mac = cryptoProvider != null ? Mac.getInstance(algorithm, cryptoProvider) + : Mac.getInstance(algorithm); mac.init(new SecretKeySpec(secretBytes, algorithm)); mac.update(headerBytes); mac.update(JWT_PART_SEPARATOR); @@ -176,7 +511,49 @@ byte[] createSignatureFor( */ byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] contentBytes) throws NoSuchAlgorithmException, InvalidKeyException { - final Mac mac = Mac.getInstance(algorithm); + + return this.createSignatureFor(algorithm, secretBytes, contentBytes, (Provider) null); + } + + /** + * Create signature. + * To get the correct JWT Signature, ensure the content is in the format {HEADER}.{PAYLOAD} + * + * @param algorithm algorithm name. + * @param secretBytes algorithm secret. + * @param contentBytes the content to be signed. + * @param providerName the crypto provider to use. + * @return the signature bytes. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] contentBytes, String providerName) + throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, NoSuchProviderException { + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); + } + + return this.createSignatureFor(algorithm, secretBytes, contentBytes, provider); + } + + /** + * Create signature. + * To get the correct JWT Signature, ensure the content is in the format {HEADER}.{PAYLOAD} + * + * @param algorithm algorithm name. + * @param secretBytes algorithm secret. + * @param contentBytes the content to be signed. + * @param cryptoProvider the crypto provider to use. + * @return the signature bytes. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + */ + byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] contentBytes, Provider cryptoProvider) + throws NoSuchAlgorithmException, InvalidKeyException { + final Mac mac = cryptoProvider != null + ? Mac.getInstance(algorithm, cryptoProvider) + : Mac.getInstance(algorithm); mac.init(new SecretKeySpec(secretBytes, algorithm)); return mac.doFinal(contentBytes); } @@ -194,15 +571,46 @@ byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] contentBy * @throws SignatureException if this signature object is not initialized properly * or if this signature algorithm is unable to process the input data provided. */ + byte[] createSignatureFor( + String algorithm, + PrivateKey privateKey, + byte[] contentBytes, + String providerName + ) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, NoSuchProviderException { + Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new NoSuchProviderException(String.format("No provider named [%s] installed", providerName)); + } + + return this.createSignatureFor(algorithm, privateKey, contentBytes, provider); + } + /** + * Create signature using a private key. + * To get the correct JWT Signature, ensure the content is in the format {HEADER}.{PAYLOAD} + * + * @param algorithm algorithm name. + * @param privateKey the private key to use for signing. + * @param contentBytes the content to be signed. + * @param cryptoProvider The crypto provider to use. + * @return the signature bytes. + * @throws NoSuchAlgorithmException if the algorithm is not supported. + * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. + * @throws SignatureException if this signature object is not initialized properly + * or if this signature algorithm is unable to process the input data provided. + */ byte[] createSignatureFor( String algorithm, PrivateKey privateKey, - byte[] contentBytes + byte[] contentBytes, + Provider cryptoProvider ) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { - final Signature s = Signature.getInstance(algorithm); + final Signature s = cryptoProvider != null ? Signature.getInstance(algorithm, cryptoProvider) + : Signature.getInstance(algorithm); + s.initSign(privateKey); s.update(contentBytes); return s.sign(); } + } diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java index b3046097..fdf31aa0 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java @@ -8,6 +8,7 @@ import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.security.Provider; import java.security.SignatureException; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; @@ -41,8 +42,31 @@ class ECDSAAlgorithm extends Algorithm { this(new CryptoHelper(), id, algorithm, ecNumberSize, keyProvider); } + //Visible for testing + static ECDSAKeyProvider providerForKeys(final ECPublicKey publicKey, final ECPrivateKey privateKey) { + if (publicKey == null && privateKey == null) { + throw new IllegalArgumentException("Both provided Keys cannot be null."); + } + return new ECDSAKeyProvider() { + @Override + public ECPublicKey getPublicKeyById(String keyId) { + return publicKey; + } + + @Override + public ECPrivateKey getPrivateKey() { + return privateKey; + } + + @Override + public String getPrivateKeyId() { + return null; + } + }; + } + @Override - public void verify(DecodedJWT jwt) throws SignatureVerificationException { + public void verify(DecodedJWT jwt, Provider cryptoProvider) throws SignatureVerificationException { try { byte[] signatureBytes = Base64.getUrlDecoder().decode(jwt.getSignature()); ECPublicKey publicKey = keyProvider.getPublicKeyById(jwt.getKeyId()); @@ -50,40 +74,26 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException { throw new IllegalStateException("The given Public Key is null."); } validateSignatureStructure(signatureBytes, publicKey); - boolean valid = crypto.verifySignatureFor( - getDescription(), publicKey, jwt.getHeader(), jwt.getPayload(), JOSEToDER(signatureBytes)); + boolean valid = crypto.verifySignatureFor(getDescription(), publicKey, jwt.getHeader(), jwt.getPayload(), + JOSEToDER(signatureBytes), cryptoProvider); if (!valid) { throw new SignatureVerificationException(this); } } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException - | IllegalStateException | IllegalArgumentException e) { + | IllegalStateException | IllegalArgumentException e) { throw new SignatureVerificationException(this, e); } } @Override - public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException { - try { - ECPrivateKey privateKey = keyProvider.getPrivateKey(); - if (privateKey == null) { - throw new IllegalStateException("The given Private Key is null."); - } - byte[] signature = crypto.createSignatureFor(getDescription(), privateKey, headerBytes, payloadBytes); - return DERToJOSE(signature); - } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) { - throw new SignatureGenerationException(this, e); - } - } - - @Override - public byte[] sign(byte[] contentBytes) throws SignatureGenerationException { + public byte[] sign(byte[] contentBytes, Provider cryptoProvider) throws SignatureGenerationException { try { ECPrivateKey privateKey = keyProvider.getPrivateKey(); if (privateKey == null) { throw new IllegalStateException("The given Private Key is null."); } - byte[] signature = crypto.createSignatureFor(getDescription(), privateKey, contentBytes); + byte[] signature = crypto.createSignatureFor(getDescription(), privateKey, contentBytes, cryptoProvider); return DERToJOSE(signature); } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) { throw new SignatureGenerationException(this, e); @@ -280,27 +290,4 @@ private int countPadding(byte[] bytes, int fromIndex, int toIndex) { } return (bytes[fromIndex + padding] & 0xff) > 0x7f ? padding - 1 : padding; } - - //Visible for testing - static ECDSAKeyProvider providerForKeys(final ECPublicKey publicKey, final ECPrivateKey privateKey) { - if (publicKey == null && privateKey == null) { - throw new IllegalArgumentException("Both provided Keys cannot be null."); - } - return new ECDSAKeyProvider() { - @Override - public ECPublicKey getPublicKeyById(String keyId) { - return publicKey; - } - - @Override - public ECPrivateKey getPrivateKey() { - return privateKey; - } - - @Override - public String getPrivateKeyId() { - return null; - } - }; - } } diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java index 0306e7c4..aa36dff1 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java @@ -7,6 +7,8 @@ import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; import java.util.Arrays; import java.util.Base64; @@ -48,23 +50,25 @@ static byte[] getSecretBytes(String secret) throws IllegalArgumentException { } @Override - public void verify(DecodedJWT jwt) throws SignatureVerificationException { + public void verify(DecodedJWT jwt, Provider cryptoProvider) throws SignatureVerificationException { try { byte[] signatureBytes = Base64.getUrlDecoder().decode(jwt.getSignature()); - boolean valid = crypto.verifySignatureFor( - getDescription(), secret, jwt.getHeader(), jwt.getPayload(), signatureBytes); + boolean valid = this.crypto.verifySignatureFor( + getDescription(), secret, jwt.getHeader(), jwt.getPayload(), signatureBytes, cryptoProvider); if (!valid) { throw new SignatureVerificationException(this); } - } catch (IllegalStateException | InvalidKeyException | NoSuchAlgorithmException | IllegalArgumentException e) { + } catch (IllegalStateException | InvalidKeyException | NoSuchAlgorithmException | IllegalArgumentException + | NoSuchProviderException e) { throw new SignatureVerificationException(this, e); } } @Override - public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException { + public byte[] sign(byte[] headerBytes, byte[] payloadBytes, Provider cryptoProvider) + throws SignatureGenerationException { try { - return crypto.createSignatureFor(getDescription(), secret, headerBytes, payloadBytes); + return this.crypto.createSignatureFor(getDescription(), secret, headerBytes, payloadBytes, (Provider) null); } catch (NoSuchAlgorithmException | InvalidKeyException e) { throw new SignatureGenerationException(this, e); } @@ -72,8 +76,13 @@ public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGene @Override public byte[] sign(byte[] contentBytes) throws SignatureGenerationException { + return this.sign(contentBytes, (Provider) null); + } + + @Override + public byte[] sign(byte[] contentBytes, Provider cryptoProvider) throws SignatureGenerationException { try { - return crypto.createSignatureFor(getDescription(), secret, contentBytes); + return this.crypto.createSignatureFor(getDescription(), secret, contentBytes, cryptoProvider); } catch (NoSuchAlgorithmException | InvalidKeyException e) { throw new SignatureGenerationException(this, e); } diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java index 5c6c0fc5..296cc516 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java @@ -3,6 +3,8 @@ import com.auth0.jwt.exceptions.SignatureGenerationException; import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; + +import java.security.Provider; import java.util.Base64; class NoneAlgorithm extends Algorithm { @@ -12,7 +14,7 @@ class NoneAlgorithm extends Algorithm { } @Override - public void verify(DecodedJWT jwt) throws SignatureVerificationException { + public void verify(DecodedJWT jwt, Provider cryptoProvider) throws SignatureVerificationException { try { byte[] signatureBytes = Base64.getUrlDecoder().decode(jwt.getSignature()); @@ -25,12 +27,12 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException { } @Override - public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException { + public byte[] sign(byte[] contentBytes) throws SignatureGenerationException { return new byte[0]; } @Override - public byte[] sign(byte[] contentBytes) throws SignatureGenerationException { + public byte[] sign(byte[] contentBytes, Provider cryptoProvider) throws SignatureGenerationException { return new byte[0]; } } diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java index 0c7a5b57..06bd5a80 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java @@ -5,9 +5,9 @@ import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.RSAKeyProvider; -import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.security.Provider; import java.security.SignatureException; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; @@ -38,8 +38,31 @@ class RSAAlgorithm extends Algorithm { this(new CryptoHelper(), id, algorithm, keyProvider); } + //Visible for testing + static RSAKeyProvider providerForKeys(final RSAPublicKey publicKey, final RSAPrivateKey privateKey) { + if (publicKey == null && privateKey == null) { + throw new IllegalArgumentException("Both provided Keys cannot be null."); + } + return new RSAKeyProvider() { + @Override + public RSAPublicKey getPublicKeyById(String keyId) { + return publicKey; + } + + @Override + public RSAPrivateKey getPrivateKey() { + return privateKey; + } + + @Override + public String getPrivateKeyId() { + return null; + } + }; + } + @Override - public void verify(DecodedJWT jwt) throws SignatureVerificationException { + public void verify(DecodedJWT jwt, Provider cryptoProvider) throws SignatureVerificationException { try { byte[] signatureBytes = Base64.getUrlDecoder().decode(jwt.getSignature()); RSAPublicKey publicKey = keyProvider.getPublicKeyById(jwt.getKeyId()); @@ -47,37 +70,29 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException { throw new IllegalStateException("The given Public Key is null."); } boolean valid = crypto.verifySignatureFor( - getDescription(), publicKey, jwt.getHeader(), jwt.getPayload(), signatureBytes); + getDescription(), publicKey, jwt.getHeader(), jwt.getPayload(), signatureBytes, cryptoProvider); if (!valid) { throw new SignatureVerificationException(this); } } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException - | IllegalArgumentException | IllegalStateException e) { + | IllegalArgumentException | IllegalStateException e) { throw new SignatureVerificationException(this, e); } } @Override - public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException { - try { - RSAPrivateKey privateKey = keyProvider.getPrivateKey(); - if (privateKey == null) { - throw new IllegalStateException("The given Private Key is null."); - } - return crypto.createSignatureFor(getDescription(), privateKey, headerBytes, payloadBytes); - } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) { - throw new SignatureGenerationException(this, e); - } + public byte[] sign(byte[] contentBytes) throws SignatureGenerationException { + return this.sign(contentBytes, (Provider) null); } @Override - public byte[] sign(byte[] contentBytes) throws SignatureGenerationException { + public byte[] sign(byte[] contentBytes, Provider cryptoProvider) throws SignatureGenerationException { try { RSAPrivateKey privateKey = keyProvider.getPrivateKey(); if (privateKey == null) { throw new IllegalStateException("The given Private Key is null."); } - return crypto.createSignatureFor(getDescription(), privateKey, contentBytes); + return crypto.createSignatureFor(getDescription(), privateKey, contentBytes, cryptoProvider); } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) { throw new SignatureGenerationException(this, e); } @@ -87,27 +102,4 @@ public byte[] sign(byte[] contentBytes) throws SignatureGenerationException { public String getSigningKeyId() { return keyProvider.getPrivateKeyId(); } - - //Visible for testing - static RSAKeyProvider providerForKeys(final RSAPublicKey publicKey, final RSAPrivateKey privateKey) { - if (publicKey == null && privateKey == null) { - throw new IllegalArgumentException("Both provided Keys cannot be null."); - } - return new RSAKeyProvider() { - @Override - public RSAPublicKey getPublicKeyById(String keyId) { - return publicKey; - } - - @Override - public RSAPrivateKey getPrivateKey() { - return privateKey; - } - - @Override - public String getPrivateKeyId() { - return null; - } - }; - } } diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index 020e5e37..5300a5fd 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -9,6 +9,8 @@ import org.junit.rules.ExpectedException; import java.nio.charset.StandardCharsets; +import java.security.NoSuchProviderException; +import java.security.Security; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.RSAPrivateKey; import java.time.Instant; @@ -574,6 +576,63 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { } + @SuppressWarnings("unchecked") + @Test + public void shouldAcceptCustomMapClaimOfBasicObjectTypesAndProvider() throws Exception { + Map data = new HashMap<>(); + + // simple types + data.put("string", "abc"); + data.put("integer", 1); + data.put("long", Long.MAX_VALUE); + data.put("double", 123.456d); + data.put("date", new Date(123000L)); + data.put("instant", Instant.ofEpochSecond(123)); + data.put("boolean", true); + + // array types + data.put("intArray", new Integer[]{3, 5}); + data.put("longArray", new Long[]{Long.MAX_VALUE, Long.MIN_VALUE}); + data.put("stringArray", new String[]{"string"}); + + data.put("list", Arrays.asList("a", "b", "c")); + + Map sub = new HashMap<>(); + sub.put("subKey", "subValue"); + + data.put("map", sub); + + String jwt = JWTCreator.init() + .withClaim("data", data) + .sign(Algorithm.HMAC256("secret"), Security.getProvider("SunEC").getName()); + + assertThat(jwt, is(notNullValue())); + String[] parts = jwt.split("\\."); + + String body = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8); + ObjectMapper mapper = new ObjectMapper(); + Map map = (Map) mapper.readValue(body, Map.class).get("data"); + + assertThat(map.get("string"), is("abc")); + assertThat(map.get("integer"), is(1)); + assertThat(map.get("long"), is(Long.MAX_VALUE)); + assertThat(map.get("double"), is(123.456d)); + + assertThat(map.get("date"), is(123)); + assertThat(map.get("instant"), is(123)); + assertThat(map.get("boolean"), is(true)); + + // array types + assertThat(map.get("intArray"), is(Arrays.asList(3, 5))); + assertThat(map.get("longArray"), is(Arrays.asList(Long.MAX_VALUE, Long.MIN_VALUE))); + assertThat(map.get("stringArray"), is(Collections.singletonList("string"))); + + // list + assertThat(map.get("list"), is(Arrays.asList("a", "b", "c"))); + assertThat(map.get("map"), is(sub)); + + } + @SuppressWarnings("unchecked") @Test public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { @@ -651,6 +710,30 @@ public void shouldRefuseCustomClaimForNullMapKey() { .sign(Algorithm.HMAC256("secret")); } + @Test + public void shouldRefuseNullProviderName() throws NoSuchProviderException { + Map data = new HashMap<>(); + data.put("test1", Arrays.asList("a", null, "c")); + + exception.expect(IllegalArgumentException.class); + + JWTCreator.init() + .withClaim("pojo", data) + .sign(Algorithm.HMAC256("secret"), (String) null); + } + + @Test + public void shouldRefuseProviderNotFound() throws NoSuchProviderException { + Map data = new HashMap<>(); + data.put("test1", Arrays.asList("a", null, "c")); + + exception.expect(NoSuchProviderException.class); + + JWTCreator.init() + .withClaim("pojo", data) + .sign(Algorithm.HMAC256("secret"), "some-provider"); + } + @SuppressWarnings({"unchecked", "rawtypes"}) @Test public void shouldRefuseCustomMapClaimForNonStringKey() { @@ -664,6 +747,15 @@ public void shouldRefuseCustomMapClaimForNonStringKey() { .sign(Algorithm.HMAC256("secret")); } + @SuppressWarnings({"unchecked", "rawtypes"}) + @Test + public void shouldAcceptNullValueClaim() { + + JWTCreator.init() + .withNullClaim("pojo") + .sign(Algorithm.HMAC256("secret")); + } + @Test public void shouldRefuseCustomListClaimForUnknownListElement() { List list = Collections.singletonList(new UserPojo("Michael", 255)); diff --git a/lib/src/test/java/com/auth0/jwt/TokenUtilsTest.java b/lib/src/test/java/com/auth0/jwt/TokenUtilsTest.java index 8649ec90..5942b1e5 100644 --- a/lib/src/test/java/com/auth0/jwt/TokenUtilsTest.java +++ b/lib/src/test/java/com/auth0/jwt/TokenUtilsTest.java @@ -34,7 +34,7 @@ public void shouldSplitTokenWithEmptySignature() { assertThat(parts, is(arrayWithSize(3))); assertThat(parts[0], is("eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0")); assertThat(parts[1], is("eyJpc3MiOiJhdXRoMCJ9")); - assertThat(parts[2], is(isEmptyString())); + assertThat(parts[2], is(emptyString())); } @Test diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/AlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/AlgorithmTest.java index e09661d3..fbb598de 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/AlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/AlgorithmTest.java @@ -9,10 +9,13 @@ import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; +import java.security.NoSuchProviderException; +import java.security.Provider; import java.security.interfaces.*; import static org.hamcrest.Matchers.*; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.withSettings; import static org.mockito.ArgumentMatchers.any; @@ -561,7 +564,8 @@ public void shouldForwardHeaderPayloadSignatureToSiblingSignMethodForBackwardsCo byte[] signature = new byte[]{0x10, 0x11, 0x12}; when(algorithm.sign(any(byte[].class), any(byte[].class))).thenCallRealMethod(); - when(algorithm.sign(contentCaptor.capture())).thenReturn(signature); + when(algorithm.sign(any(byte[].class), any(byte[].class), (Provider) isNull())).thenCallRealMethod(); + when(algorithm.sign(contentCaptor.capture(), (Provider) isNull())).thenReturn(signature); byte[] sign = algorithm.sign(header, payload); diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java b/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java index ef8e65e8..a6a20f7b 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java @@ -1,35 +1,61 @@ package com.auth0.jwt.algorithms; import java.nio.charset.StandardCharsets; +import java.security.NoSuchProviderException; +import java.security.Provider; import java.util.Base64; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.hamcrest.Matchers.*; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.fail; public abstract class CryptoTestHelper { private static final Pattern authHeaderPattern = Pattern.compile("^([\\w-]+)\\.([\\w-]+)\\.([\\w-]+)"); - public static String asJWT(Algorithm algorithm, String header, String payload) { - byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8)); - String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); - return String.format("%s.%s.%s", header, payload, jwtSignature); - } - - public static void assertSignatureValue(String jwt, String expectedSignature) { - String jwtSignature = jwt.substring(jwt.lastIndexOf('.') + 1); + public static String asJWT(Algorithm algorithm, String header, String payload) { + byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8)); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); + return String.format("%s.%s.%s", header, payload, jwtSignature); + } + + public static String asJWT(Algorithm algorithm, String header, String payload, String providerName) throws NoSuchProviderException { + byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8), providerName); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); + return String.format("%s.%s.%s", header, payload, jwtSignature); + } + + public static String asJWT(Algorithm algorithm, String header, String payload, Provider provider) { + byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8), provider); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); + return String.format("%s.%s.%s", header, payload, jwtSignature); + } + + public static String asJWT(Algorithm algorithm, byte[] content, String providerName) throws NoSuchProviderException { + byte[] signatureBytes = algorithm.sign(content, providerName); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); + return String.format("%s.%s", new String(content, StandardCharsets.UTF_8), jwtSignature); + } + + public static String asJWT(Algorithm algorithm, byte[] content, Provider provider) { + byte[] signatureBytes = algorithm.sign(content, provider); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); + return String.format("%s.%s", new String(content), jwtSignature); + } + + public static void assertSignatureValue(String jwt, String expectedSignature) { + String jwtSignature = jwt.substring(jwt.lastIndexOf('.') + 1); assertThat(jwtSignature, is(expectedSignature)); - } - - public static void assertSignaturePresent(String jwt) { + } + + public static void assertSignaturePresent(String jwt) { Matcher matcher = authHeaderPattern.matcher(jwt); if (!matcher.find() || matcher.groupCount() < 3) { fail("No signature present in " + jwt); } - + assertThat(matcher.group(3), not(is(emptyString()))); - } + } } diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java index 2e636c71..4db17bce 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java @@ -10,6 +10,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.ArgumentMatchers; import java.io.ByteArrayOutputStream; import java.math.BigInteger; @@ -27,12 +28,11 @@ import static com.auth0.jwt.algorithms.CryptoTestHelper.asJWT; import static com.auth0.jwt.algorithms.CryptoTestHelper.assertSignaturePresent; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.isA; import static org.hamcrest.Matchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -41,7 +41,7 @@ public class ECDSAAlgorithmTest { private static final String PRIVATE_KEY_FILE_256 = "src/test/resources/ec256-key-private.pem"; private static final String PUBLIC_KEY_FILE_256 = "src/test/resources/ec256-key-public.pem"; private static final String INVALID_PUBLIC_KEY_FILE_256 = "src/test/resources/ec256-key-public-invalid.pem"; - + private static final String PRIVATE_KEY_FILE_384 = "src/test/resources/ec384-key-private.pem"; private static final String PUBLIC_KEY_FILE_384 = "src/test/resources/ec384-key-public.pem"; private static final String INVALID_PUBLIC_KEY_FILE_384 = "src/test/resources/ec384-key-public-invalid.pem"; @@ -49,9 +49,8 @@ public class ECDSAAlgorithmTest { private static final String PRIVATE_KEY_FILE_512 = "src/test/resources/ec512-key-private.pem"; private static final String PUBLIC_KEY_FILE_512 = "src/test/resources/ec512-key-public.pem"; private static final String INVALID_PUBLIC_KEY_FILE_512 = "src/test/resources/ec512-key-public-invalid.pem"; - - @Rule - public ExpectedException exception = ExpectedException.none(); + //Sign + private static final String ES256Header = "eyJhbGciOiJFUzI1NiJ9"; //JOSE Signatures obtained using Node 'jwa' lib: https://github.com/brianloveswords/node-jwa //DER Signatures obtained from source JOSE signature using 'ecdsa-sig-formatter' lib: https://github.com/Brightspace/node-ecdsa-sig-formatter @@ -59,6 +58,141 @@ public class ECDSAAlgorithmTest { //These tests use the default preferred SecurityProvider to handle ECDSA algorithms // Verify + private static final String ES384Header = "eyJhbGciOiJFUzM4NCJ9"; + private static final String ES512Header = "eyJhbGciOiJFUzUxMiJ9"; + private static final String auth0IssPayload = "eyJpc3MiOiJhdXRoMCJ9"; + private static final byte[] ES256HeaderBytes = ES256Header.getBytes(StandardCharsets.UTF_8); + private static final byte[] ES384HeaderBytes = ES384Header.getBytes(StandardCharsets.UTF_8); + private static final byte[] ES512HeaderBytes = ES512Header.getBytes(StandardCharsets.UTF_8); + private static final byte[] auth0IssPayloadBytes = auth0IssPayload.getBytes(StandardCharsets.UTF_8); + @Rule + public ExpectedException exception = ExpectedException.none(); + + //Test Helpers + static void assertValidJOSESignature(byte[] joseSignature, int numberSize, boolean withRPadding, boolean withSPadding) { + assertThat(joseSignature, is(Matchers.notNullValue())); + assertThat(numberSize, is(IsIn.oneOf(32, 48, 66))); + + assertThat(joseSignature.length, is(numberSize * 2)); + + byte[] rCopy = Arrays.copyOfRange(joseSignature, 0, numberSize); + byte[] sCopy = Arrays.copyOfRange(joseSignature, numberSize, numberSize * 2); + + byte[] rNumber = new byte[numberSize]; + byte[] sNumber = new byte[numberSize]; + Arrays.fill(rNumber, (byte) 0x11); + Arrays.fill(sNumber, (byte) 0x22); + if (withRPadding) { + rNumber[0] = (byte) 0; + } + if (withSPadding) { + sNumber[0] = (byte) 0; + } + assertThat(Arrays.equals(rNumber, rCopy), is(true)); + assertThat(Arrays.equals(sNumber, sCopy), is(true)); + } + + static byte[] createDERSignature(int numberSize, boolean withRPadding, boolean withSPadding) { + assertThat(numberSize, is(IsIn.oneOf(32, 48, 66))); + + int rLength = withRPadding ? numberSize - 1 : numberSize; + int sLength = withSPadding ? numberSize - 1 : numberSize; + int totalLength = 2 + (2 + rLength) + (2 + sLength); + + byte[] rNumber = new byte[rLength]; + byte[] sNumber = new byte[sLength]; + Arrays.fill(rNumber, (byte) 0x11); + Arrays.fill(sNumber, (byte) 0x22); + + byte[] derSignature; + int offset = 0; + if (totalLength > 0x7f) { + totalLength++; + derSignature = new byte[totalLength]; + //Start sequence and sign + derSignature[offset++] = (byte) 0x30; + derSignature[offset++] = (byte) 0x81; + } else { + derSignature = new byte[totalLength]; + //Start sequence + derSignature[offset++] = (byte) 0x30; + } + + //Sequence length + derSignature[offset++] = (byte) (totalLength - offset); + + //R number + derSignature[offset++] = (byte) 0x02; + derSignature[offset++] = (byte) rLength; + System.arraycopy(rNumber, 0, derSignature, offset, rLength); + offset += rLength; + + //S number + derSignature[offset++] = (byte) 0x02; + derSignature[offset++] = (byte) sLength; + System.arraycopy(sNumber, 0, derSignature, offset, sLength); + + return derSignature; + } + + static byte[] createJOSESignature(int numberSize, boolean withRPadding, boolean withSPadding) { + assertThat(numberSize, is(IsIn.oneOf(32, 48, 66))); + + byte[] rNumber = new byte[numberSize]; + byte[] sNumber = new byte[numberSize]; + Arrays.fill(rNumber, (byte) 0x11); + Arrays.fill(sNumber, (byte) 0x22); + if (withRPadding) { + rNumber[0] = (byte) 0; + } + if (withSPadding) { + sNumber[0] = (byte) 0; + } + byte[] joseSignature = new byte[numberSize * 2]; + System.arraycopy(rNumber, 0, joseSignature, 0, numberSize); + System.arraycopy(sNumber, 0, joseSignature, numberSize, numberSize); + return joseSignature; + } + + static void assertValidDERSignature(byte[] derSignature, int numberSize, boolean withRPadding, boolean withSPadding) { + assertThat(derSignature, is(Matchers.notNullValue())); + assertThat(numberSize, is(IsIn.oneOf(32, 48, 66))); + + int rLength = withRPadding ? numberSize - 1 : numberSize; + int sLength = withSPadding ? numberSize - 1 : numberSize; + int totalLength = 2 + (2 + rLength) + (2 + sLength); + int offset = 0; + + //Start sequence + assertThat(derSignature[offset++], is((byte) 0x30)); + if (totalLength > 0x7f) { + //Add sign before sequence length + totalLength++; + assertThat(derSignature[offset++], is((byte) 0x81)); + } + //Sequence length + assertThat(derSignature[offset++], is((byte) (totalLength - offset))); + + //R number + assertThat(derSignature[offset++], is((byte) 0x02)); + assertThat(derSignature[offset++], is((byte) rLength)); + byte[] rCopy = Arrays.copyOfRange(derSignature, offset, offset + rLength); + offset += rLength; + + //S number + assertThat(derSignature[offset++], is((byte) 0x02)); + assertThat(derSignature[offset++], is((byte) sLength)); + byte[] sCopy = Arrays.copyOfRange(derSignature, offset, offset + sLength); + + + byte[] rNumber = new byte[rLength]; + byte[] sNumber = new byte[sLength]; + Arrays.fill(rNumber, (byte) 0x11); + Arrays.fill(sNumber, (byte) 0x22); + assertThat(Arrays.equals(rNumber, rCopy), is(true)); + assertThat(Arrays.equals(sNumber, sCopy), is(true)); + assertThat(derSignature.length, is(totalLength)); + } @Test public void shouldPassECDSA256VerificationWithJOSESignature() throws Exception { @@ -461,7 +595,7 @@ public void shouldThrowOnVerifyWhenSignatureAlgorithmDoesNotExists() throws Exce exception.expectCause(isA(NoSuchAlgorithmException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class))) + when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class), (Provider) isNull())) .thenThrow(NoSuchAlgorithmException.class); ECPublicKey publicKey = mock(ECPublicKey.class); @@ -483,7 +617,7 @@ public void shouldThrowOnVerifyWhenThePublicKeyIsInvalid() throws Exception { exception.expectCause(isA(InvalidKeyException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class))) + when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class), (Provider) isNull())) .thenThrow(InvalidKeyException.class); ECPublicKey publicKey = mock(ECPublicKey.class); @@ -505,7 +639,7 @@ public void shouldThrowOnVerifyWhenTheSignatureIsNotPrepared() throws Exception exception.expectCause(isA(SignatureException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class))) + when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class), (Provider) isNull())) .thenThrow(SignatureException.class); ECPublicKey publicKey = mock(ECPublicKey.class); @@ -531,18 +665,6 @@ public void shouldThrowWhenSignatureNotValidBase64() throws Exception { algorithm.verify(JWT.decode(jwt)); } - //Sign - private static final String ES256Header = "eyJhbGciOiJFUzI1NiJ9"; - private static final String ES384Header = "eyJhbGciOiJFUzM4NCJ9"; - private static final String ES512Header = "eyJhbGciOiJFUzUxMiJ9"; - private static final String auth0IssPayload = "eyJpc3MiOiJhdXRoMCJ9"; - - private static final byte[] ES256HeaderBytes = ES256Header.getBytes(StandardCharsets.UTF_8); - private static final byte[] ES384HeaderBytes = ES384Header.getBytes(StandardCharsets.UTF_8); - private static final byte[] ES512HeaderBytes = ES512Header.getBytes(StandardCharsets.UTF_8); - private static final byte[] auth0IssPayloadBytes = auth0IssPayload.getBytes(StandardCharsets.UTF_8); - - @Test public void shouldDoECDSA256Signing() throws Exception { Algorithm algorithm = Algorithm.ECDSA256((ECKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); @@ -682,7 +804,6 @@ public void shouldDoECDSA512SigningWithBothKeys() throws Exception { algorithm.verify(JWT.decode(jwt)); } - @Test public void shouldDoECDSA512SigningWithProvidedPrivateKey() throws Exception { ECDSAKeyProvider provider = mock(ECDSAKeyProvider.class); @@ -729,7 +850,7 @@ public void shouldThrowOnSignWhenSignatureAlgorithmDoesNotExists() throws Except exception.expectCause(isA(NoSuchAlgorithmException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), any(byte[].class))) + when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(NoSuchAlgorithmException.class); ECPublicKey publicKey = mock(ECPublicKey.class); @@ -739,6 +860,38 @@ public void shouldThrowOnSignWhenSignatureAlgorithmDoesNotExists() throws Except algorithm.sign(ES256HeaderBytes, new byte[0]); } + @Test + public void shouldThrowOnSignWhenProviderDoesNotExists() throws Exception { + exception.expect(NoSuchProviderException.class); + exception.expectMessage("No provider named [some-provider] installed"); + + CryptoHelper crypto = mock(CryptoHelper.class); + when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), anyString())) + .thenThrow(NoSuchAlgorithmException.class); + + ECPublicKey publicKey = mock(ECPublicKey.class); + ECPrivateKey privateKey = mock(ECPrivateKey.class); + ECDSAKeyProvider provider = ECDSAAlgorithm.providerForKeys(publicKey, privateKey); + Algorithm algorithm = new ECDSAAlgorithm(crypto, "some-alg", "some-algorithm", 32, provider); + algorithm.sign(ES256HeaderBytes, new byte[0], "some-provider"); + } + + @Test + public void shouldThrowOnSignWhenProviderIsNull() throws Exception { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("providerName cannot be null"); + + CryptoHelper crypto = mock(CryptoHelper.class); + when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), anyString())) + .thenThrow(NoSuchAlgorithmException.class); + + ECPublicKey publicKey = mock(ECPublicKey.class); + ECPrivateKey privateKey = mock(ECPrivateKey.class); + ECDSAKeyProvider provider = ECDSAAlgorithm.providerForKeys(publicKey, privateKey); + Algorithm algorithm = new ECDSAAlgorithm(crypto, "some-alg", "some-algorithm", 32, provider); + algorithm.sign(ES256HeaderBytes, new byte[0], (String) null); + } + @Test public void shouldThrowOnSignWhenThePrivateKeyIsInvalid() throws Exception { exception.expect(SignatureGenerationException.class); @@ -746,7 +899,7 @@ public void shouldThrowOnSignWhenThePrivateKeyIsInvalid() throws Exception { exception.expectCause(isA(InvalidKeyException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), any(byte[].class))) + when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(InvalidKeyException.class); ECPublicKey publicKey = mock(ECPublicKey.class); @@ -763,7 +916,7 @@ public void shouldThrowOnSignWhenTheSignatureIsNotPrepared() throws Exception { exception.expectCause(isA(SignatureException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), any(byte[].class))) + when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(SignatureException.class); ECPublicKey publicKey = mock(ECPublicKey.class); @@ -863,6 +1016,142 @@ public void shouldSignAndVerifyWithECDSA256() throws Exception { } } + @Test + public void shouldSignAndVerifyWithProviderByName() throws Exception { + ECDSAAlgorithm algorithm256 = (ECDSAAlgorithm) Algorithm.ECDSA256( + (ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"), + (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); + String header256 = "eyJhbGciOiJFUzI1NiJ9"; + String body = "eyJpc3MiOiJhdXRoMCJ9"; + + for (int i = 0; i < 10; i++) { + String jwt = asJWT(algorithm256, header256, body, Security.getProvider("SunEC").getName()); + algorithm256.verify(JWT.decode(jwt), Security.getProvider("SunEC").getName()); + } + } + + @Test + public void shouldSignAndVerifyWithContentAndProviderByName() throws Exception { + ECDSAAlgorithm algorithm256 = (ECDSAAlgorithm) Algorithm.ECDSA256( + (ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"), + (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); + String header256 = "eyJhbGciOiJFUzI1NiJ9"; + String body = "eyJpc3MiOiJhdXRoMCJ9"; + + for (int i = 0; i < 10; i++) { + byte[] signatureBytes = algorithm256.sign( + header256.getBytes(StandardCharsets.UTF_8), + body.getBytes(StandardCharsets.UTF_8), + Security.getProvider("SunEC").getName()); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); + String jwt = String.format("%s.%s.%s", header256, body, jwtSignature); + + algorithm256.verify(JWT.decode(jwt), Security.getProvider("SunEC").getName()); + } + } + + @Test + public void shouldThrowOnSignHeaderAndPayloadWithNullProviderName() throws Exception { + ECPublicKey publicKey = (ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"); + ECDSAAlgorithm algorithm256 = (ECDSAAlgorithm) Algorithm.ECDSA256(publicKey, (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); + byte[] joseSignature = new byte[32 * 2 - 1]; + exception.expect(IllegalArgumentException.class); + exception.expectMessage("providerName cannot be null"); + + String header256 = "eyJhbGciOiJFUzI1NiJ9"; + String body = "eyJpc3MiOiJhdXRoMCJ9"; + + byte[] signatureBytes = algorithm256.sign( + header256.getBytes(StandardCharsets.UTF_8), + body.getBytes(StandardCharsets.UTF_8), + Security.getProvider("SunEC").getName()); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); + String jwt = String.format("%s.%s.%s", header256, body, jwtSignature); + + algorithm256.verify(JWT.decode(jwt), (String) null); + } + + @Test + public void shouldThrowOnSignContentWithNullProviderName() throws Exception { + ECPublicKey publicKey = (ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"); + ECDSAAlgorithm algorithm256 = (ECDSAAlgorithm) Algorithm.ECDSA256(publicKey, (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); + byte[] joseSignature = new byte[32 * 2 - 1]; + exception.expect(IllegalArgumentException.class); + exception.expectMessage("providerName cannot be null"); + + String header256 = "eyJhbGciOiJFUzI1NiJ9"; + String body = "eyJpc3MiOiJhdXRoMCJ9"; + + byte[] signatureBytes = algorithm256.sign( + (header256 + "." + body).getBytes(StandardCharsets.UTF_8), (String) null); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); + String jwt = String.format("%s.%s.%s", header256, body, jwtSignature); + + algorithm256.verify(JWT.decode(jwt), (String) null); + } + + @Test + public void shouldThrowOnSignContentWithNoSuchProvider() throws Exception { + ECPublicKey publicKey = (ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"); + ECDSAAlgorithm algorithm256 = (ECDSAAlgorithm) Algorithm.ECDSA256(publicKey, (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); + byte[] joseSignature = new byte[32 * 2 - 1]; + exception.expect(NoSuchProviderException.class); + exception.expectMessage("No provider named [some-provider] installed"); + + String header256 = "eyJhbGciOiJFUzI1NiJ9"; + String body = "eyJpc3MiOiJhdXRoMCJ9"; + + byte[] signatureBytes = algorithm256.sign( + (header256 + "." + body).getBytes(StandardCharsets.UTF_8), "some-provider"); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); + String jwt = String.format("%s.%s.%s", header256, body, jwtSignature); + + algorithm256.verify(JWT.decode(jwt), "some-provider"); + } + + @Test + public void shouldThrowOnVerifyContentWithNoSuchProvider() throws Exception { + ECPublicKey publicKey = (ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"); + ECDSAAlgorithm algorithm256 = (ECDSAAlgorithm) Algorithm.ECDSA256(publicKey, (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); + byte[] joseSignature = new byte[32 * 2 - 1]; + exception.expect(NoSuchProviderException.class); + exception.expectMessage("No provider named [some-provider] installed"); + + String header256 = "eyJhbGciOiJFUzI1NiJ9"; + String body = "eyJpc3MiOiJhdXRoMCJ9"; + + byte[] signatureBytes = algorithm256.sign( + (header256 + "." + body).getBytes(StandardCharsets.UTF_8), "SunEC"); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); + String jwt = String.format("%s.%s.%s", header256, body, jwtSignature); + + algorithm256.verify(JWT.decode(jwt), "some-provider"); + } + + @Test + public void shouldSignAndVerifyWithProviderByInstance() throws Exception { + ECDSAAlgorithm algorithm256 = (ECDSAAlgorithm) Algorithm.ECDSA256((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); + String header256 = "eyJhbGciOiJFUzI1NiJ9"; + String body = "eyJpc3MiOiJhdXRoMCJ9"; + + for (int i = 0; i < 10; i++) { + String jwt = asJWT(algorithm256, (header256 + "." + body).getBytes(StandardCharsets.UTF_8), Security.getProvider("SunEC")); + algorithm256.verify(JWT.decode(jwt), Security.getProvider("SunEC")); + } + } + + @Test + public void shouldSignContentAndVerifyWithProviderByName() throws Exception { + ECDSAAlgorithm algorithm256 = (ECDSAAlgorithm) Algorithm.ECDSA256((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); + String header256 = "eyJhbGciOiJFUzI1NiJ9"; + String body = "eyJpc3MiOiJhdXRoMCJ9"; + + for (int i = 0; i < 10; i++) { + String jwt = asJWT(algorithm256, (header256 + "." + body).getBytes(StandardCharsets.UTF_8), Security.getProvider("SunEC")); + algorithm256.verify(JWT.decode(jwt), "SunEC"); + } + } + @Test public void shouldSignAndVerifyWithECDSA384() throws Exception { ECDSAAlgorithm algorithm384 = (ECDSAAlgorithm) Algorithm.ECDSA384((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_384, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_384, "EC")); @@ -1037,133 +1326,6 @@ public void shouldDecodeECDSA512DER() throws Exception { assertValidJOSESignature(joseSignature, 66, true, true); } - - //Test Helpers - static void assertValidJOSESignature(byte[] joseSignature, int numberSize, boolean withRPadding, boolean withSPadding) { - assertThat(joseSignature, is(Matchers.notNullValue())); - assertThat(numberSize, is(IsIn.oneOf(32, 48, 66))); - - assertThat(joseSignature.length, is(numberSize * 2)); - - byte[] rCopy = Arrays.copyOfRange(joseSignature, 0, numberSize); - byte[] sCopy = Arrays.copyOfRange(joseSignature, numberSize, numberSize * 2); - - byte[] rNumber = new byte[numberSize]; - byte[] sNumber = new byte[numberSize]; - Arrays.fill(rNumber, (byte) 0x11); - Arrays.fill(sNumber, (byte) 0x22); - if (withRPadding) { - rNumber[0] = (byte) 0; - } - if (withSPadding) { - sNumber[0] = (byte) 0; - } - assertThat(Arrays.equals(rNumber, rCopy), is(true)); - assertThat(Arrays.equals(sNumber, sCopy), is(true)); - } - - static byte[] createDERSignature(int numberSize, boolean withRPadding, boolean withSPadding) { - assertThat(numberSize, is(IsIn.oneOf(32, 48, 66))); - - int rLength = withRPadding ? numberSize - 1 : numberSize; - int sLength = withSPadding ? numberSize - 1 : numberSize; - int totalLength = 2 + (2 + rLength) + (2 + sLength); - - byte[] rNumber = new byte[rLength]; - byte[] sNumber = new byte[sLength]; - Arrays.fill(rNumber, (byte) 0x11); - Arrays.fill(sNumber, (byte) 0x22); - - byte[] derSignature; - int offset = 0; - if (totalLength > 0x7f) { - totalLength++; - derSignature = new byte[totalLength]; - //Start sequence and sign - derSignature[offset++] = (byte) 0x30; - derSignature[offset++] = (byte) 0x81; - } else { - derSignature = new byte[totalLength]; - //Start sequence - derSignature[offset++] = (byte) 0x30; - } - - //Sequence length - derSignature[offset++] = (byte) (totalLength - offset); - - //R number - derSignature[offset++] = (byte) 0x02; - derSignature[offset++] = (byte) rLength; - System.arraycopy(rNumber, 0, derSignature, offset, rLength); - offset += rLength; - - //S number - derSignature[offset++] = (byte) 0x02; - derSignature[offset++] = (byte) sLength; - System.arraycopy(sNumber, 0, derSignature, offset, sLength); - - return derSignature; - } - - static byte[] createJOSESignature(int numberSize, boolean withRPadding, boolean withSPadding) { - assertThat(numberSize, is(IsIn.oneOf(32, 48, 66))); - - byte[] rNumber = new byte[numberSize]; - byte[] sNumber = new byte[numberSize]; - Arrays.fill(rNumber, (byte) 0x11); - Arrays.fill(sNumber, (byte) 0x22); - if (withRPadding) { - rNumber[0] = (byte) 0; - } - if (withSPadding) { - sNumber[0] = (byte) 0; - } - byte[] joseSignature = new byte[numberSize * 2]; - System.arraycopy(rNumber, 0, joseSignature, 0, numberSize); - System.arraycopy(sNumber, 0, joseSignature, numberSize, numberSize); - return joseSignature; - } - - static void assertValidDERSignature(byte[] derSignature, int numberSize, boolean withRPadding, boolean withSPadding) { - assertThat(derSignature, is(Matchers.notNullValue())); - assertThat(numberSize, is(IsIn.oneOf(32, 48, 66))); - - int rLength = withRPadding ? numberSize - 1 : numberSize; - int sLength = withSPadding ? numberSize - 1 : numberSize; - int totalLength = 2 + (2 + rLength) + (2 + sLength); - int offset = 0; - - //Start sequence - assertThat(derSignature[offset++], is((byte) 0x30)); - if (totalLength > 0x7f) { - //Add sign before sequence length - totalLength++; - assertThat(derSignature[offset++], is((byte) 0x81)); - } - //Sequence length - assertThat(derSignature[offset++], is((byte) (totalLength - offset))); - - //R number - assertThat(derSignature[offset++], is((byte) 0x02)); - assertThat(derSignature[offset++], is((byte) rLength)); - byte[] rCopy = Arrays.copyOfRange(derSignature, offset, offset + rLength); - offset += rLength; - - //S number - assertThat(derSignature[offset++], is((byte) 0x02)); - assertThat(derSignature[offset++], is((byte) sLength)); - byte[] sCopy = Arrays.copyOfRange(derSignature, offset, offset + sLength); - - - byte[] rNumber = new byte[rLength]; - byte[] sNumber = new byte[sLength]; - Arrays.fill(rNumber, (byte) 0x11); - Arrays.fill(sNumber, (byte) 0x22); - assertThat(Arrays.equals(rNumber, rCopy), is(true)); - assertThat(Arrays.equals(sNumber, sCopy), is(true)); - assertThat(derSignature.length, is(totalLength)); - } - @Test public void shouldBeEqualSignatureMethodDecodeResults() throws Exception { // signatures are not deterministic in value, so instead of directly comparing the signatures, @@ -1196,8 +1358,8 @@ public void shouldBeEqualSignatureMethodDecodeResults() throws Exception { /** * Test deprecated signing method error handling. * - * @see {@linkplain #shouldFailOnECDSA256SigningWhenProvidedPrivateKeyIsNull} * @throws Exception expected exception + * @see {@linkplain #shouldFailOnECDSA256SigningWhenProvidedPrivateKeyIsNull} */ @Test diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java index 3925eef3..14664a24 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java @@ -10,6 +10,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.ArgumentMatchers; import java.math.BigInteger; import java.nio.charset.StandardCharsets; @@ -478,7 +479,7 @@ public void shouldThrowOnVerifyWhenSignatureAlgorithmDoesNotExists() throws Exce exception.expectCause(isA(NoSuchAlgorithmException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class))) + when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(NoSuchAlgorithmException.class); ECPublicKey publicKey = mock(ECPublicKey.class); @@ -500,7 +501,7 @@ public void shouldThrowOnVerifyWhenThePublicKeyIsInvalid() throws Exception { exception.expectCause(isA(InvalidKeyException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class))) + when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(InvalidKeyException.class); ECPublicKey publicKey = mock(ECPublicKey.class); @@ -522,7 +523,7 @@ public void shouldThrowOnVerifyWhenTheSignatureIsNotPrepared() throws Exception exception.expectCause(isA(SignatureException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class))) + when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(SignatureException.class); ECPublicKey publicKey = mock(ECPublicKey.class); @@ -725,7 +726,7 @@ public void shouldThrowOnSignWhenSignatureAlgorithmDoesNotExists() throws Except exception.expectCause(isA(NoSuchAlgorithmException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), any(byte[].class))) + when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(NoSuchAlgorithmException.class); ECPublicKey publicKey = mock(ECPublicKey.class); @@ -742,7 +743,7 @@ public void shouldThrowOnSignWhenThePrivateKeyIsInvalid() throws Exception { exception.expectCause(isA(InvalidKeyException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), any(byte[].class))) + when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(InvalidKeyException.class); ECPublicKey publicKey = mock(ECPublicKey.class); @@ -759,7 +760,7 @@ public void shouldThrowOnSignWhenTheSignatureIsNotPrepared() throws Exception { exception.expectCause(isA(SignatureException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), any(byte[].class))) + when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(SignatureException.class); ECPublicKey publicKey = mock(ECPublicKey.class); diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java index 4a0269cc..81936df0 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java @@ -7,12 +7,14 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.ArgumentMatchers; import java.io.ByteArrayOutputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.security.Provider; import java.util.Arrays; import static com.auth0.jwt.algorithms.CryptoTestHelper.asJWT; @@ -143,7 +145,7 @@ public void shouldThrowOnVerifyWhenSignatureAlgorithmDoesNotExists() throws Exce exception.expectCause(isA(NoSuchAlgorithmException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.verifySignatureFor(anyString(), any(byte[].class), any(String.class), any(String.class), any(byte[].class))) + when(crypto.verifySignatureFor(anyString(), any(byte[].class), any(String.class), any(String.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(NoSuchAlgorithmException.class); Algorithm algorithm = new HMACAlgorithm(crypto, "some-alg", "some-algorithm", "secret".getBytes(StandardCharsets.UTF_8)); @@ -158,7 +160,7 @@ public void shouldThrowOnVerifyWhenTheSecretIsInvalid() throws Exception { exception.expectCause(isA(InvalidKeyException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.verifySignatureFor(anyString(), any(byte[].class), any(String.class), any(String.class), any(byte[].class))) + when(crypto.verifySignatureFor(anyString(), any(byte[].class), any(String.class), any(String.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(InvalidKeyException.class); Algorithm algorithm = new HMACAlgorithm(crypto, "some-alg", "some-algorithm", "secret".getBytes(StandardCharsets.UTF_8)); @@ -252,7 +254,7 @@ public void shouldThrowOnSignWhenSignatureAlgorithmDoesNotExists() throws Except exception.expectCause(isA(NoSuchAlgorithmException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.createSignatureFor(anyString(), any(byte[].class), any(byte[].class), any(byte[].class))) + when(crypto.createSignatureFor(anyString(), any(byte[].class), any(byte[].class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(NoSuchAlgorithmException.class); Algorithm algorithm = new HMACAlgorithm(crypto, "some-alg", "some-algorithm", "secret".getBytes(StandardCharsets.UTF_8)); @@ -266,7 +268,7 @@ public void shouldThrowOnSignWhenTheSecretIsInvalid() throws Exception { exception.expectCause(isA(InvalidKeyException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.createSignatureFor(anyString(), any(byte[].class), any(byte[].class), any(byte[].class))) + when(crypto.createSignatureFor(anyString(), any(byte[].class), any(byte[].class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(InvalidKeyException.class); Algorithm algorithm = new HMACAlgorithm(crypto, "some-alg", "some-algorithm", "secret".getBytes(StandardCharsets.UTF_8)); diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java index 115de64c..b8ab31d1 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java @@ -7,6 +7,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.ArgumentMatchers; import java.io.ByteArrayOutputStream; import java.security.*; @@ -214,7 +215,7 @@ public void shouldThrowWhenMacAlgorithmDoesNotExists() throws Exception { exception.expectCause(isA(NoSuchAlgorithmException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class))) + when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(NoSuchAlgorithmException.class); RSAPublicKey publicKey = mock(RSAPublicKey.class); @@ -232,7 +233,7 @@ public void shouldThrowWhenThePublicKeyIsInvalid() throws Exception { exception.expectCause(isA(InvalidKeyException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class))) + when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(InvalidKeyException.class); RSAPublicKey publicKey = mock(RSAPublicKey.class); @@ -250,7 +251,7 @@ public void shouldThrowWhenTheSignatureIsNotPrepared() throws Exception { exception.expectCause(isA(SignatureException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class))) + when(crypto.verifySignatureFor(anyString(), any(PublicKey.class), any(String.class), any(String.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(SignatureException.class); RSAPublicKey publicKey = mock(RSAPublicKey.class); @@ -467,7 +468,7 @@ public void shouldThrowOnSignWhenSignatureAlgorithmDoesNotExists() throws Except exception.expectCause(isA(NoSuchAlgorithmException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), any(byte[].class))) + when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(NoSuchAlgorithmException.class); RSAPublicKey publicKey = mock(RSAPublicKey.class); @@ -484,7 +485,7 @@ public void shouldThrowOnSignWhenThePrivateKeyIsInvalid() throws Exception { exception.expectCause(isA(InvalidKeyException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), any(byte[].class))) + when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(InvalidKeyException.class); RSAPublicKey publicKey = mock(RSAPublicKey.class); @@ -501,7 +502,7 @@ public void shouldThrowOnSignWhenTheSignatureIsNotPrepared() throws Exception { exception.expectCause(isA(SignatureException.class)); CryptoHelper crypto = mock(CryptoHelper.class); - when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), any(byte[].class))) + when(crypto.createSignatureFor(anyString(), any(PrivateKey.class), any(byte[].class), (Provider) ArgumentMatchers.isNull())) .thenThrow(SignatureException.class); RSAPublicKey publicKey = mock(RSAPublicKey.class);