Skip to content

Commit

Permalink
Performance improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
skjolber committed May 30, 2018
1 parent 7dcea42 commit dabb2e4
Show file tree
Hide file tree
Showing 29 changed files with 483 additions and 326 deletions.
26 changes: 25 additions & 1 deletion lib/src/main/java/com/auth0/jwt/JWT.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,35 @@

import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.impl.JWTParser;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;

@SuppressWarnings("WeakerAccess")
public abstract class JWT {
public class JWT {

private final JWTParser parser;

/**
* Constructs a new instance of the JWT library. Use this if you need to decode many JWT
* tokens on the fly and do not wish to instantiate a new parser for each invocation.
*/
public JWT() {
parser = new JWTParser();
}

/**
* Decode a given Json Web Token.
* <p>
* 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.
*
* @param token with jwt format as string.
* @return a decoded JWT.
* @throws JWTDecodeException if any part of the token contained an invalid jwt or JSON format of each of the jwt parts.
*/
public DecodedJWT decodeJwt(String token) throws JWTDecodeException {
return new JWTDecoder(parser, token);
}

/**
* Decode a given Json Web Token.
Expand Down
5 changes: 2 additions & 3 deletions lib/src/main/java/com/auth0/jwt/JWTCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,10 @@ private void addClaim(String name, Object value) {
private String sign() throws SignatureGenerationException {
String header = Base64.encodeBase64URLSafeString(headerJson.getBytes(StandardCharsets.UTF_8));
String payload = Base64.encodeBase64URLSafeString(payloadJson.getBytes(StandardCharsets.UTF_8));
String content = String.format("%s.%s", header, payload);

byte[] signatureBytes = algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8));
String signature = Base64.encodeBase64URLSafeString((signatureBytes));

return String.format("%s.%s", content, signature);
return String.format("%s.%s.%s", header, payload, signature);
}
}
5 changes: 4 additions & 1 deletion lib/src/main/java/com/auth0/jwt/JWTDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ final class JWTDecoder implements DecodedJWT {
private final Payload payload;

JWTDecoder(String jwt) throws JWTDecodeException {
this(new JWTParser(), jwt);
}

JWTDecoder(JWTParser converter, String jwt) throws JWTDecodeException {
parts = TokenUtils.splitToken(jwt);
final JWTParser converter = new JWTParser();
String headerJson;
String payloadJson;
try {
Expand Down
7 changes: 5 additions & 2 deletions lib/src/main/java/com/auth0/jwt/JWTVerifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.impl.JWTParser;
import com.auth0.jwt.impl.PublicClaims;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.Clock;
Expand All @@ -18,11 +19,13 @@ public final class JWTVerifier {
private final Algorithm algorithm;
final Map<String, Object> claims;
private final Clock clock;
private final JWTParser parser;

JWTVerifier(Algorithm algorithm, Map<String, Object> claims, Clock clock) {
this.algorithm = algorithm;
this.claims = Collections.unmodifiableMap(claims);
this.clock = clock;
this.parser = new JWTParser();
}

/**
Expand Down Expand Up @@ -50,7 +53,7 @@ public static class BaseVerification implements Verification {
}

this.algorithm = algorithm;
this.claims = new HashMap<>();
this.claims = new HashMap<>(48);
this.defaultLeeway = 0;
}

Expand Down Expand Up @@ -349,7 +352,7 @@ private void requireClaim(String name, Object value) {
* @throws InvalidClaimException if a claim contained a different value than the expected one.
*/
public DecodedJWT verify(String token) throws JWTVerificationException {
DecodedJWT jwt = JWT.decode(token);
DecodedJWT jwt = new JWTDecoder(parser, token);
verifyAlgorithm(jwt, algorithm);
algorithm.verify(jwt);
verifyClaims(jwt, claims);
Expand Down
22 changes: 15 additions & 7 deletions lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.auth0.jwt.interfaces.ECDSAKeyProvider;
import com.auth0.jwt.interfaces.RSAKeyProvider;

import java.io.UnsupportedEncodingException;
import java.security.interfaces.*;

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

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

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

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

/**
* 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.
* @return the signature in a base64 encoded array of bytes
* @throws SignatureGenerationException if the Key is invalid.
*/
public abstract byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException;

/**
* Sign the given content using this Algorithm instance.
*
* @param contentBytes an array of bytes representing the base64 encoded content to be verified against the signature.
* @return the signature in a base64 encoded array of bytes
* @throws SignatureGenerationException if the Key is invalid.
* @deprecated
*/
public abstract byte[] sign(byte[] contentBytes) throws SignatureGenerationException;

}
32 changes: 29 additions & 3 deletions lib/src/main/java/com/auth0/jwt/algorithms/CryptoHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,49 @@

class CryptoHelper {

boolean verifySignatureFor(String algorithm, byte[] secretBytes, byte[] contentBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException {
return MessageDigest.isEqual(createSignatureFor(algorithm, secretBytes, contentBytes), signatureBytes);
boolean verifySignatureFor(String algorithm, byte[] secretBytes, byte[] headerBytes, byte[] payloadBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException {
return MessageDigest.isEqual(createSignatureFor(algorithm, secretBytes, headerBytes, payloadBytes), signatureBytes);
}

byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] contentBytes) throws NoSuchAlgorithmException, InvalidKeyException {
final Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(secretBytes, algorithm));
return mac.doFinal(contentBytes);
}

byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] headerBytes, byte[] payloadBytes) throws NoSuchAlgorithmException, InvalidKeyException {
final Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(secretBytes, algorithm));
mac.update(headerBytes);
mac.update((byte)46);
return mac.doFinal(payloadBytes);
}

