Skip to content

Commit

Permalink
auth0#597 Add support for specifying provider without breaking changes
Browse files Browse the repository at this point in the history
* No tests were changed and still compile, so the API is unchanged
* ProviderName is only checked ate crypto helper. If null, then the previous code still stands. Otherwise it is used when getting an instance of java.security.Signature
* Hope you find this work useful
  • Loading branch information
aps-augentictech committed Jul 12, 2022
1 parent 84dc660 commit 472a5b7
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 24 deletions.
46 changes: 34 additions & 12 deletions lib/src/main/java/com/auth0/jwt/JWTCreator.java
Expand Up @@ -540,6 +540,30 @@ private static boolean isBasicType(Object value) {
* or there was a problem with the signing key.
*/
public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException {

return this.sign(algorithm, (String) null);
}

private void assertNonNull(String name) {
if (name == null) {
throw new IllegalArgumentException("The Custom Claim's name can't be null.");
}
}

private void addClaim(String name, Object value) {
payloadClaims.put(name, value);
}

/**
* 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, String providerName) throws IllegalArgumentException, JWTCreationException {
if (algorithm == null) {
throw new IllegalArgumentException("The Algorithm cannot be null.");
}
Expand All @@ -551,30 +575,28 @@ public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCrea
if (signingKeyId != null) {
withKeyId(signingKeyId);
}
return new JWTCreator(algorithm, headerClaims, payloadClaims).sign();
}

private void assertNonNull(String name) {
if (name == null) {
throw new IllegalArgumentException("The Custom Claim's name can't be null.");
}
}

private void addClaim(String name, Object value) {
payloadClaims.put(name, value);
return new JWTCreator(algorithm, headerClaims, payloadClaims).sign(providerName);
}
}

private String sign() throws SignatureGenerationException {
return sign((String) null);
}


// Added methods to support specifying provider

private String sign(String providerName) 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));
payload.getBytes(StandardCharsets.UTF_8), providerName);
String signature = Base64.getUrlEncoder().withoutPadding().encodeToString((signatureBytes));

return String.format("%s.%s.%s", header, payload, signature);
}

}
34 changes: 33 additions & 1 deletion lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java
Expand Up @@ -398,7 +398,39 @@ 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 abstract byte[] sign(byte[] contentBytes) throws SignatureGenerationException;

/**
* 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.
*/
public byte[] sign(byte[] headerBytes, byte[] payloadBytes, String providerName) 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, providerName);
}

/**
* 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 name
* @return the signature in a base64 encoded array of bytes
* @throws SignatureGenerationException if the Key is invalid.
*/
public abstract byte[] sign(byte[] contentBytes, String providerName) throws SignatureGenerationException;
}
40 changes: 37 additions & 3 deletions lib/src/main/java/com/auth0/jwt/algorithms/CryptoHelper.java
Expand Up @@ -131,7 +131,35 @@ byte[] createSignatureFor(
byte[] headerBytes,
byte[] payloadBytes
) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
final Signature s = Signature.getInstance(algorithm);
return this.createSignatureFor(algorithm,privateKey, headerBytes, payloadBytes, (String) 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, SignatureException {
final Signature s;
try {
s = providerName != null ? Signature.getInstance(algorithm, providerName) : Signature.getInstance(algorithm);
} catch (NoSuchProviderException e) {
throw new SignatureException("Unable to create signature with given provider", e);
}
s.initSign(privateKey);
s.update(headerBytes);
s.update(JWT_PART_SEPARATOR);
Expand Down Expand Up @@ -198,9 +226,15 @@ byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] contentBy
byte[] createSignatureFor(
String algorithm,
PrivateKey privateKey,
byte[] contentBytes
byte[] contentBytes,
String providerName
) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
final Signature s = Signature.getInstance(algorithm);
final Signature s;
try {
s = providerName != null ? Signature.getInstance(algorithm, providerName) : Signature.getInstance(algorithm);
} catch (NoSuchProviderException e) {
throw new SignatureException("Unable to use given provider to compute signature.", e);
}
s.initSign(privateKey);
s.update(contentBytes);
return s.sign();
Expand Down
13 changes: 9 additions & 4 deletions lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java
Expand Up @@ -63,27 +63,32 @@ 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 this.sign(contentBytes, (String) null);
}

@Override
public byte[] sign(byte[] headerBytes, byte[] payloadBytes, String providerName) 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);
byte[] signature = crypto.createSignatureFor(getDescription(), privateKey, headerBytes, payloadBytes, providerName);
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, String providerName) 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, providerName);
return DERToJOSE(signature);
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) {
throw new SignatureGenerationException(this, e);
Expand Down
9 changes: 9 additions & 0 deletions lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java
Expand Up @@ -78,4 +78,13 @@ public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
throw new SignatureGenerationException(this, e);
}
}

/**
* This method does not take the provider name into consideration for computing the HMAC
* @param providerName the cryptographic provider name
*/
@Override
public byte[] sign(byte[] contentBytes, String providerName) throws SignatureGenerationException {
return this.sign(contentBytes);
}
}
5 changes: 5 additions & 0 deletions lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java
Expand Up @@ -33,4 +33,9 @@ public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGene
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
return new byte[0];
}

@Override
public byte[] sign(byte[] contentBytes, String providerName) throws SignatureGenerationException {
return new byte[0];
}
}
13 changes: 9 additions & 4 deletions lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java
Expand Up @@ -58,26 +58,31 @@ 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 this.sign(contentBytes, (String) null);
}

@Override
public byte[] sign(byte[] headerBytes, byte[] payloadBytes, String providerName) 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);
return crypto.createSignatureFor(getDescription(), privateKey, headerBytes, payloadBytes, providerName);
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) {
throw new SignatureGenerationException(this, e);
}
}

@Override
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
public byte[] sign(byte[] contentBytes, String providerName) 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, providerName);
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) {
throw new SignatureGenerationException(this, e);
}
Expand Down

0 comments on commit 472a5b7

Please sign in to comment.