Skip to content

Commit 66e7263

Browse files
committed
Performance improvements
1 parent 7dcea42 commit 66e7263

29 files changed

+488
-326
lines changed

lib/src/main/java/com/auth0/jwt/JWT.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,35 @@
22

33
import com.auth0.jwt.algorithms.Algorithm;
44
import com.auth0.jwt.exceptions.JWTDecodeException;
5+
import com.auth0.jwt.impl.JWTParser;
56
import com.auth0.jwt.interfaces.DecodedJWT;
67
import com.auth0.jwt.interfaces.Verification;
78

89
@SuppressWarnings("WeakerAccess")
9-
public abstract class JWT {
10+
public class JWT {
11+
12+
private final JWTParser parser;
13+
14+
/**
15+
* Constructs a new instance of the JWT library. Use this if you need to decode many JWT
16+
* tokens on the fly and do not wish to instantiate a new parser for each invocation.
17+
*/
18+
public JWT() {
19+
parser = new JWTParser();
20+
}
21+
22+
/**
23+
* Decode a given Json Web Token.
24+
* <p>
25+
* Note that this method <b>doesn't verify the token's signature!</b> Use it only if you trust the token or you already verified it.
26+
*
27+
* @param token with jwt format as string.
28+
* @return a decoded JWT.
29+
* @throws JWTDecodeException if any part of the token contained an invalid jwt or JSON format of each of the jwt parts.
30+
*/
31+
public DecodedJWT decodeJwt(String token) throws JWTDecodeException {
32+
return new JWTDecoder(parser, token);
33+
}
1034

1135
/**
1236
* Decode a given Json Web Token.

lib/src/main/java/com/auth0/jwt/JWTCreator.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,11 +329,10 @@ private void addClaim(String name, Object value) {
329329
private String sign() throws SignatureGenerationException {
330330
String header = Base64.encodeBase64URLSafeString(headerJson.getBytes(StandardCharsets.UTF_8));
331331
String payload = Base64.encodeBase64URLSafeString(payloadJson.getBytes(StandardCharsets.UTF_8));
332-
String content = String.format("%s.%s", header, payload);
333332

334-
byte[] signatureBytes = algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
333+
byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8));
335334
String signature = Base64.encodeBase64URLSafeString((signatureBytes));
336335

337-
return String.format("%s.%s", content, signature);
336+
return String.format("%s.%s.%s", header, payload, signature);
338337
}
339338
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ final class JWTDecoder implements DecodedJWT {
2424
private final Payload payload;
2525

2626
JWTDecoder(String jwt) throws JWTDecodeException {
27+
this(new JWTParser(), jwt);
28+
}
29+
30+
JWTDecoder(JWTParser converter, String jwt) throws JWTDecodeException {
2731
parts = TokenUtils.splitToken(jwt);
28-
final JWTParser converter = new JWTParser();
2932
String headerJson;
3033
String payloadJson;
3134
try {

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.auth0.jwt.algorithms.Algorithm;
44
import com.auth0.jwt.exceptions.*;
5+
import com.auth0.jwt.impl.JWTParser;
56
import com.auth0.jwt.impl.PublicClaims;
67
import com.auth0.jwt.interfaces.Claim;
78
import com.auth0.jwt.interfaces.Clock;
@@ -18,11 +19,13 @@ public final class JWTVerifier {
1819
private final Algorithm algorithm;
1920
final Map<String, Object> claims;
2021
private final Clock clock;
22+
private final JWTParser parser;
2123

2224
JWTVerifier(Algorithm algorithm, Map<String, Object> claims, Clock clock) {
2325
this.algorithm = algorithm;
2426
this.claims = Collections.unmodifiableMap(claims);
2527
this.clock = clock;
28+
this.parser = new JWTParser();
2629
}
2730

2831
/**
@@ -50,7 +53,7 @@ public static class BaseVerification implements Verification {
5053
}
5154

5255
this.algorithm = algorithm;
53-
this.claims = new HashMap<>();
56+
this.claims = new HashMap<>(48);
5457
this.defaultLeeway = 0;
5558
}
5659

@@ -349,7 +352,7 @@ private void requireClaim(String name, Object value) {
349352
* @throws InvalidClaimException if a claim contained a different value than the expected one.
350353
*/
351354
public DecodedJWT verify(String token) throws JWTVerificationException {
352-
DecodedJWT jwt = JWT.decode(token);
355+
DecodedJWT jwt = new JWTDecoder(parser, token);
353356
verifyAlgorithm(jwt, algorithm);
354357
algorithm.verify(jwt);
355358
verifyClaims(jwt, claims);

lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import com.auth0.jwt.interfaces.ECDSAKeyProvider;
77
import com.auth0.jwt.interfaces.RSAKeyProvider;
88

9-
import java.io.UnsupportedEncodingException;
109
import java.security.interfaces.*;
1110

1211
/**
@@ -138,9 +137,8 @@ public static Algorithm RSA512(RSAKey key) throws IllegalArgumentException {
138137
* @param secret the secret to use in the verify or signing instance.
139138
* @return a valid HMAC256 Algorithm.
140139
* @throws IllegalArgumentException if the provided Secret is null.
141-
* @throws UnsupportedEncodingException if the current Java platform implementation doesn't support the UTF-8 character encoding.
142140
*/
143-
public static Algorithm HMAC256(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
141+
public static Algorithm HMAC256(String secret) throws IllegalArgumentException {
144142
return new HMACAlgorithm("HS256", "HmacSHA256", secret);
145143
}
146144

@@ -150,9 +148,8 @@ public static Algorithm HMAC256(String secret) throws IllegalArgumentException,
150148
* @param secret the secret to use in the verify or signing instance.
151149
* @return a valid HMAC384 Algorithm.
152150
* @throws IllegalArgumentException if the provided Secret is null.
153-
* @throws UnsupportedEncodingException if the current Java platform implementation doesn't support the UTF-8 character encoding.
154151
*/
155-
public static Algorithm HMAC384(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
152+
public static Algorithm HMAC384(String secret) throws IllegalArgumentException {
156153
return new HMACAlgorithm("HS384", "HmacSHA384", secret);
157154
}
158155

@@ -162,9 +159,8 @@ public static Algorithm HMAC384(String secret) throws IllegalArgumentException,
162159
* @param secret the secret to use in the verify or signing instance.
163160
* @return a valid HMAC512 Algorithm.
164161
* @throws IllegalArgumentException if the provided Secret is null.
165-
* @throws UnsupportedEncodingException if the current Java platform implementation doesn't support the UTF-8 character encoding.
166162
*/
167-
public static Algorithm HMAC512(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
163+
public static Algorithm HMAC512(String secret) throws IllegalArgumentException {
168164
return new HMACAlgorithm("HS512", "HmacSHA512", secret);
169165
}
170166

@@ -365,12 +361,24 @@ public String toString() {
365361
*/
366362
public abstract void verify(DecodedJWT jwt) throws SignatureVerificationException;
367363

364+
/**
365+
* Sign the given content using this Algorithm instance.
366+
*
367+
* @param headerBytes an array of bytes representing the base64 encoded header content to be verified against the signature.
368+
* @param payloadBytes an array of bytes representing the base64 encoded payload content to be verified against the signature.
369+
* @return the signature in a base64 encoded array of bytes
370+
* @throws SignatureGenerationException if the Key is invalid.
371+
*/
372+
public abstract byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException;
373+
368374
/**
369375
* Sign the given content using this Algorithm instance.
370376
*
371377
* @param contentBytes an array of bytes representing the base64 encoded content to be verified against the signature.
372378
* @return the signature in a base64 encoded array of bytes
373379
* @throws SignatureGenerationException if the Key is invalid.
380+
* @deprecated
374381
*/
375382
public abstract byte[] sign(byte[] contentBytes) throws SignatureGenerationException;
383+
376384
}

lib/src/main/java/com/auth0/jwt/algorithms/CryptoHelper.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,52 @@
55
import java.security.*;
66

77
class CryptoHelper {
8+
9+
private static final byte DOT = (byte)46;
810

9-
boolean verifySignatureFor(String algorithm, byte[] secretBytes, byte[] contentBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException {
10-
return MessageDigest.isEqual(createSignatureFor(algorithm, secretBytes, contentBytes), signatureBytes);
11+
boolean verifySignatureFor(String algorithm, byte[] secretBytes, byte[] headerBytes, byte[] payloadBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException {
12+
return MessageDigest.isEqual(createSignatureFor(algorithm, secretBytes, headerBytes, payloadBytes), signatureBytes);
1113
}
12-
14+
1315
byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] contentBytes) throws NoSuchAlgorithmException, InvalidKeyException {
1416
final Mac mac = Mac.getInstance(algorithm);
1517
mac.init(new SecretKeySpec(secretBytes, algorithm));
1618
return mac.doFinal(contentBytes);
1719
}
1820

21+
byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] headerBytes, byte[] payloadBytes) throws NoSuchAlgorithmException, InvalidKeyException {
22+
final Mac mac = Mac.getInstance(algorithm);
23+
mac.init(new SecretKeySpec(secretBytes, algorithm));
24+
mac.update(headerBytes);
25+
mac.update(DOT);
26+
return mac.doFinal(payloadBytes);
27+
}
28+
1929
boolean verifySignatureFor(String algorithm, PublicKey publicKey, byte[] contentBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
2030
final Signature s = Signature.getInstance(algorithm);
2131
s.initVerify(publicKey);
2232
s.update(contentBytes);
2333
return s.verify(signatureBytes);
2434
}
2535

36+
boolean verifySignatureFor(String algorithm, PublicKey publicKey, byte[] headerBytes, byte[] payloadBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
37+
final Signature s = Signature.getInstance(algorithm);
38+
s.initVerify(publicKey);
39+
s.update(headerBytes);
40+
s.update(DOT);
41+
s.update(payloadBytes);
42+
return s.verify(signatureBytes);
43+
}
44+
45+
byte[] createSignatureFor(String algorithm, PrivateKey privateKey, byte[] headerBytes, byte[] payloadBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
46+
final Signature s = Signature.getInstance(algorithm);
47+
s.initSign(privateKey);
48+
s.update(headerBytes);
49+
s.update(DOT);
50+
s.update(payloadBytes);
51+
return s.sign();
52+
}
53+
2654
byte[] createSignatureFor(String algorithm, PrivateKey privateKey, byte[] contentBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
2755
final Signature s = Signature.getInstance(algorithm);
2856
s.initSign(privateKey);

lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,31 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException {
5555
}
5656

5757
@Override
58-
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
58+
public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException {
5959
try {
6060
ECPrivateKey privateKey = keyProvider.getPrivateKey();
6161
if (privateKey == null) {
6262
throw new IllegalStateException("The given Private Key is null.");
6363
}
64-
byte[] signature = crypto.createSignatureFor(getDescription(), privateKey, contentBytes);
64+
byte[] signature = crypto.createSignatureFor(getDescription(), privateKey, headerBytes, payloadBytes);
6565
return DERToJOSE(signature);
6666
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) {
6767
throw new SignatureGenerationException(this, e);
6868
}
6969
}
70+
71+
@Override
72+
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
73+
try {
74+
ECPrivateKey privateKey = keyProvider.getPrivateKey();
75+
if (privateKey == null) {
76+
throw new IllegalStateException("The given Private Key is null.");
77+
}
78+
return crypto.createSignatureFor(getDescription(), privateKey, contentBytes);
79+
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) {
80+
throw new SignatureGenerationException(this, e);
81+
}
82+
}
7083

7184
@Override
7285
public String getSigningKeyId() {

lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
import com.auth0.jwt.exceptions.SignatureGenerationException;
44
import com.auth0.jwt.exceptions.SignatureVerificationException;
55
import com.auth0.jwt.interfaces.DecodedJWT;
6-
import org.apache.commons.codec.CharEncoding;
76
import org.apache.commons.codec.binary.Base64;
87

9-
import java.io.UnsupportedEncodingException;
108
import java.nio.charset.StandardCharsets;
119
import java.security.InvalidKeyException;
1210
import java.security.NoSuchAlgorithmException;
@@ -30,25 +28,24 @@ class HMACAlgorithm extends Algorithm {
3028
this(new CryptoHelper(), id, algorithm, secretBytes);
3129
}
3230

33-
HMACAlgorithm(String id, String algorithm, String secret) throws IllegalArgumentException, UnsupportedEncodingException {
31+
HMACAlgorithm(String id, String algorithm, String secret) throws IllegalArgumentException {
3432
this(new CryptoHelper(), id, algorithm, getSecretBytes(secret));
3533
}
3634

3735
//Visible for testing
38-
static byte[] getSecretBytes(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
36+
static byte[] getSecretBytes(String secret) throws IllegalArgumentException {
3937
if (secret == null) {
4038
throw new IllegalArgumentException("The Secret cannot be null");
4139
}
42-
return secret.getBytes(CharEncoding.UTF_8);
40+
return secret.getBytes(StandardCharsets.UTF_8);
4341
}
4442

4543
@Override
4644
public void verify(DecodedJWT jwt) throws SignatureVerificationException {
47-
byte[] contentBytes = String.format("%s.%s", jwt.getHeader(), jwt.getPayload()).getBytes(StandardCharsets.UTF_8);
4845
byte[] signatureBytes = Base64.decodeBase64(jwt.getSignature());
4946

5047
try {
51-
boolean valid = crypto.verifySignatureFor(getDescription(), secret, contentBytes, signatureBytes);
48+
boolean valid = crypto.verifySignatureFor(getDescription(), secret, jwt.getHeader().getBytes(StandardCharsets.UTF_8), jwt.getPayload().getBytes(StandardCharsets.UTF_8) ,signatureBytes);
5249
if (!valid) {
5350
throw new SignatureVerificationException(this);
5451
}
@@ -58,12 +55,20 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException {
5855
}
5956

6057
@Override
61-
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
58+
public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException {
6259
try {
63-
return crypto.createSignatureFor(getDescription(), secret, contentBytes);
60+
return crypto.createSignatureFor(getDescription(), secret, headerBytes, payloadBytes);
6461
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
6562
throw new SignatureGenerationException(this, e);
6663
}
6764
}
6865

66+
@Override
67+
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
68+
try {
69+
return crypto.createSignatureFor(getDescription(), secret, contentBytes);
70+
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
71+
throw new SignatureGenerationException(this, e);
72+
}
73+
}
6974
}

lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException {
1919
}
2020
}
2121

22+
@Override
23+
public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException {
24+
return new byte[0];
25+
}
26+
2227
@Override
2328
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
2429
return new byte[0];

lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,14 @@ class RSAAlgorithm extends Algorithm {
3434

3535
@Override
3636
public void verify(DecodedJWT jwt) throws SignatureVerificationException {
37-
byte[] contentBytes = String.format("%s.%s", jwt.getHeader(), jwt.getPayload()).getBytes(StandardCharsets.UTF_8);
3837
byte[] signatureBytes = Base64.decodeBase64(jwt.getSignature());
3938

4039
try {
4140
RSAPublicKey publicKey = keyProvider.getPublicKeyById(jwt.getKeyId());
4241
if (publicKey == null) {
4342
throw new IllegalStateException("The given Public Key is null.");
4443
}
45-
boolean valid = crypto.verifySignatureFor(getDescription(), publicKey, contentBytes, signatureBytes);
44+
boolean valid = crypto.verifySignatureFor(getDescription(), publicKey, jwt.getHeader().getBytes(StandardCharsets.UTF_8), jwt.getPayload().getBytes(StandardCharsets.UTF_8), signatureBytes);
4645
if (!valid) {
4746
throw new SignatureVerificationException(this);
4847
}
@@ -51,6 +50,20 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException {
5150
}
5251
}
5352

53+
54+
@Override
55+
public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException {
56+
try {
57+
RSAPrivateKey privateKey = keyProvider.getPrivateKey();
58+
if (privateKey == null) {
59+
throw new IllegalStateException("The given Private Key is null.");
60+
}
61+
return crypto.createSignatureFor(getDescription(), privateKey, headerBytes, payloadBytes);
62+
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) {
63+
throw new SignatureGenerationException(this, e);
64+
}
65+
}
66+
5467
@Override
5568
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
5669
try {
@@ -64,6 +77,7 @@ public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
6477
}
6578
}
6679

80+
6781
@Override
6882
public String getSigningKeyId() {
6983
return keyProvider.getPrivateKeyId();

0 commit comments

Comments
 (0)