boolean verifySignatureFor(String algorithm, PublicKey publicKey, byte[] contentBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
final Signature s = Signature.getInstance(algorithm);
s.initVerify(publicKey);
s.update(contentBytes);
return s.verify(signatureBytes);
}

boolean verifySignatureFor(String algorithm, PublicKey publicKey, byte[] headerBytes, byte[] payloadBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
final Signature s = Signature.getInstance(algorithm);
s.initVerify(publicKey);
s.update(headerBytes);
s.update((byte)46);
s.update(payloadBytes);
return s.verify(signatureBytes);
}

byte[] createSignatureFor(String algorithm, PrivateKey privateKey, byte[] headerBytes, byte[] payloadBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
final Signature s = Signature.getInstance(algorithm);
s.initSign(privateKey);
s.update(headerBytes);
s.update((byte)46);
s.update(payloadBytes);
return s.sign();
}

byte[] createSignatureFor(String algorithm, PrivateKey privateKey, byte[] contentBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
final Signature s = Signature.getInstance(algorithm);
s.initSign(privateKey);
Expand Down
17 changes: 15 additions & 2 deletions lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,31 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException {
}

@Override
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
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, contentBytes);
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 {
try {
ECPrivateKey privateKey = keyProvider.getPrivateKey();
if (privateKey == null) {
throw new IllegalStateException("The given Private Key is null.");
}
return crypto.createSignatureFor(getDescription(), privateKey, contentBytes);
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) {
throw new SignatureGenerationException(this, e);
}
}

@Override
public String getSigningKeyId() {
Expand Down
23 changes: 14 additions & 9 deletions lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
import com.auth0.jwt.exceptions.SignatureGenerationException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.codec.binary.Base64;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
Expand All @@ -30,25 +28,24 @@ class HMACAlgorithm extends Algorithm {
this(new CryptoHelper(), id, algorithm, secretBytes);
}

HMACAlgorithm(String id, String algorithm, String secret) throws IllegalArgumentException, UnsupportedEncodingException {
HMACAlgorithm(String id, String algorithm, String secret) throws IllegalArgumentException {
this(new CryptoHelper(), id, algorithm, getSecretBytes(secret));
}

//Visible for testing
static byte[] getSecretBytes(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
static byte[] getSecretBytes(String secret) throws IllegalArgumentException {
if (secret == null) {
throw new IllegalArgumentException("The Secret cannot be null");
}
return secret.getBytes(CharEncoding.UTF_8);
return secret.getBytes(StandardCharsets.UTF_8);
}

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

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

@Override
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException {
try {
return crypto.createSignatureFor(getDescription(), secret, contentBytes);
return crypto.createSignatureFor(getDescription(), secret, headerBytes, payloadBytes);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new SignatureGenerationException(this, e);
}
}

@Override
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
try {
return crypto.createSignatureFor(getDescription(), secret, contentBytes);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new SignatureGenerationException(this, e);
}
}
}
5 changes: 5 additions & 0 deletions lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException {
}
}

@Override
public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException {
return new byte[0];
}

@Override
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
return new byte[0];
Expand Down
18 changes: 16 additions & 2 deletions lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,14 @@ class RSAAlgorithm extends Algorithm {

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

try {
RSAPublicKey publicKey = keyProvider.getPublicKeyById(jwt.getKeyId());
if (publicKey == null) {
throw new IllegalStateException("The given Public Key is null.");
}
boolean valid = crypto.verifySignatureFor(getDescription(), publicKey, contentBytes, signatureBytes);
boolean valid = crypto.verifySignatureFor(getDescription(), publicKey, jwt.getHeader().getBytes(StandardCharsets.UTF_8), jwt.getPayload().getBytes(StandardCharsets.UTF_8), signatureBytes);
if (!valid) {
throw new SignatureVerificationException(this);
}
Expand All @@ -51,6 +50,20 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException {
}
}


@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);
}
}

@Override
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
try {
Expand All @@ -64,6 +77,7 @@ public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
}
}


@Override
public String getSigningKeyId() {
return keyProvider.getPrivateKeyId();
Expand Down
Loading

0 comments on commit dabb2e4

Please sign in to comment.