Skip to content

Commit e4ff044

Browse files
committed
refactor algorithms creation
1 parent 4e8a44c commit e4ff044

20 files changed

Lines changed: 541 additions & 484 deletions

lib/src/main/java/com/auth0/jwtdecodejava/JWTDecoder.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.auth0.jwtdecodejava;
22

3-
import com.auth0.jwtdecodejava.algorithms.Algorithm;
43
import com.auth0.jwtdecodejava.exceptions.JWTDecodeException;
54
import com.auth0.jwtdecodejava.impl.JWTParser;
65
import com.auth0.jwtdecodejava.interfaces.Claim;
@@ -52,7 +51,7 @@ private void parseToken(String token) throws JWTDecodeException {
5251
}
5352

5453
@Override
55-
public Algorithm getAlgorithm() {
54+
public String getAlgorithm() {
5655
return header.getAlgorithm();
5756
}
5857

lib/src/main/java/com/auth0/jwtdecodejava/JWTVerifier.java

Lines changed: 11 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,41 @@
11
package com.auth0.jwtdecodejava;
22

33
import com.auth0.jwtdecodejava.algorithms.Algorithm;
4-
import com.auth0.jwtdecodejava.algorithms.HSAlgorithm;
5-
import com.auth0.jwtdecodejava.algorithms.NoneAlgorithm;
6-
import com.auth0.jwtdecodejava.algorithms.RSAlgorithm;
7-
import com.auth0.jwtdecodejava.exceptions.AlgorithmMismatchException;
8-
import com.auth0.jwtdecodejava.exceptions.InvalidClaimException;
9-
import com.auth0.jwtdecodejava.exceptions.JWTVerificationException;
10-
import com.auth0.jwtdecodejava.exceptions.SignatureVerificationException;
4+
import com.auth0.jwtdecodejava.exceptions.*;
115
import com.auth0.jwtdecodejava.impl.PublicClaims;
126
import com.auth0.jwtdecodejava.interfaces.JWT;
137

14-
import java.security.InvalidKeyException;
15-
import java.security.NoSuchAlgorithmException;
16-
import java.security.PublicKey;
17-
import java.security.SignatureException;
188
import java.util.Arrays;
199
import java.util.Date;
2010
import java.util.HashMap;
2111
import java.util.Map;
2212

23-
import static com.auth0.jwtdecodejava.algorithms.NoneAlgorithm.none;
24-
2513
/**
2614
* The JWTVerifier class holds the verify method to assert that a given Token has not only a proper JWT format, but also it's signature matches.
2715
*/
2816
public class JWTVerifier {
2917
private final Algorithm algorithm;
30-
private final String secret;
31-
private final PublicKey key;
3218
private final Map<String, Object> claims;
3319

34-
private JWTVerifier(Algorithm algorithm, String secret, PublicKey key) {
20+
private JWTVerifier(Algorithm algorithm) throws IllegalArgumentException {
21+
if (algorithm == null) {
22+
throw new IllegalArgumentException("The Algorithm cannot be null.");
23+
}
3524
this.algorithm = algorithm;
36-
this.key = key;
37-
this.secret = secret;
3825
this.claims = new HashMap<>();
3926
}
4027

41-
/**
42-
* Initialize a JWTVerifier instance using the Algorithm "none".
43-
*
44-
* @return a JWTVerifier instance to configure.
45-
*/
46-
public static JWTVerifier init() {
47-
return init(none, null, null);
48-
}
49-
5028
/**
5129
* Initialize a JWTVerifier instance using a HS Algorithm.
5230
*
5331
* @param algorithm a HSAlgorithm. Valid values are HS256, HS384, HS512.
54-
* @param secret to use when verifying the signature.
5532
* @return a JWTVerifier instance to configure.
5633
* @throws IllegalArgumentException if the provided algorithm is null or if the secret is null.
5734
*/
58-
public static JWTVerifier init(HSAlgorithm algorithm, String secret) throws IllegalArgumentException {
59-
return init(algorithm, null, secret);
35+
public static JWTVerifier init(Algorithm algorithm) throws IllegalArgumentException {
36+
return new JWTVerifier(algorithm);
6037
}
6138

62-
/**
63-
* Initialize a JWTVerifier instance using a RS Algorithm.
64-
*
65-
* @param algorithm a RSAlgorithm. Valid values are RS256, RS384, RS512.
66-
* @param publicKey to use when verifying the signature.
67-
* @return a JWTVerifier instance to configure.
68-
* @throws IllegalArgumentException if the provided algorithm is null or if the publicKey is null.
69-
*/
70-
public static JWTVerifier init(RSAlgorithm algorithm, PublicKey publicKey) throws IllegalArgumentException {
71-
return init(algorithm, publicKey, null);
72-
}
73-
74-
private static JWTVerifier init(Algorithm algorithm, PublicKey publicKey, String secret) throws IllegalArgumentException {
75-
if (algorithm == null) {
76-
throw new IllegalArgumentException("The Algorithm cannot be null.");
77-
}
78-
if (algorithm instanceof HSAlgorithm && secret == null) {
79-
throw new IllegalArgumentException(String.format("You can't use the %s algorithm without providing a valid Secret.", algorithm.name()));
80-
}
81-
if (algorithm instanceof RSAlgorithm && publicKey == null) {
82-
throw new IllegalArgumentException(String.format("You can't use the %s algorithm without providing a valid PublicKey.", algorithm.name()));
83-
}
84-
return new JWTVerifier(algorithm, secret, publicKey);
85-
}
8639

8740
/**
8841
* Require a specific Issuer ("iss") claim.
@@ -159,9 +112,10 @@ public JWTVerifier withJWTId(String jwtId) {
159112
*
160113
* @param token the String representation of the JWT.
161114
* @return a verified JWT.
115+
* @throws JWTDecodeException if any part of the Token contained an invalid JWT or JSON format.
162116
* @throws JWTVerificationException if any of the required contents inside the JWT is invalid.
163117
*/
164-
public JWT verify(String token) throws JWTVerificationException {
118+
public JWT verify(String token) throws JWTDecodeException, JWTVerificationException {
165119
JWT jwt = JWTDecoder.decode(token);
166120
verifyAlgorithm(jwt, algorithm);
167121
verifySignature(SignUtils.splitToken(token));
@@ -170,25 +124,11 @@ public JWT verify(String token) throws JWTVerificationException {
170124
}
171125

172126
private void verifySignature(String[] parts) throws SignatureVerificationException {
173-
if (algorithm instanceof HSAlgorithm) {
174-
try {
175-
SignUtils.verifyHS((HSAlgorithm) algorithm, parts, secret);
176-
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
177-
throw new SignatureVerificationException(algorithm, e);
178-
}
179-
} else if (algorithm instanceof RSAlgorithm) {
180-
try {
181-
SignUtils.verifyRS((RSAlgorithm) algorithm, parts, key);
182-
} catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException e) {
183-
throw new SignatureVerificationException(algorithm, e);
184-
}
185-
} else if (algorithm instanceof NoneAlgorithm && !parts[2].isEmpty()) {
186-
throw new SignatureVerificationException(algorithm);
187-
}
127+
algorithm.verify(parts);
188128
}
189129

190130
private void verifyAlgorithm(JWT jwt, Algorithm expectedAlgorithm) throws AlgorithmMismatchException {
191-
if (!expectedAlgorithm.equals(jwt.getAlgorithm())) {
131+
if (!expectedAlgorithm.getName().equals(jwt.getAlgorithm())) {
192132
throw new AlgorithmMismatchException("The provided Algorithm doesn't match the one defined in the JWT's Header.");
193133
}
194134
}
Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
package com.auth0.jwtdecodejava;
22

3-
import com.auth0.jwtdecodejava.algorithms.HSAlgorithm;
4-
import com.auth0.jwtdecodejava.algorithms.RSAlgorithm;
53
import com.auth0.jwtdecodejava.exceptions.JWTDecodeException;
64
import org.apache.commons.codec.binary.Base64;
75
import org.apache.commons.codec.binary.StringUtils;
86

9-
import javax.crypto.Mac;
10-
import javax.crypto.spec.SecretKeySpec;
117
import java.security.*;
128

139
class SignUtils {
@@ -54,43 +50,4 @@ static String[] splitToken(String token) throws JWTDecodeException {
5450
return parts;
5551
}
5652

57-
/**
58-
* Verify the given JWT parts using a specific HS Algorithm and a given secret.
59-
*
60-
* @param algorithm the HSAlgorithm to use. Must be one of HS256, HS384, or HS512.
61-
* @param jwtParts a valid array of size 3 representing the JWT parts.
62-
* @param secret the secret used when signing the token's content.
63-
* @return whether the Token's signature is valid or not.
64-
* @throws NoSuchAlgorithmException if the chosen algorithm isn't present.
65-
* @throws InvalidKeyException
66-
*/
67-
static boolean verifyHS(HSAlgorithm algorithm, String[] jwtParts, String secret) throws NoSuchAlgorithmException, InvalidKeyException {
68-
if (secret == null) {
69-
throw new IllegalArgumentException("The Secret cannot be null");
70-
}
71-
if (algorithm == null) {
72-
throw new IllegalArgumentException("The Algorithm must be one of HS256, HS384, or HS512.");
73-
}
74-
75-
Mac mac = Mac.getInstance(algorithm.describe());
76-
mac.init(new SecretKeySpec(secret.getBytes(), algorithm.describe()));
77-
String message = String.format("%s.%s", jwtParts[0], jwtParts[1]);
78-
byte[] result = mac.doFinal(message.getBytes());
79-
return MessageDigest.isEqual(result, Base64.decodeBase64(jwtParts[2]));
80-
}
81-
82-
static boolean verifyRS(RSAlgorithm algorithm, String[] jwtParts, PublicKey publicKey) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
83-
if (publicKey == null) {
84-
throw new IllegalArgumentException("The PublicKey cannot be null");
85-
}
86-
if (algorithm == null) {
87-
throw new IllegalArgumentException("The Algorithm must be one of RS256, RS384, or RS512.");
88-
}
89-
90-
final String content = String.format("%s.%s", jwtParts[0], jwtParts[1]);
91-
Signature s = Signature.getInstance(algorithm.describe());
92-
s.initVerify(publicKey);
93-
s.update(content.getBytes());
94-
return s.verify(Base64.decodeBase64(jwtParts[2]));
95-
}
9653
}
Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,114 @@
11
package com.auth0.jwtdecodejava.algorithms;
22

3+
import com.auth0.jwtdecodejava.exceptions.SignatureVerificationException;
4+
5+
import java.security.PublicKey;
6+
37
/**
48
* The Algorithm class represents an algorithm to be used in the Signing or Verification process of a Token.
59
*/
6-
public interface Algorithm {
10+
public abstract class Algorithm {
11+
12+
private final String name;
13+
private final String description;
714

815
/**
9-
* Getter for the description required when instantiating a Mac or Signature object.
16+
* Creates a new Algorithms instance using SHA256withRSA. Tokens specify this as "RS256".
1017
*
11-
* @return the algorithm description.
18+
* @param publicKey the key to use in the verify instance.
19+
* @return a valid RSA256 Algorithm.
20+
*/
21+
public static Algorithm RSA256(PublicKey publicKey) {
22+
return new RSAAlgorithm("RS256", "SHA256withRSA", publicKey);
23+
}
24+
25+
/**
26+
* Creates a new Algorithms instance using SHA384withRSA. Tokens specify this as "RS384".
27+
*
28+
* @param publicKey the key to use in the verify instance.
29+
* @return a valid RSA384 Algorithm.
30+
*/
31+
public static Algorithm RSA384(PublicKey publicKey) {
32+
return new RSAAlgorithm("RS384", "SHA384withRSA", publicKey);
33+
}
34+
35+
/**
36+
* Creates a new Algorithms instance using SHA512withRSA. Tokens specify this as "RS512".
37+
*
38+
* @param publicKey the key to use in the verify instance.
39+
* @return a valid RSA512 Algorithm.
1240
*/
13-
String describe();
41+
public static Algorithm RSA512(PublicKey publicKey) {
42+
return new RSAAlgorithm("RS512", "SHA512withRSA", publicKey);
43+
}
1444

1545
/**
16-
* Getter for the name of this algorithm as referenced in the JWT standard.
46+
* Creates a new Algorithms instance using HmacSHA256. Tokens specify this as "HS256".
47+
*
48+
* @param secret the secret to use in the verify instance.
49+
* @return a valid HMAC256 Algorithm.
50+
*/
51+
public static Algorithm HMAC256(String secret) {
52+
return new HMACAlgorithm("HS256", "HmacSHA256", secret);
53+
}
54+
55+
/**
56+
* Creates a new Algorithms instance using HmacSHA384. Tokens specify this as "HS384".
57+
*
58+
* @param secret the secret to use in the verify instance.
59+
* @return a valid HMAC384 Algorithm.
60+
*/
61+
public static Algorithm HMAC384(String secret) {
62+
return new HMACAlgorithm("HS384", "HmacSHA384", secret);
63+
}
64+
65+
/**
66+
* Creates a new Algorithms instance using HmacSHA512. Tokens specify this as "HS512".
67+
*
68+
* @param secret the secret to use in the verify instance.
69+
* @return a valid HMAC512 Algorithm.
70+
*/
71+
public static Algorithm HMAC512(String secret) {
72+
return new HMACAlgorithm("HS512", "HmacSHA512", secret);
73+
}
74+
75+
public static Algorithm none() {
76+
return new NoneAlgorithm();
77+
}
78+
79+
protected Algorithm(String name, String description) {
80+
this.name = name;
81+
this.description = description;
82+
}
83+
84+
/**
85+
* Getter for the name of this Algorithm, as defined in the JWT Standard. i.e. "HS256"
1786
*
1887
* @return the algorithm name.
1988
*/
20-
String name();
89+
public String getName() {
90+
return name;
91+
}
2192

93+
/**
94+
* Getter for the description of this Algorithm, required when instantiating a Mac or Signature object. i.e. "HmacSHA256"
95+
*
96+
* @return the algorithm description.
97+
*/
98+
String getDescription() {
99+
return description;
100+
}
101+
102+
@Override
103+
public String toString() {
104+
return description;
105+
}
106+
107+
/**
108+
* Verify the given JWT parts using this Algorithm instance.
109+
*
110+
* @param jwtParts a valid array of size 3 representing the JWT parts.
111+
* @throws SignatureVerificationException if the Token's Signature is invalid.
112+
*/
113+
public abstract void verify(String[] jwtParts) throws SignatureVerificationException;
22114
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.auth0.jwtdecodejava.algorithms;
2+
3+
import com.auth0.jwtdecodejava.exceptions.SignatureVerificationException;
4+
import org.apache.commons.codec.binary.Base64;
5+
6+
import javax.crypto.Mac;
7+
import javax.crypto.spec.SecretKeySpec;
8+
import java.security.InvalidKeyException;
9+
import java.security.MessageDigest;
10+
import java.security.NoSuchAlgorithmException;
11+
12+
class HMACAlgorithm extends Algorithm {
13+
14+
private final String secret;
15+
16+
HMACAlgorithm(String id, String algorithm, String secret) {
17+
super(id, algorithm);
18+
if (secret == null) {
19+
throw new IllegalArgumentException("The Secret cannot be null");
20+
}
21+
this.secret = secret;
22+
}
23+
24+
String getSecret() {
25+
return secret;
26+
}
27+
28+
@Override
29+
public void verify(String[] jwtParts) throws SignatureVerificationException {
30+
try {
31+
Mac mac = Mac.getInstance(getDescription());
32+
mac.init(new SecretKeySpec(secret.getBytes(), getDescription()));
33+
String message = String.format("%s.%s", jwtParts[0], jwtParts[1]);
34+
byte[] result = mac.doFinal(message.getBytes());
35+
boolean valid = MessageDigest.isEqual(result, Base64.decodeBase64(jwtParts[2]));
36+
if (!valid) {
37+
throw new SignatureVerificationException(this);
38+
}
39+
} catch (InvalidKeyException | NoSuchAlgorithmException e) {
40+
throw new SignatureVerificationException(this, e);
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)