mirror of https://github.com/jwtk/jjwt.git
334: key strength assertions and signature validation. Resolves #334
This commit is contained in:
parent
45f83d0cb1
commit
9189253668
|
@ -17,6 +17,7 @@ package io.jsonwebtoken;
|
|||
|
||||
import io.jsonwebtoken.io.Decoder;
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
|
||||
import java.security.Key;
|
||||
import java.util.Date;
|
||||
|
|
|
@ -16,6 +16,16 @@
|
|||
package io.jsonwebtoken;
|
||||
|
||||
import io.jsonwebtoken.lang.RuntimeEnvironment;
|
||||
import io.jsonwebtoken.security.InvalidKeyException;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
import io.jsonwebtoken.security.WeakKeyException;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.security.Key;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.interfaces.ECKey;
|
||||
import java.security.interfaces.RSAKey;
|
||||
|
||||
/**
|
||||
* Type-safe representation of standard JWT signature algorithm names as defined in the
|
||||
|
@ -25,85 +35,104 @@ import io.jsonwebtoken.lang.RuntimeEnvironment;
|
|||
*/
|
||||
public enum SignatureAlgorithm {
|
||||
|
||||
/** JWA name for {@code No digital signature or MAC performed} */
|
||||
NONE("none", "No digital signature or MAC performed", "None", null, false),
|
||||
/**
|
||||
* JWA name for {@code No digital signature or MAC performed}
|
||||
*/
|
||||
NONE("none", "No digital signature or MAC performed", "None", null, false, 0, 0),
|
||||
|
||||
/** JWA algorithm name for {@code HMAC using SHA-256} */
|
||||
HS256("HS256", "HMAC using SHA-256", "HMAC", "HmacSHA256", true),
|
||||
/**
|
||||
* JWA algorithm name for {@code HMAC using SHA-256}
|
||||
*/
|
||||
HS256("HS256", "HMAC using SHA-256", "HMAC", "HmacSHA256", true, 256, 256),
|
||||
|
||||
/** JWA algorithm name for {@code HMAC using SHA-384} */
|
||||
HS384("HS384", "HMAC using SHA-384", "HMAC", "HmacSHA384", true),
|
||||
/**
|
||||
* JWA algorithm name for {@code HMAC using SHA-384}
|
||||
*/
|
||||
HS384("HS384", "HMAC using SHA-384", "HMAC", "HmacSHA384", true, 384, 384),
|
||||
|
||||
/** JWA algorithm name for {@code HMAC using SHA-512} */
|
||||
HS512("HS512", "HMAC using SHA-512", "HMAC", "HmacSHA512", true),
|
||||
/**
|
||||
* JWA algorithm name for {@code HMAC using SHA-512}
|
||||
*/
|
||||
HS512("HS512", "HMAC using SHA-512", "HMAC", "HmacSHA512", true, 512, 512),
|
||||
|
||||
/** JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-256} */
|
||||
RS256("RS256", "RSASSA-PKCS-v1_5 using SHA-256", "RSA", "SHA256withRSA", true),
|
||||
/**
|
||||
* JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-256}
|
||||
*/
|
||||
RS256("RS256", "RSASSA-PKCS-v1_5 using SHA-256", "RSA", "SHA256withRSA", true, 256, 2048),
|
||||
|
||||
/** JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-384} */
|
||||
RS384("RS384", "RSASSA-PKCS-v1_5 using SHA-384", "RSA", "SHA384withRSA", true),
|
||||
/**
|
||||
* JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-384}
|
||||
*/
|
||||
RS384("RS384", "RSASSA-PKCS-v1_5 using SHA-384", "RSA", "SHA384withRSA", true, 384, 2048),
|
||||
|
||||
/** JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-512} */
|
||||
RS512("RS512", "RSASSA-PKCS-v1_5 using SHA-512", "RSA", "SHA512withRSA", true),
|
||||
/**
|
||||
* JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-512}
|
||||
*/
|
||||
RS512("RS512", "RSASSA-PKCS-v1_5 using SHA-512", "RSA", "SHA512withRSA", true, 512, 2048),
|
||||
|
||||
/**
|
||||
* JWA algorithm name for {@code ECDSA using P-256 and SHA-256}. <b>This is not a JDK standard algorithm and
|
||||
* requires that a JCA provider like BouncyCastle be in the runtime classpath.</b> BouncyCastle will be used
|
||||
* automatically if found in the runtime classpath.
|
||||
*/
|
||||
ES256("ES256", "ECDSA using P-256 and SHA-256", "Elliptic Curve", "SHA256withECDSA", false),
|
||||
ES256("ES256", "ECDSA using P-256 and SHA-256", "ECDSA", "SHA256withECDSA", false, 256, 256),
|
||||
|
||||
/**
|
||||
* JWA algorithm name for {@code ECDSA using P-384 and SHA-384}. <b>This is not a JDK standard algorithm and
|
||||
* requires that a JCA provider like BouncyCastle be in the runtime classpath.</b> BouncyCastle will be used
|
||||
* automatically if found in the runtime classpath.
|
||||
*/
|
||||
ES384("ES384", "ECDSA using P-384 and SHA-384", "Elliptic Curve", "SHA384withECDSA", false),
|
||||
ES384("ES384", "ECDSA using P-384 and SHA-384", "ECDSA", "SHA384withECDSA", false, 384, 384),
|
||||
|
||||
/**
|
||||
* JWA algorithm name for {@code ECDSA using P-512 and SHA-512}. <b>This is not a JDK standard algorithm and
|
||||
* JWA algorithm name for {@code ECDSA using P-521 and SHA-512}. <b>This is not a JDK standard algorithm and
|
||||
* requires that a JCA provider like BouncyCastle be in the runtime classpath.</b> BouncyCastle will be used
|
||||
* automatically if found in the runtime classpath.
|
||||
*/
|
||||
ES512("ES512", "ECDSA using P-512 and SHA-512", "Elliptic Curve", "SHA512withECDSA", false),
|
||||
ES512("ES512", "ECDSA using P-521 and SHA-512", "ECDSA", "SHA512withECDSA", false, 512, 521),
|
||||
|
||||
/**
|
||||
* JWA algorithm name for {@code RSASSA-PSS using SHA-256 and MGF1 with SHA-256}. <b>This is not a JDK standard
|
||||
* algorithm and requires that a JCA provider like BouncyCastle be in the runtime classpath.</b> BouncyCastle
|
||||
* will be used automatically if found in the runtime classpath.
|
||||
*/
|
||||
PS256("PS256", "RSASSA-PSS using SHA-256 and MGF1 with SHA-256", "RSA", "SHA256withRSAandMGF1", false),
|
||||
PS256("PS256", "RSASSA-PSS using SHA-256 and MGF1 with SHA-256", "RSA", "SHA256withRSAandMGF1", false, 256, 2048),
|
||||
|
||||
/**
|
||||
* JWA algorithm name for {@code RSASSA-PSS using SHA-384 and MGF1 with SHA-384}. <b>This is not a JDK standard
|
||||
* algorithm and requires that a JCA provider like BouncyCastle be in the runtime classpath.</b> BouncyCastle
|
||||
* will be used automatically if found in the runtime classpath.
|
||||
*/
|
||||
PS384("PS384", "RSASSA-PSS using SHA-384 and MGF1 with SHA-384", "RSA", "SHA384withRSAandMGF1", false),
|
||||
PS384("PS384", "RSASSA-PSS using SHA-384 and MGF1 with SHA-384", "RSA", "SHA384withRSAandMGF1", false, 384, 2048),
|
||||
|
||||
/**
|
||||
* JWA algorithm name for {@code RSASSA-PSS using SHA-512 and MGF1 with SHA-512}. <b>This is not a JDK standard
|
||||
* algorithm and requires that a JCA provider like BouncyCastle be in the classpath.</b> BouncyCastle will be used
|
||||
* automatically if found in the runtime classpath.
|
||||
*/
|
||||
PS512("PS512", "RSASSA-PSS using SHA-512 and MGF1 with SHA-512", "RSA", "SHA512withRSAandMGF1", false);
|
||||
PS512("PS512", "RSASSA-PSS using SHA-512 and MGF1 with SHA-512", "RSA", "SHA512withRSAandMGF1", false, 512, 2048);
|
||||
|
||||
static {
|
||||
RuntimeEnvironment.enableBouncyCastleIfPossible();
|
||||
}
|
||||
|
||||
private final String value;
|
||||
private final String description;
|
||||
private final String familyName;
|
||||
private final String jcaName;
|
||||
private final String value;
|
||||
private final String description;
|
||||
private final String familyName;
|
||||
private final String jcaName;
|
||||
private final boolean jdkStandard;
|
||||
private final int digestLength;
|
||||
private final int minKeyLength;
|
||||
|
||||
SignatureAlgorithm(String value, String description, String familyName, String jcaName, boolean jdkStandard) {
|
||||
SignatureAlgorithm(String value, String description, String familyName, String jcaName, boolean jdkStandard,
|
||||
int digestLength, int minKeyLength) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.familyName = familyName;
|
||||
this.jcaName = jcaName;
|
||||
this.jdkStandard = jdkStandard;
|
||||
this.digestLength = digestLength;
|
||||
this.minKeyLength = minKeyLength;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,67 +159,66 @@ public enum SignatureAlgorithm {
|
|||
* following table:
|
||||
*
|
||||
* <table>
|
||||
* <caption>Crypto Family</caption>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th>SignatureAlgorithm</th>
|
||||
* <th>Family Name</th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr>
|
||||
* <td>HS256</td>
|
||||
* <td>HMAC</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>HS384</td>
|
||||
* <td>HMAC</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>HS512</td>
|
||||
* <td>HMAC</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>RS256</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>RS384</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>RS512</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>PS256</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>PS384</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>PS512</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>ES256</td>
|
||||
* <td>Elliptic Curve</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>ES384</td>
|
||||
* <td>Elliptic Curve</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>ES512</td>
|
||||
* <td>Elliptic Curve</td>
|
||||
* </tr>
|
||||
* </tbody>
|
||||
* <caption>Crypto Family</caption>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th>SignatureAlgorithm</th>
|
||||
* <th>Family Name</th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr>
|
||||
* <td>HS256</td>
|
||||
* <td>HMAC</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>HS384</td>
|
||||
* <td>HMAC</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>HS512</td>
|
||||
* <td>HMAC</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>RS256</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>RS384</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>RS512</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>PS256</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>PS384</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>PS512</td>
|
||||
* <td>RSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>ES256</td>
|
||||
* <td>ECDSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>ES384</td>
|
||||
* <td>ECDSA</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>ES512</td>
|
||||
* <td>ECDSA</td>
|
||||
* </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
*
|
||||
* @return Returns the cryptographic family name of the signature algorithm.
|
||||
*
|
||||
* @since 0.5
|
||||
*/
|
||||
public String getFamilyName() {
|
||||
|
@ -225,7 +253,7 @@ public enum SignatureAlgorithm {
|
|||
* @return {@code true} if the enum instance represents an HMAC signature algorithm, {@code false} otherwise.
|
||||
*/
|
||||
public boolean isHmac() {
|
||||
return name().startsWith("HS");
|
||||
return familyName.equals("HMAC");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,18 +264,152 @@ public enum SignatureAlgorithm {
|
|||
* {@code false} otherwise.
|
||||
*/
|
||||
public boolean isRsa() {
|
||||
return getDescription().startsWith("RSASSA");
|
||||
return familyName.equals("RSA");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the enum instance represents an Elliptic Curve signature algorithm, {@code false}
|
||||
* Returns {@code true} if the enum instance represents an Elliptic Curve ECDSA signature algorithm, {@code false}
|
||||
* otherwise.
|
||||
*
|
||||
* @return {@code true} if the enum instance represents an Elliptic Curve signature algorithm, {@code false}
|
||||
* @return {@code true} if the enum instance represents an Elliptic Curve ECDSA signature algorithm, {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isEllipticCurve() {
|
||||
return name().startsWith("ES");
|
||||
return familyName.equals("ECDSA");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns quietly if the specified key is allowed to create signatures using this algorithm
|
||||
* according to the <a href="https://tools.ietf.org/html/rfc7518">JWT JWA Specification (RFC 7518)</a> or throws an
|
||||
* {@link InvalidKeyException} if the key is not allowed or not secure enough for this algorithm.
|
||||
*
|
||||
* @param key the key to check for validity.
|
||||
* @throws InvalidKeyException if the key is not allowed or not secure enough for this algorithm.
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public void assertValidSigningKey(Key key) throws InvalidKeyException {
|
||||
assertValid(key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns quietly if the specified key is allowed to verify signatures using this algorithm
|
||||
* according to the <a href="https://tools.ietf.org/html/rfc7518">JWT JWA Specification (RFC 7518)</a> or throws an
|
||||
* {@link InvalidKeyException} if the key is not allowed or not secure enough for this algorithm.
|
||||
*
|
||||
* @param key the key to check for validity.
|
||||
* @throws InvalidKeyException if the key is not allowed or not secure enough for this algorithm.
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public void assertValidVerificationKey(Key key) throws InvalidKeyException {
|
||||
assertValid(key, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.10.0 to support assertValid(Key, boolean)
|
||||
*/
|
||||
private static String keyType(boolean signing) {
|
||||
return signing ? "signing" : "verification";
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
private void assertValid(Key key, boolean signing) throws InvalidKeyException {
|
||||
|
||||
if (this == NONE) {
|
||||
|
||||
String msg = "The 'NONE' signature algorithm does not support cryptographic keys.";
|
||||
throw new InvalidKeyException(msg);
|
||||
|
||||
} else if (isHmac()) {
|
||||
|
||||
if (!(key instanceof SecretKey)) {
|
||||
String msg = this.familyName + " " + keyType(signing) + " keys must be SecretKey instances.";
|
||||
throw new InvalidKeyException(msg);
|
||||
}
|
||||
SecretKey secretKey = (SecretKey) key;
|
||||
|
||||
byte[] encoded = secretKey.getEncoded();
|
||||
if (encoded == null) {
|
||||
throw new InvalidKeyException("The " + keyType(signing) + " key's encoded bytes cannot be null.");
|
||||
}
|
||||
|
||||
String alg = secretKey.getAlgorithm();
|
||||
if (alg == null) {
|
||||
throw new InvalidKeyException("The " + keyType(signing) + " key's algorithm cannot be null.");
|
||||
}
|
||||
if (!HS256.jcaName.equals(alg) && !HS384.jcaName.equals(alg) && !HS512.jcaName.equals(alg)) {
|
||||
throw new InvalidKeyException("The " + keyType(signing) + " key's algorithm '" + alg +
|
||||
"' does not equal a valid HmacSHA* algorithm name and cannot be used with " + name() + ".");
|
||||
}
|
||||
|
||||
int size = encoded.length * 8; //size in bits
|
||||
if (size < this.minKeyLength) {
|
||||
String msg = "The " + keyType(signing) + " key's size is " + size + " bits which " +
|
||||
"is not secure enough for the " + name() + " algorithm. The JWT " +
|
||||
"JWA Specification (RFC 7518, Section 3.2) states that keys used with " + name() + " MUST have a " +
|
||||
"size >= " + minKeyLength + " bits (the key size must be greater than or equal to the hash " +
|
||||
"output size). Consider using the " + Keys.class.getName() + " class's " +
|
||||
"'secretKeyFor(SignatureAlgorithm." + name() + ")' method to create a key guaranteed to be " +
|
||||
"secure enough for " + name() + ". See " +
|
||||
"https://tools.ietf.org/html/rfc7518#section-3.2 for more information.";
|
||||
throw new WeakKeyException(msg);
|
||||
}
|
||||
|
||||
} else { //EC or RSA
|
||||
|
||||
if (signing) {
|
||||
if (!(key instanceof PrivateKey)) {
|
||||
String msg = familyName + " signing keys must be PrivateKey instances.";
|
||||
throw new InvalidKeyException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (isEllipticCurve()) {
|
||||
|
||||
if (!(key instanceof ECKey)) {
|
||||
String msg = familyName + " " + keyType(signing) + " keys must be ECKey instances.";
|
||||
throw new InvalidKeyException(msg);
|
||||
}
|
||||
|
||||
ECKey ecKey = (ECKey) key;
|
||||
int size = ecKey.getParams().getOrder().bitLength();
|
||||
if (size < this.minKeyLength) {
|
||||
String msg = "The " + keyType(signing) + " key's size (ECParameterSpec order) is " + size +
|
||||
" bits which is not secure enough for the " + name() + " algorithm. The JWT " +
|
||||
"JWA Specification (RFC 7518, Section 3.4) states that keys used with " +
|
||||
name() + " MUST have a size >= " + this.minKeyLength +
|
||||
" bits. Consider using the " + Keys.class.getName() + " class's " +
|
||||
"'keyPairFor(SignatureAlgorithm." + name() + ")' method to create a key pair guaranteed " +
|
||||
"to be secure enough for " + name() + ". See " +
|
||||
"https://tools.ietf.org/html/rfc7518#section-3.4 for more information.";
|
||||
throw new WeakKeyException(msg);
|
||||
}
|
||||
|
||||
} else { //RSA
|
||||
|
||||
if (!(key instanceof RSAKey)) {
|
||||
String msg = familyName + " " + keyType(signing) + " keys must be RSAKey instances.";
|
||||
throw new InvalidKeyException(msg);
|
||||
}
|
||||
|
||||
RSAKey rsaKey = (RSAKey)key;
|
||||
int size = rsaKey.getModulus().bitLength();
|
||||
if (size < this.minKeyLength) {
|
||||
|
||||
String section = name().startsWith("P") ? "3.5" : "3.3";
|
||||
|
||||
String msg = "The " + keyType(signing) + " key's size is " + size + " bits which is not secure " +
|
||||
"enough for the " + name() + " algorithm. The JWT JWA Specification (RFC 7518, Section " +
|
||||
section + ") states that keys used with " + name() + " MUST have a size >= " +
|
||||
this.minKeyLength + " bits. Consider using the " + Keys.class.getName() + " class's " +
|
||||
"'keyPairFor(SignatureAlgorithm." + name() + ")' method to create a key pair guaranteed " +
|
||||
"to be secure enough for " + name() + ". See " +
|
||||
"https://tools.ietf.org/html/rfc7518#section-" + section + " for more information.";
|
||||
throw new WeakKeyException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,12 +15,16 @@
|
|||
*/
|
||||
package io.jsonwebtoken;
|
||||
|
||||
import io.jsonwebtoken.security.SecurityException;
|
||||
|
||||
/**
|
||||
* Exception indicating that either calculating a signature or verifying an existing signature of a JWT failed.
|
||||
*
|
||||
* @since 0.1
|
||||
* @deprecated in favor of {@link io.jsonwebtoken.security.SecurityException}; this class will be removed before 1.0
|
||||
*/
|
||||
public class SignatureException extends JwtException {
|
||||
@Deprecated
|
||||
public class SignatureException extends SecurityException {
|
||||
|
||||
public SignatureException(String message) {
|
||||
super(message);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public class InvalidKeyException extends KeyException {
|
||||
|
||||
public InvalidKeyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public class KeyException extends SecurityException {
|
||||
|
||||
public KeyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package io.jsonwebtoken.crypto;
|
||||
package io.jsonwebtoken.security;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
@ -24,6 +24,22 @@ public final class Keys {
|
|||
private Keys() {
|
||||
}
|
||||
|
||||
/*
|
||||
public static final int bitLength(Key key) throws IllegalArgumentException {
|
||||
Assert.notNull(key, "Key cannot be null.");
|
||||
if (key instanceof SecretKey) {
|
||||
byte[] encoded = key.getEncoded();
|
||||
return Arrays.length(encoded) * 8;
|
||||
} else if (key instanceof RSAKey) {
|
||||
return ((RSAKey)key).getModulus().bitLength();
|
||||
} else if (key instanceof ECKey) {
|
||||
return ((ECKey)key).getParams().getOrder().bitLength();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unsupported key type: " + key.getClass().getName());
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a new {@link SecretKey} with a key length suitable for use with the specified {@link SignatureAlgorithm}.
|
||||
*
|
|
@ -0,0 +1,17 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
import io.jsonwebtoken.JwtException;
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public class SecurityException extends JwtException {
|
||||
|
||||
public SecurityException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SecurityException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public class SignatureException extends io.jsonwebtoken.SignatureException {
|
||||
|
||||
public SignatureException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SignatureException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public class WeakKeyException extends InvalidKeyException {
|
||||
|
||||
public WeakKeyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -15,17 +15,33 @@
|
|||
*/
|
||||
package io.jsonwebtoken
|
||||
|
||||
import io.jsonwebtoken.security.InvalidKeyException
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import io.jsonwebtoken.security.SignatureException
|
||||
import org.junit.Test
|
||||
|
||||
import javax.crypto.SecretKey
|
||||
import java.security.Key
|
||||
import java.security.PrivateKey
|
||||
import java.security.interfaces.ECPrivateKey
|
||||
import java.security.interfaces.ECPublicKey
|
||||
import java.security.interfaces.RSAPrivateKey
|
||||
import java.security.interfaces.RSAPublicKey
|
||||
import java.security.spec.ECParameterSpec
|
||||
|
||||
import static org.easymock.EasyMock.*
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class SignatureAlgorithmTest {
|
||||
|
||||
private static final Random random = new Random() //does not need to be secure for testing
|
||||
|
||||
@Test
|
||||
void testNames() {
|
||||
def algNames = ['HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512',
|
||||
'ES256', 'ES384', 'ES512', 'PS256', 'PS384', 'PS512', 'NONE']
|
||||
|
||||
for( String name : algNames ) {
|
||||
for (String name : algNames) {
|
||||
testName(name)
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +60,7 @@ class SignatureAlgorithmTest {
|
|||
|
||||
@Test
|
||||
void testIsHmac() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
if (alg.name().startsWith("HS")) {
|
||||
assertTrue alg.isHmac()
|
||||
} else {
|
||||
|
@ -55,7 +71,7 @@ class SignatureAlgorithmTest {
|
|||
|
||||
@Test
|
||||
void testHmacFamilyName() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
if (alg.name().startsWith("HS")) {
|
||||
assertEquals alg.getFamilyName(), "HMAC"
|
||||
}
|
||||
|
@ -64,7 +80,7 @@ class SignatureAlgorithmTest {
|
|||
|
||||
@Test
|
||||
void testIsRsa() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
if (alg.getDescription().startsWith("RSASSA")) {
|
||||
assertTrue alg.isRsa()
|
||||
} else {
|
||||
|
@ -75,7 +91,7 @@ class SignatureAlgorithmTest {
|
|||
|
||||
@Test
|
||||
void testRsaFamilyName() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
if (alg.name().startsWith("RS") || alg.name().startsWith("PS")) {
|
||||
assertEquals alg.getFamilyName(), "RSA"
|
||||
}
|
||||
|
@ -84,7 +100,7 @@ class SignatureAlgorithmTest {
|
|||
|
||||
@Test
|
||||
void testIsEllipticCurve() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
if (alg.name().startsWith("ES")) {
|
||||
assertTrue alg.isEllipticCurve()
|
||||
} else {
|
||||
|
@ -95,16 +111,16 @@ class SignatureAlgorithmTest {
|
|||
|
||||
@Test
|
||||
void testEllipticCurveFamilyName() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
if (alg.name().startsWith("ES")) {
|
||||
assertEquals alg.getFamilyName(), "Elliptic Curve"
|
||||
assertEquals alg.getFamilyName(), "ECDSA"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsJdkStandard() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
if (alg.name().startsWith("ES") || alg.name().startsWith("PS") || alg == SignatureAlgorithm.NONE) {
|
||||
assertFalse alg.isJdkStandard()
|
||||
} else {
|
||||
|
@ -112,4 +128,618 @@ class SignatureAlgorithmTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidSigningKeyWithNoneAlgorithm() {
|
||||
Key key = createMock(Key)
|
||||
try {
|
||||
SignatureAlgorithm.NONE.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The 'NONE' signature algorithm does not support cryptographic keys." as String, expected.message
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacSigningKeyHappyPath() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
SecretKey key = createMock(SecretKey)
|
||||
int numBits = alg.minKeyLength
|
||||
int numBytes = numBits / 8 as int
|
||||
expect(key.getEncoded()).andReturn(new byte[numBytes])
|
||||
expect(key.getAlgorithm()).andReturn(alg.jcaName)
|
||||
|
||||
replay key
|
||||
|
||||
alg.assertValidSigningKey(key)
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacSigningKeyNotSecretKey() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
Key key = createMock(Key)
|
||||
|
||||
try {
|
||||
alg.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals 'HMAC signing keys must be SecretKey instances.', expected.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacSigningKeyNullBytes() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
SecretKey key = createMock(SecretKey)
|
||||
expect(key.getEncoded()).andReturn(null)
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The signing key's encoded bytes cannot be null.", expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacSigningKeyMissingAlgorithm() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
SecretKey key = createMock(SecretKey)
|
||||
expect(key.getEncoded()).andReturn(new byte[alg.minKeyLength / 8 as int])
|
||||
expect(key.getAlgorithm()).andReturn(null)
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The signing key's algorithm cannot be null.", expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacSigningKeyUnsupportedAlgorithm() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
SecretKey key = createMock(SecretKey)
|
||||
expect(key.getEncoded()).andReturn(new byte[alg.minKeyLength / 8 as int])
|
||||
expect(key.getAlgorithm()).andReturn('AES')
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The signing key's algorithm 'AES' does not equal a valid HmacSHA* algorithm " +
|
||||
"name and cannot be used with ${alg.name()}." as String, expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacSigningKeyInsufficientKeyLength() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
SecretKey key = createMock(SecretKey)
|
||||
int numBits = alg.minKeyLength - 8 //8 bits shorter than expected
|
||||
int numBytes = numBits / 8 as int
|
||||
expect(key.getEncoded()).andReturn(new byte[numBytes])
|
||||
expect(key.getAlgorithm()).andReturn(alg.jcaName)
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The signing key's size is $numBits bits which is not secure enough for the " +
|
||||
"${alg.name()} algorithm. The JWT JWA Specification " +
|
||||
"(RFC 7518, Section 3.2) states that keys used with ${alg.name()} MUST have a size >= " +
|
||||
"${alg.minKeyLength} bits (the key size must be greater than or equal to the hash output " +
|
||||
"size). Consider using the ${Keys.class.getName()} class's 'secretKeyFor(" +
|
||||
"SignatureAlgorithm.${alg.name()})' method to create a key guaranteed to be secure enough " +
|
||||
"for ${alg.name()}. See https://tools.ietf.org/html/rfc7518#section-3.2 for " +
|
||||
"more information." as String, expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidECSigningKeyHappyPath() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {
|
||||
|
||||
ECPrivateKey key = createMock(ECPrivateKey)
|
||||
ECParameterSpec spec = createMock(ECParameterSpec)
|
||||
int numBits = alg.minKeyLength
|
||||
int numBytes = numBits / 8 as int
|
||||
byte[] orderBytes = new byte[numBytes + 1]
|
||||
random.nextBytes(orderBytes)
|
||||
BigInteger order = new BigInteger(orderBytes)
|
||||
expect(key.getParams()).andReturn(spec)
|
||||
expect(spec.getOrder()).andReturn(order)
|
||||
|
||||
replay key, spec
|
||||
|
||||
alg.assertValidSigningKey(key)
|
||||
|
||||
verify key, spec
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidECSigningNotPrivateKey() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {
|
||||
|
||||
ECPublicKey key = createMock(ECPublicKey)
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals 'ECDSA signing keys must be PrivateKey instances.', expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidECSigningKeyNotECKey() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {
|
||||
|
||||
PrivateKey key = createMock(PrivateKey)
|
||||
|
||||
try {
|
||||
alg.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals 'ECDSA signing keys must be ECKey instances.', expected.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidECSigningKeyInsufficientKeyLength() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {
|
||||
|
||||
ECPrivateKey key = createMock(ECPrivateKey)
|
||||
ECParameterSpec spec = createMock(ECParameterSpec)
|
||||
int numBits = alg.minKeyLength - 8 // 8 bits less than expected
|
||||
int numBytes = numBits / 8 as int
|
||||
byte[] orderBytes = new byte[numBytes]
|
||||
random.nextBytes(orderBytes)
|
||||
BigInteger order = new BigInteger(orderBytes)
|
||||
expect(key.getParams()).andReturn(spec)
|
||||
expect(spec.getOrder()).andReturn(order)
|
||||
|
||||
replay key, spec
|
||||
|
||||
try {
|
||||
alg.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The signing key's size (ECParameterSpec order) is ${order.bitLength()} bits " +
|
||||
"which is not secure enough for the ${alg.name()} algorithm. The JWT JWA Specification " +
|
||||
"(RFC 7518, Section 3.4) states that keys used with ${alg.name()} MUST have a size >= " +
|
||||
"${alg.minKeyLength} bits. Consider using the ${Keys.class.getName()} class's " +
|
||||
"'keyPairFor(SignatureAlgorithm.${alg.name()})' method to create a key pair guaranteed " +
|
||||
"to be secure enough for ${alg.name()}. See " +
|
||||
"https://tools.ietf.org/html/rfc7518#section-3.4 for more information." as String, expected.message
|
||||
}
|
||||
|
||||
verify key, spec
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidRSASigningKeyHappyPath() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {
|
||||
|
||||
RSAPrivateKey key = createMock(RSAPrivateKey)
|
||||
int numBits = alg.minKeyLength
|
||||
int numBytes = numBits / 8 as int
|
||||
byte[] modulusBytes = new byte[numBytes + 1]
|
||||
random.nextBytes(modulusBytes)
|
||||
BigInteger modulus = new BigInteger(modulusBytes)
|
||||
expect(key.getModulus()).andReturn(modulus)
|
||||
|
||||
replay key
|
||||
|
||||
alg.assertValidSigningKey(key)
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidRSASigningNotPrivateKey() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {
|
||||
|
||||
RSAPublicKey key = createMock(RSAPublicKey)
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals 'RSA signing keys must be PrivateKey instances.', expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidRSASigningKeyNotRSAKey() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {
|
||||
|
||||
PrivateKey key = createMock(PrivateKey)
|
||||
|
||||
try {
|
||||
alg.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals 'RSA signing keys must be RSAKey instances.', expected.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidRSASigningKeyInsufficientKeyLength() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {
|
||||
|
||||
String section = alg.name().startsWith("P") ? "3.5" : "3.3"
|
||||
|
||||
RSAPrivateKey key = createMock(RSAPrivateKey)
|
||||
int numBits = alg.minKeyLength - 8
|
||||
int numBytes = numBits / 8 as int
|
||||
byte[] modulusBytes = new byte[numBytes]
|
||||
random.nextBytes(modulusBytes)
|
||||
BigInteger modulus = new BigInteger(modulusBytes)
|
||||
expect(key.getModulus()).andReturn(modulus)
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidSigningKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The signing key's size is ${modulus.bitLength()} bits which is not secure " +
|
||||
"enough for the ${alg.name()} algorithm. The JWT JWA Specification " +
|
||||
"(RFC 7518, Section ${section}) states that keys used with ${alg.name()} MUST have a size >= " +
|
||||
"${alg.minKeyLength} bits. Consider using the ${Keys.class.getName()} class's " +
|
||||
"'keyPairFor(SignatureAlgorithm.${alg.name()})' method to create a key pair guaranteed " +
|
||||
"to be secure enough for ${alg.name()}. See " +
|
||||
"https://tools.ietf.org/html/rfc7518#section-${section} for more information." as String, expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidVerificationKeyWithNoneAlgorithm() {
|
||||
Key key = createMock(Key)
|
||||
try {
|
||||
SignatureAlgorithm.NONE.assertValidVerificationKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The 'NONE' signature algorithm does not support cryptographic keys." as String, expected.message
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacVerificationKeyHappyPath() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
SecretKey key = createMock(SecretKey)
|
||||
int numBits = alg.minKeyLength
|
||||
int numBytes = numBits / 8 as int
|
||||
expect(key.getEncoded()).andReturn(new byte[numBytes])
|
||||
expect(key.getAlgorithm()).andReturn(alg.jcaName)
|
||||
|
||||
replay key
|
||||
|
||||
alg.assertValidVerificationKey(key)
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacVerificationKeyNotSecretKey() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
Key key = createMock(Key)
|
||||
|
||||
try {
|
||||
alg.assertValidVerificationKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals 'HMAC verification keys must be SecretKey instances.', expected.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacVerificationKeyNullBytes() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
SecretKey key = createMock(SecretKey)
|
||||
expect(key.getEncoded()).andReturn(null)
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidVerificationKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The verification key's encoded bytes cannot be null.", expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacVerificationKeyMissingAlgorithm() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
SecretKey key = createMock(SecretKey)
|
||||
expect(key.getEncoded()).andReturn(new byte[alg.minKeyLength / 8 as int])
|
||||
expect(key.getAlgorithm()).andReturn(null)
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidVerificationKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The verification key's algorithm cannot be null.", expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacVerificationKeyUnsupportedAlgorithm() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
SecretKey key = createMock(SecretKey)
|
||||
expect(key.getEncoded()).andReturn(new byte[alg.minKeyLength / 8 as int])
|
||||
expect(key.getAlgorithm()).andReturn('AES')
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidVerificationKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The verification key's algorithm 'AES' does not equal a valid HmacSHA* algorithm " +
|
||||
"name and cannot be used with ${alg.name()}." as String, expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidHmacVerificationKeyInsufficientKeyLength() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {
|
||||
|
||||
SecretKey key = createMock(SecretKey)
|
||||
int numBits = alg.minKeyLength - 8 // 8 bits (1 byte) less than required
|
||||
int numBytes = numBits / 8 as int
|
||||
expect(key.getEncoded()).andReturn(new byte[numBytes])
|
||||
expect(key.getAlgorithm()).andReturn(alg.jcaName)
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidVerificationKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The verification key's size is $numBits bits which is not secure enough for the " +
|
||||
"${alg.name()} algorithm. The JWT JWA Specification " +
|
||||
"(RFC 7518, Section 3.2) states that keys used with ${alg.name()} MUST have a size >= " +
|
||||
"${alg.minKeyLength} bits (the key size must be greater than or equal to the hash output " +
|
||||
"size). Consider using the ${Keys.class.getName()} class's 'secretKeyFor(" +
|
||||
"SignatureAlgorithm.${alg.name()})' method to create a key guaranteed to be secure enough " +
|
||||
"for ${alg.name()}. See https://tools.ietf.org/html/rfc7518#section-3.2 for " +
|
||||
"more information." as String, expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidECVerificationKeyHappyPath() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {
|
||||
|
||||
ECPrivateKey key = createMock(ECPrivateKey)
|
||||
ECParameterSpec spec = createMock(ECParameterSpec)
|
||||
int numBits = alg.minKeyLength
|
||||
int numBytes = numBits / 8 as int
|
||||
byte[] orderBytes = new byte[numBytes + 1]
|
||||
random.nextBytes(orderBytes)
|
||||
BigInteger order = new BigInteger(orderBytes)
|
||||
expect(key.getParams()).andReturn(spec)
|
||||
expect(spec.getOrder()).andReturn(order)
|
||||
|
||||
replay key, spec
|
||||
|
||||
alg.assertValidVerificationKey(key)
|
||||
|
||||
verify key, spec
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidECVerificationKeyNotECKey() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {
|
||||
|
||||
PrivateKey key = createMock(PrivateKey)
|
||||
|
||||
try {
|
||||
alg.assertValidVerificationKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals 'ECDSA verification keys must be ECKey instances.', expected.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidECVerificationKeyInsufficientKeyLength() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {
|
||||
|
||||
ECPrivateKey key = createMock(ECPrivateKey)
|
||||
ECParameterSpec spec = createMock(ECParameterSpec)
|
||||
int numBits = alg.minKeyLength - 8 // 8 bits = 1 byte
|
||||
int numBytes = numBits / 8 as int
|
||||
byte[] orderBytes = new byte[numBytes]
|
||||
random.nextBytes(orderBytes)
|
||||
BigInteger order = new BigInteger(orderBytes)
|
||||
expect(key.getParams()).andReturn(spec)
|
||||
expect(spec.getOrder()).andReturn(order)
|
||||
|
||||
replay key, spec
|
||||
|
||||
try {
|
||||
alg.assertValidVerificationKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The verification key's size (ECParameterSpec order) is ${order.bitLength()} bits " +
|
||||
"which is not secure enough for the ${alg.name()} algorithm. The JWT JWA Specification " +
|
||||
"(RFC 7518, Section 3.4) states that keys used with ${alg.name()} MUST have a size >= " +
|
||||
"${alg.minKeyLength} bits. Consider using the ${Keys.class.getName()} class's " +
|
||||
"'keyPairFor(SignatureAlgorithm.${alg.name()})' method to create a key pair guaranteed " +
|
||||
"to be secure enough for ${alg.name()}. See " +
|
||||
"https://tools.ietf.org/html/rfc7518#section-3.4 for more information." as String, expected.message
|
||||
}
|
||||
|
||||
verify key, spec
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidRSAVerificationKeyHappyPath() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {
|
||||
|
||||
RSAPrivateKey key = createMock(RSAPrivateKey)
|
||||
int numBits = alg.minKeyLength
|
||||
int numBytes = numBits / 8 as int
|
||||
byte[] modulusBytes = new byte[numBytes + 1]
|
||||
random.nextBytes(modulusBytes)
|
||||
BigInteger modulus = new BigInteger(modulusBytes)
|
||||
expect(key.getModulus()).andReturn(modulus)
|
||||
|
||||
replay key
|
||||
|
||||
alg.assertValidVerificationKey(key)
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidRSAVerificationKeyNotRSAKey() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {
|
||||
|
||||
PrivateKey key = createMock(PrivateKey)
|
||||
|
||||
try {
|
||||
alg.assertValidVerificationKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals 'RSA verification keys must be RSAKey instances.', expected.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertValidRSAVerificationKeyInsufficientKeyLength() {
|
||||
|
||||
for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {
|
||||
|
||||
String section = alg.name().startsWith("P") ? "3.5" : "3.3"
|
||||
|
||||
RSAPrivateKey key = createMock(RSAPrivateKey)
|
||||
int numBits = alg.minKeyLength - 8 // 8 bits = 1 byte
|
||||
int numBytes = numBits / 8 as int
|
||||
byte[] modulusBytes = new byte[numBytes]
|
||||
random.nextBytes(modulusBytes)
|
||||
BigInteger modulus = new BigInteger(modulusBytes)
|
||||
expect(key.getModulus()).andReturn(modulus)
|
||||
|
||||
replay key
|
||||
|
||||
try {
|
||||
alg.assertValidVerificationKey(key)
|
||||
fail()
|
||||
} catch (InvalidKeyException expected) {
|
||||
assertEquals "The verification key's size is ${modulus.bitLength()} bits which is not secure enough " +
|
||||
"for the ${alg.name()} algorithm. The JWT JWA Specification " +
|
||||
"(RFC 7518, Section ${section}) states that keys used with ${alg.name()} MUST have a size >= " +
|
||||
"${alg.minKeyLength} bits. Consider using the ${Keys.class.getName()} class's " +
|
||||
"'keyPairFor(SignatureAlgorithm.${alg.name()})' method to create a key pair guaranteed " +
|
||||
"to be secure enough for ${alg.name()}. See " +
|
||||
"https://tools.ietf.org/html/rfc7518#section-${section} for more information." as String, expected.message
|
||||
}
|
||||
|
||||
verify key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package io.jsonwebtoken.crypto
|
||||
package io.jsonwebtoken.security
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.lang.Classes
|
||||
|
@ -13,14 +13,8 @@ import java.security.KeyPair
|
|||
import static org.easymock.EasyMock.eq
|
||||
import static org.easymock.EasyMock.expect
|
||||
import static org.easymock.EasyMock.same
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertSame
|
||||
import static org.junit.Assert.fail
|
||||
import static org.powermock.api.easymock.PowerMock.mockStatic
|
||||
import static org.powermock.api.easymock.PowerMock.createMock
|
||||
import static org.powermock.api.easymock.PowerMock.replay
|
||||
import static org.powermock.api.easymock.PowerMock.reset
|
||||
import static org.powermock.api.easymock.PowerMock.verify
|
||||
import static org.junit.Assert.*
|
||||
import static org.powermock.api.easymock.PowerMock.*
|
||||
|
||||
/**
|
||||
* This test class is for cursory API-level testing only (what is available to the API module at build time).
|
|
@ -0,0 +1,22 @@
|
|||
package io.jsonwebtoken.security
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
|
||||
class SignatureExceptionTest {
|
||||
|
||||
@Test
|
||||
void testStringConstructor() {
|
||||
def exception = new SignatureException("my message")
|
||||
assertEquals "my message", exception.getMessage()
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCauseConstructor() {
|
||||
def ioException = new IOException("root error")
|
||||
def exception = new SignatureException("wrapping", ioException)
|
||||
assertEquals "wrapping", exception.getMessage()
|
||||
assertEquals ioException, exception.getCause()
|
||||
}
|
||||
}
|
|
@ -22,19 +22,20 @@ import io.jsonwebtoken.JwsHeader;
|
|||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.JwtParser;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.io.Encoder;
|
||||
import io.jsonwebtoken.impl.crypto.DefaultJwtSigner;
|
||||
import io.jsonwebtoken.impl.crypto.JwtSigner;
|
||||
import io.jsonwebtoken.impl.io.InstanceLocator;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.Encoder;
|
||||
import io.jsonwebtoken.io.Encoders;
|
||||
import io.jsonwebtoken.io.SerializationException;
|
||||
import io.jsonwebtoken.io.Serializer;
|
||||
import io.jsonwebtoken.impl.io.InstanceLocator;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.Key;
|
||||
import java.util.Date;
|
||||
|
@ -108,13 +109,12 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public JwtBuilder signWith(SignatureAlgorithm alg, byte[] secretKey) {
|
||||
public JwtBuilder signWith(SignatureAlgorithm alg, byte[] secretKeyBytes) {
|
||||
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
|
||||
Assert.notEmpty(secretKey, "secret key byte array cannot be null or empty.");
|
||||
Assert.notEmpty(secretKeyBytes, "secret key byte array cannot be null or empty.");
|
||||
Assert.isTrue(alg.isHmac(), "Key bytes may only be specified for HMAC signatures. If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.");
|
||||
this.algorithm = alg;
|
||||
this.key = new SecretKeySpec(secretKey, alg.getJcaName());
|
||||
return this;
|
||||
SecretKey key = new SecretKeySpec(secretKeyBytes, alg.getJcaName());
|
||||
return signWith(alg, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -129,6 +129,7 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
|||
public JwtBuilder signWith(SignatureAlgorithm alg, Key key) {
|
||||
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
|
||||
Assert.notNull(key, "Key argument cannot be null.");
|
||||
alg.assertValidSigningKey(key); //since 0.10.0 for https://github.com/jwtk/jjwt/issues/334
|
||||
this.algorithm = alg;
|
||||
this.key = key;
|
||||
return this;
|
||||
|
|
|
@ -34,22 +34,24 @@ import io.jsonwebtoken.MalformedJwtException;
|
|||
import io.jsonwebtoken.MissingClaimException;
|
||||
import io.jsonwebtoken.PrematureJwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.SigningKeyResolver;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.io.Decoder;
|
||||
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver;
|
||||
import io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator;
|
||||
import io.jsonwebtoken.impl.crypto.JwtSignatureValidator;
|
||||
import io.jsonwebtoken.impl.io.InstanceLocator;
|
||||
import io.jsonwebtoken.io.Decoder;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.DeserializationException;
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.impl.io.InstanceLocator;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
import io.jsonwebtoken.lang.DateFormats;
|
||||
import io.jsonwebtoken.lang.Objects;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
import io.jsonwebtoken.security.InvalidKeyException;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
import io.jsonwebtoken.security.WeakKeyException;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.Key;
|
||||
|
@ -359,8 +361,11 @@ public class DefaultJwtParser implements JwtParser {
|
|||
|
||||
JwtSignatureValidator validator;
|
||||
try {
|
||||
algorithm.assertValidVerificationKey(key); //since 0.10.0: https://github.com/jwtk/jjwt/issues/334
|
||||
validator = createSignatureValidator(algorithm, key);
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (WeakKeyException e) {
|
||||
throw e;
|
||||
} catch (InvalidKeyException | IllegalArgumentException e) {
|
||||
String algName = algorithm.getValue();
|
||||
String msg = "The parsed JWT indicates it was signed with the " + algName + " signature " +
|
||||
"algorithm, but the specified signing key of type " + key.getClass().getName() +
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
|
@ -22,10 +26,6 @@ import java.security.SecureRandom;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
/**
|
||||
* ElliptiCurve crypto provider.
|
||||
*
|
||||
|
@ -172,8 +172,7 @@ public abstract class EllipticCurveProvider extends SignatureProvider {
|
|||
*
|
||||
* @throws JwtException If the ASN.1/DER signature format is invalid.
|
||||
*/
|
||||
public static byte[] transcodeSignatureToConcat(final byte[] derSignature, int outputLength)
|
||||
throws JwtException {
|
||||
public static byte[] transcodeSignatureToConcat(final byte[] derSignature, int outputLength) throws JwtException {
|
||||
|
||||
if (derSignature.length < 8 || derSignature[0] != 48) {
|
||||
throw new JwtException("Invalid ECDSA signature format");
|
||||
|
@ -191,16 +190,16 @@ public abstract class EllipticCurveProvider extends SignatureProvider {
|
|||
byte rLength = derSignature[offset + 1];
|
||||
|
||||
int i = rLength;
|
||||
while ((i > 0)
|
||||
&& (derSignature[(offset + 2 + rLength) - i] == 0))
|
||||
while ((i > 0) && (derSignature[(offset + 2 + rLength) - i] == 0)) {
|
||||
i--;
|
||||
}
|
||||
|
||||
byte sLength = derSignature[offset + 2 + rLength + 1];
|
||||
|
||||
int j = sLength;
|
||||
while ((j > 0)
|
||||
&& (derSignature[(offset + 2 + rLength + 2 + sLength) - j] == 0))
|
||||
while ((j > 0) && (derSignature[(offset + 2 + rLength + 2 + sLength) - j] == 0)) {
|
||||
j--;
|
||||
}
|
||||
|
||||
int rawLen = Math.max(i, j);
|
||||
rawLen = Math.max(rawLen, outputLength / 2);
|
||||
|
@ -234,16 +233,15 @@ public abstract class EllipticCurveProvider extends SignatureProvider {
|
|||
*
|
||||
* @throws JwtException If the ECDSA JWS signature format is invalid.
|
||||
*/
|
||||
public static byte[] transcodeSignatureToDER(byte[] jwsSignature)
|
||||
throws JwtException {
|
||||
public static byte[] transcodeSignatureToDER(byte[] jwsSignature) throws JwtException {
|
||||
|
||||
int rawLen = jwsSignature.length / 2;
|
||||
|
||||
int i = rawLen;
|
||||
|
||||
while((i > 0)
|
||||
&& (jwsSignature[rawLen - i] == 0))
|
||||
while((i > 0) && (jwsSignature[rawLen - i] == 0)) {
|
||||
i--;
|
||||
}
|
||||
|
||||
int j = i;
|
||||
|
||||
|
@ -253,9 +251,9 @@ public abstract class EllipticCurveProvider extends SignatureProvider {
|
|||
|
||||
int k = rawLen;
|
||||
|
||||
while ((k > 0)
|
||||
&& (jwsSignature[2 * rawLen - k] == 0))
|
||||
while ((k > 0) && (jwsSignature[2 * rawLen - k] == 0)) {
|
||||
k--;
|
||||
}
|
||||
|
||||
int l = k;
|
||||
|
||||
|
|
|
@ -15,16 +15,16 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
public class EllipticCurveSignatureValidator extends EllipticCurveProvider implements SignatureValidator {
|
||||
|
||||
private static final String EC_PUBLIC_KEY_REQD_MSG =
|
||||
|
|
|
@ -15,16 +15,16 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Signature;
|
||||
import java.security.interfaces.ECKey;
|
||||
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
|
||||
public class EllipticCurveSigner extends EllipticCurveProvider implements Signer {
|
||||
|
||||
public EllipticCurveSigner(SignatureAlgorithm alg, Key key) {
|
||||
|
|
|
@ -18,9 +18,10 @@ package io.jsonwebtoken.impl.crypto;
|
|||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public abstract class MacProvider extends SignatureProvider {
|
||||
|
@ -31,11 +32,11 @@ public abstract class MacProvider extends SignatureProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Generates a new secure-random 512 bit secret key suitable for creating and verifying HMAC signatures. This is a
|
||||
* convenience method that immediately delegates to {@link #generateKey(SignatureAlgorithm)} using {@link
|
||||
* Generates a new secure-random 512 bit secret key suitable for creating and verifying HMAC-SHA signatures. This
|
||||
* is a convenience method that immediately delegates to {@link #generateKey(SignatureAlgorithm)} using {@link
|
||||
* SignatureAlgorithm#HS512} as the method argument.
|
||||
*
|
||||
* @return a new secure-random 512 bit secret key suitable for creating and verifying HMAC signatures.
|
||||
* @return a new secure-random 512 bit secret key suitable for creating and verifying HMAC-SHA signatures.
|
||||
* @see #generateKey(SignatureAlgorithm)
|
||||
* @see #generateKey(SignatureAlgorithm, SecureRandom)
|
||||
* @since 0.5
|
||||
|
@ -78,26 +79,22 @@ public abstract class MacProvider extends SignatureProvider {
|
|||
* @see #generateKey()
|
||||
* @see #generateKey(SignatureAlgorithm)
|
||||
* @since 0.5
|
||||
* @deprecated since 0.10.0 - use {@link #generateKey(SignatureAlgorithm)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static SecretKey generateKey(SignatureAlgorithm alg, SecureRandom random) {
|
||||
|
||||
Assert.isTrue(alg.isHmac(), "SignatureAlgorithm argument must represent an HMAC algorithm.");
|
||||
|
||||
byte[] bytes;
|
||||
KeyGenerator gen;
|
||||
|
||||
switch (alg) {
|
||||
case HS256:
|
||||
bytes = new byte[32];
|
||||
break;
|
||||
case HS384:
|
||||
bytes = new byte[48];
|
||||
break;
|
||||
default:
|
||||
bytes = new byte[64];
|
||||
try {
|
||||
gen = KeyGenerator.getInstance(alg.getJcaName());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("The " + alg.getJcaName() + " algorithm is not available. " +
|
||||
"This should never happen on JDK 7 or later - please report this to the JJWT developers.", e);
|
||||
}
|
||||
|
||||
random.nextBytes(bytes);
|
||||
|
||||
return new SecretKeySpec(bytes, alg.getJcaName());
|
||||
return gen.generateKey();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.Key;
|
||||
|
@ -127,9 +127,9 @@ public abstract class RsaProvider extends SignatureProvider {
|
|||
* @see #generateKeyPair(String, int, SecureRandom)
|
||||
* @since 0.10.0
|
||||
*/
|
||||
@SuppressWarnings("unused") //used by io.jsonwebtoken.crypto.Keys
|
||||
@SuppressWarnings("unused") //used by io.jsonwebtoken.security.Keys
|
||||
public static KeyPair generateKeyPair(SignatureAlgorithm alg) {
|
||||
Assert.isTrue("RSA".equalsIgnoreCase(alg.getFamilyName()), "Only RSA algorithms are supported by this method.");
|
||||
Assert.isTrue(alg.isRsa(), "Only RSA algorithms are supported by this method.");
|
||||
int keySizeInBits = 4096;
|
||||
switch (alg) {
|
||||
case RS256:
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
|
|
|
@ -15,16 +15,16 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.RuntimeEnvironment;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Signature;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.RuntimeEnvironment;
|
||||
|
||||
abstract class SignatureProvider {
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
|
||||
public interface Signer {
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import io.jsonwebtoken.impl.DefaultClock
|
|||
import io.jsonwebtoken.impl.FixedClock
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import io.jsonwebtoken.security.SignatureException
|
||||
import org.junit.Test
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
|
|
@ -19,12 +19,11 @@ import io.jsonwebtoken.impl.DefaultHeader
|
|||
import io.jsonwebtoken.impl.DefaultJwsHeader
|
||||
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver
|
||||
import io.jsonwebtoken.impl.compression.GzipCompressionCodec
|
||||
import io.jsonwebtoken.impl.crypto.EllipticCurveProvider
|
||||
import io.jsonwebtoken.impl.crypto.MacProvider
|
||||
import io.jsonwebtoken.impl.crypto.RsaProvider
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.impl.io.RuntimeClasspathSerializerLocator
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import io.jsonwebtoken.security.WeakKeyException
|
||||
import org.junit.Test
|
||||
|
||||
import javax.crypto.Mac
|
||||
|
@ -348,11 +347,12 @@ class JwtsTest {
|
|||
@Test
|
||||
void testUncompressedJwt() {
|
||||
|
||||
byte[] key = MacProvider.generateKey().getEncoded()
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.HS256
|
||||
byte[] key = Keys.secretKeyFor(alg).encoded
|
||||
|
||||
String id = UUID.randomUUID().toString()
|
||||
|
||||
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(SignatureAlgorithm.HS256, key)
|
||||
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(alg, key)
|
||||
.claim("state", "hello this is an amazing jwt").compact()
|
||||
|
||||
def jws = Jwts.parser().setSigningKey(key).parseClaimsJws(compact)
|
||||
|
@ -369,11 +369,12 @@ class JwtsTest {
|
|||
@Test
|
||||
void testCompressedJwtWithDeflate() {
|
||||
|
||||
byte[] key = MacProvider.generateKey().getEncoded()
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.HS256
|
||||
byte[] key = Keys.secretKeyFor(alg).encoded
|
||||
|
||||
String id = UUID.randomUUID().toString()
|
||||
|
||||
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(SignatureAlgorithm.HS256, key)
|
||||
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(alg, key)
|
||||
.claim("state", "hello this is an amazing jwt").compressWith(CompressionCodecs.DEFLATE).compact()
|
||||
|
||||
def jws = Jwts.parser().setSigningKey(key).parseClaimsJws(compact)
|
||||
|
@ -390,11 +391,12 @@ class JwtsTest {
|
|||
@Test
|
||||
void testCompressedJwtWithGZIP() {
|
||||
|
||||
byte[] key = MacProvider.generateKey().getEncoded()
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.HS256
|
||||
byte[] key = Keys.secretKeyFor(alg).encoded
|
||||
|
||||
String id = UUID.randomUUID().toString()
|
||||
|
||||
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(SignatureAlgorithm.HS256, key)
|
||||
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(alg, key)
|
||||
.claim("state", "hello this is an amazing jwt").compressWith(CompressionCodecs.GZIP).compact()
|
||||
|
||||
def jws = Jwts.parser().setSigningKey(key).parseClaimsJws(compact)
|
||||
|
@ -410,11 +412,13 @@ class JwtsTest {
|
|||
|
||||
@Test
|
||||
void testCompressedWithCustomResolver() {
|
||||
byte[] key = MacProvider.generateKey().getEncoded()
|
||||
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.HS256
|
||||
byte[] key = Keys.secretKeyFor(alg).encoded
|
||||
|
||||
String id = UUID.randomUUID().toString()
|
||||
|
||||
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(SignatureAlgorithm.HS256, key)
|
||||
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(alg, key)
|
||||
.claim("state", "hello this is an amazing jwt").compressWith(new GzipCompressionCodec() {
|
||||
@Override
|
||||
String getAlgorithmName() {
|
||||
|
@ -446,11 +450,13 @@ class JwtsTest {
|
|||
|
||||
@Test(expected = CompressionException.class)
|
||||
void testCompressedJwtWithUnrecognizedHeader() {
|
||||
byte[] key = MacProvider.generateKey().getEncoded()
|
||||
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.HS256
|
||||
byte[] key = Keys.secretKeyFor(alg).encoded
|
||||
|
||||
String id = UUID.randomUUID().toString()
|
||||
|
||||
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(SignatureAlgorithm.HS256, key)
|
||||
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(alg, key)
|
||||
.claim("state", "hello this is an amazing jwt").compressWith(new GzipCompressionCodec() {
|
||||
@Override
|
||||
String getAlgorithmName() {
|
||||
|
@ -464,11 +470,12 @@ class JwtsTest {
|
|||
@Test
|
||||
void testCompressStringPayloadWithDeflate() {
|
||||
|
||||
byte[] key = MacProvider.generateKey().getEncoded()
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.HS256
|
||||
byte[] key = Keys.secretKeyFor(alg).encoded
|
||||
|
||||
String payload = "this is my test for a payload"
|
||||
|
||||
String compact = Jwts.builder().setPayload(payload).signWith(SignatureAlgorithm.HS256, key)
|
||||
String compact = Jwts.builder().setPayload(payload).signWith(alg, key)
|
||||
.compressWith(CompressionCodecs.DEFLATE).compact()
|
||||
|
||||
def jws = Jwts.parser().setSigningKey(key).parsePlaintextJws(compact)
|
||||
|
@ -482,62 +489,62 @@ class JwtsTest {
|
|||
|
||||
@Test
|
||||
void testHS256() {
|
||||
testHmac(SignatureAlgorithm.HS256);
|
||||
testHmac(SignatureAlgorithm.HS256)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHS384() {
|
||||
testHmac(SignatureAlgorithm.HS384);
|
||||
testHmac(SignatureAlgorithm.HS384)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHS512() {
|
||||
testHmac(SignatureAlgorithm.HS512);
|
||||
testHmac(SignatureAlgorithm.HS512)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRS256() {
|
||||
testRsa(SignatureAlgorithm.RS256);
|
||||
testRsa(SignatureAlgorithm.RS256)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRS384() {
|
||||
testRsa(SignatureAlgorithm.RS384);
|
||||
testRsa(SignatureAlgorithm.RS384)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRS512() {
|
||||
testRsa(SignatureAlgorithm.RS512);
|
||||
testRsa(SignatureAlgorithm.RS512)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPS256() {
|
||||
testRsa(SignatureAlgorithm.PS256);
|
||||
testRsa(SignatureAlgorithm.PS256)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPS384() {
|
||||
testRsa(SignatureAlgorithm.PS384);
|
||||
testRsa(SignatureAlgorithm.PS384)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPS512() {
|
||||
testRsa(SignatureAlgorithm.PS512, 2048, false);
|
||||
testRsa(SignatureAlgorithm.PS512)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRSA256WithPrivateKeyValidation() {
|
||||
testRsa(SignatureAlgorithm.RS256, 1024, true);
|
||||
testRsa(SignatureAlgorithm.RS256, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRSA384WithPrivateKeyValidation() {
|
||||
testRsa(SignatureAlgorithm.RS384, 1024, true);
|
||||
testRsa(SignatureAlgorithm.RS384, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRSA512WithPrivateKeyValidation() {
|
||||
testRsa(SignatureAlgorithm.RS512, 1024, true);
|
||||
testRsa(SignatureAlgorithm.RS512, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -565,17 +572,34 @@ class JwtsTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testParseClaimsJwsWithWeakHmacKey() {
|
||||
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.HS384
|
||||
def key = Keys.secretKeyFor(alg)
|
||||
def weakKey = Keys.secretKeyFor(SignatureAlgorithm.HS256)
|
||||
|
||||
String jws = Jwts.builder().setSubject("Foo").signWith(alg, key).compact()
|
||||
|
||||
try {
|
||||
Jwts.parser().setSigningKey(weakKey).parseClaimsJws(jws)
|
||||
fail('parseClaimsJws must fail for weak keys')
|
||||
} catch (WeakKeyException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
//Asserts correct/expected behavior discussed in https://github.com/jwtk/jjwt/issues/20
|
||||
@Test
|
||||
void testParseClaimsJwsWithUnsignedJwt() {
|
||||
|
||||
//create random signing key for testing:
|
||||
byte[] key = MacProvider.generateKey().getEncoded()
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.HS256
|
||||
byte[] key = Keys.secretKeyFor(alg).encoded
|
||||
|
||||
String notSigned = Jwts.builder().setSubject("Foo").compact();
|
||||
String notSigned = Jwts.builder().setSubject("Foo").compact()
|
||||
|
||||
try {
|
||||
Jwts.parser().setSigningKey(key).parseClaimsJws(notSigned);
|
||||
Jwts.parser().setSigningKey(key).parseClaimsJws(notSigned)
|
||||
fail('parseClaimsJws must fail for unsigned JWTs')
|
||||
} catch (UnsupportedJwtException expected) {
|
||||
assertEquals expected.message, 'Unsigned Claims JWTs are not supported.'
|
||||
|
@ -587,27 +611,28 @@ class JwtsTest {
|
|||
void testForgedTokenWithSwappedHeaderUsingNoneAlgorithm() {
|
||||
|
||||
//create random signing key for testing:
|
||||
byte[] key = MacProvider.generateKey().getEncoded()
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.HS256
|
||||
byte[] key = Keys.secretKeyFor(alg).encoded
|
||||
|
||||
//this is a 'real', valid JWT:
|
||||
String compact = Jwts.builder().setSubject("Joe").signWith(SignatureAlgorithm.HS256, key).compact();
|
||||
String compact = Jwts.builder().setSubject("Joe").signWith(alg, key).compact()
|
||||
|
||||
//Now strip off the signature so we can add it back in later on a forged token:
|
||||
int i = compact.lastIndexOf('.');
|
||||
String signature = compact.substring(i + 1);
|
||||
int i = compact.lastIndexOf('.')
|
||||
String signature = compact.substring(i + 1)
|
||||
|
||||
//now let's create a fake header and payload with whatever we want (without signing):
|
||||
String forged = Jwts.builder().setSubject("Not Joe").compact();
|
||||
String forged = Jwts.builder().setSubject("Not Joe").compact()
|
||||
|
||||
//assert that our forged header has a 'NONE' algorithm:
|
||||
assertEquals Jwts.parser().parseClaimsJwt(forged).getHeader().get('alg'), 'none'
|
||||
|
||||
//now let's forge it by appending the signature the server expects:
|
||||
forged += signature;
|
||||
forged += signature
|
||||
|
||||
//now assert that, when the server tries to parse the forged token, parsing fails:
|
||||
try {
|
||||
Jwts.parser().setSigningKey(key).parse(forged);
|
||||
Jwts.parser().setSigningKey(key).parse(forged)
|
||||
fail("Parsing must fail for a forged token.")
|
||||
} catch (MalformedJwtException expected) {
|
||||
assertEquals expected.message, 'JWT string has a digest/signature, but the header does not reference a valid signature algorithm.'
|
||||
|
@ -619,9 +644,9 @@ class JwtsTest {
|
|||
void testParseForgedRsaPublicKeyAsHmacTokenVerifiedWithTheRsaPrivateKey() {
|
||||
|
||||
//Create a legitimate RSA public and private key pair:
|
||||
KeyPair kp = RsaProvider.generateKeyPair(1024)
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
PrivateKey privateKey = kp.getPrivate();
|
||||
KeyPair kp = Keys.keyPairFor(SignatureAlgorithm.RS256)
|
||||
PublicKey publicKey = kp.getPublic()
|
||||
PrivateKey privateKey = kp.getPrivate()
|
||||
|
||||
String header = base64Url(toJson(['alg': 'HS256']))
|
||||
String body = base64Url(toJson('foo'))
|
||||
|
@ -651,7 +676,7 @@ class JwtsTest {
|
|||
void testParseForgedRsaPublicKeyAsHmacTokenVerifiedWithTheRsaPublicKey() {
|
||||
|
||||
//Create a legitimate RSA public and private key pair:
|
||||
KeyPair kp = RsaProvider.generateKeyPair(1024)
|
||||
KeyPair kp = Keys.keyPairFor(SignatureAlgorithm.RS256)
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
//PrivateKey privateKey = kp.getPrivate();
|
||||
|
||||
|
@ -683,7 +708,7 @@ class JwtsTest {
|
|||
void testParseForgedEllipticCurvePublicKeyAsHmacToken() {
|
||||
|
||||
//Create a legitimate RSA public and private key pair:
|
||||
KeyPair kp = EllipticCurveProvider.generateKeyPair()
|
||||
KeyPair kp = Keys.keyPairFor(SignatureAlgorithm.ES256)
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
//PrivateKey privateKey = kp.getPrivate();
|
||||
|
||||
|
@ -703,29 +728,29 @@ class JwtsTest {
|
|||
|
||||
// Assert that the parser does not recognized the forged token:
|
||||
try {
|
||||
Jwts.parser().setSigningKey(publicKey).parse(forged);
|
||||
Jwts.parser().setSigningKey(publicKey).parse(forged)
|
||||
fail("Forged token must not be successfully parsed.")
|
||||
} catch (UnsupportedJwtException expected) {
|
||||
assertTrue expected.getMessage().startsWith('The parsed JWT indicates it was signed with the')
|
||||
}
|
||||
}
|
||||
|
||||
static void testRsa(SignatureAlgorithm alg, int keySize = 1024, boolean verifyWithPrivateKey = false) {
|
||||
static void testRsa(SignatureAlgorithm alg, boolean verifyWithPrivateKey = false) {
|
||||
|
||||
KeyPair kp = RsaProvider.generateKeyPair(keySize)
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
PrivateKey privateKey = kp.getPrivate();
|
||||
KeyPair kp = Keys.keyPairFor(alg)
|
||||
PublicKey publicKey = kp.getPublic()
|
||||
PrivateKey privateKey = kp.getPrivate()
|
||||
|
||||
def claims = [iss: 'joe', exp: later(), 'http://example.com/is_root': true]
|
||||
|
||||
String jwt = Jwts.builder().setClaims(claims).signWith(alg, privateKey).compact();
|
||||
String jwt = Jwts.builder().setClaims(claims).signWith(alg, privateKey).compact()
|
||||
|
||||
def key = publicKey;
|
||||
def key = publicKey
|
||||
if (verifyWithPrivateKey) {
|
||||
key = privateKey;
|
||||
key = privateKey
|
||||
}
|
||||
|
||||
def token = Jwts.parser().setSigningKey(key).parse(jwt);
|
||||
def token = Jwts.parser().setSigningKey(key).parse(jwt)
|
||||
|
||||
assert [alg: alg.name()] == token.header
|
||||
//noinspection GrEqualsBetweenInconvertibleTypes
|
||||
|
@ -733,12 +758,13 @@ class JwtsTest {
|
|||
}
|
||||
|
||||
static void testHmac(SignatureAlgorithm alg) {
|
||||
|
||||
//create random signing key for testing:
|
||||
byte[] key = MacProvider.generateKey().encoded
|
||||
byte[] key = Keys.secretKeyFor(alg).encoded
|
||||
|
||||
def claims = [iss: 'joe', exp: later(), 'http://example.com/is_root': true]
|
||||
|
||||
String jwt = Jwts.builder().setClaims(claims).signWith(alg, key).compact();
|
||||
String jwt = Jwts.builder().setClaims(claims).signWith(alg, key).compact()
|
||||
|
||||
def token = Jwts.parser().setSigningKey(key).parse(jwt)
|
||||
|
||||
|
@ -749,20 +775,20 @@ class JwtsTest {
|
|||
|
||||
static void testEC(SignatureAlgorithm alg, boolean verifyWithPrivateKey = false) {
|
||||
|
||||
KeyPair pair = EllipticCurveProvider.generateKeyPair(alg)
|
||||
KeyPair pair = Keys.keyPairFor(alg)
|
||||
PublicKey publicKey = pair.getPublic()
|
||||
PrivateKey privateKey = pair.getPrivate()
|
||||
|
||||
def claims = [iss: 'joe', exp: later(), 'http://example.com/is_root': true]
|
||||
|
||||
String jwt = Jwts.builder().setClaims(claims).signWith(alg, privateKey).compact();
|
||||
String jwt = Jwts.builder().setClaims(claims).signWith(alg, privateKey).compact()
|
||||
|
||||
def key = publicKey;
|
||||
def key = publicKey
|
||||
if (verifyWithPrivateKey) {
|
||||
key = privateKey;
|
||||
key = privateKey
|
||||
}
|
||||
|
||||
def token = Jwts.parser().setSigningKey(key).parse(jwt);
|
||||
def token = Jwts.parser().setSigningKey(key).parse(jwt)
|
||||
|
||||
assert token.header == [alg: alg.name()]
|
||||
//noinspection GrEqualsBetweenInconvertibleTypes
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package io.jsonwebtoken
|
||||
|
||||
import io.jsonwebtoken.impl.crypto.RsaProvider
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
|
@ -26,9 +26,11 @@ class RsaSigningKeyResolverAdapterTest {
|
|||
@Test
|
||||
void testResolveClaimsSigningKeyWithRsaKey() {
|
||||
|
||||
def pair = RsaProvider.generateKeyPair(1024) //real apps should use 4096 or better. We're only reducing the size here so the tests are fast
|
||||
def alg = SignatureAlgorithm.RS256
|
||||
|
||||
def compact = Jwts.builder().claim('foo', 'bar').signWith(SignatureAlgorithm.RS256, pair.private).compact()
|
||||
def pair = Keys.keyPairFor(alg)
|
||||
|
||||
def compact = Jwts.builder().claim('foo', 'bar').signWith(alg, pair.private).compact()
|
||||
|
||||
Jws<Claims> jws = Jwts.parser().setSigningKey(pair.public).parseClaimsJws(compact)
|
||||
|
||||
|
|
|
@ -18,9 +18,11 @@ package io.jsonwebtoken.impl
|
|||
import io.jsonwebtoken.JwsHeader
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.impl.crypto.MacProvider
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertSame
|
||||
|
||||
class DefaultJwsTest {
|
||||
|
||||
|
@ -38,8 +40,9 @@ class DefaultJwsTest {
|
|||
@Test
|
||||
void testToString() {
|
||||
//create random signing key for testing:
|
||||
byte[] key = MacProvider.generateKey().encoded
|
||||
String compact = Jwts.builder().claim('foo', 'bar').signWith(SignatureAlgorithm.HS256, key).compact();
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.HS256
|
||||
byte[] key = Keys.secretKeyFor(alg).encoded
|
||||
String compact = Jwts.builder().claim('foo', 'bar').signWith(alg, key).compact();
|
||||
int i = compact.lastIndexOf('.')
|
||||
String signature = compact.substring(i + 1)
|
||||
def jws = Jwts.parser().setSigningKey(key).parseClaimsJws(compact)
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
package io.jsonwebtoken.impl
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import io.jsonwebtoken.CompressionCodecs
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.io.Encoder
|
||||
import io.jsonwebtoken.io.EncodingException
|
||||
import io.jsonwebtoken.CompressionCodecs
|
||||
import io.jsonwebtoken.impl.crypto.MacProvider
|
||||
import io.jsonwebtoken.io.SerializationException
|
||||
import io.jsonwebtoken.io.Serializer
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
@ -174,8 +174,9 @@ class DefaultJwtBuilderTest {
|
|||
def b = new DefaultJwtBuilder()
|
||||
b.setHeader(Jwts.jwsHeader().setKeyId('a'))
|
||||
b.setPayload('foo')
|
||||
def key = MacProvider.generateKey()
|
||||
b.signWith(SignatureAlgorithm.HS256, key)
|
||||
def alg = SignatureAlgorithm.HS256
|
||||
def key = Keys.secretKeyFor(alg)
|
||||
b.signWith(alg, key)
|
||||
b.compact()
|
||||
}
|
||||
|
||||
|
@ -338,14 +339,14 @@ class DefaultJwtBuilderTest {
|
|||
def serializer = new Serializer() {
|
||||
@Override
|
||||
byte[] serialize(Object o) throws SerializationException {
|
||||
return objectMapper.writeValueAsBytes(o);
|
||||
return objectMapper.writeValueAsBytes(o)
|
||||
}
|
||||
}
|
||||
|
||||
def b = new DefaultJwtBuilder().serializeToJsonWith(serializer)
|
||||
assertSame serializer, b.serializer
|
||||
|
||||
def key = MacProvider.generateKey(SignatureAlgorithm.HS256)
|
||||
def key = Keys.secretKeyFor(SignatureAlgorithm.HS256)
|
||||
|
||||
String jws = b.signWith(SignatureAlgorithm.HS256, key)
|
||||
.claim('foo', 'bar')
|
||||
|
|
|
@ -4,13 +4,9 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.MalformedJwtException
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.io.Decoder
|
||||
import io.jsonwebtoken.io.DecodingException
|
||||
import io.jsonwebtoken.impl.crypto.MacProvider
|
||||
import io.jsonwebtoken.io.DeserializationException
|
||||
import io.jsonwebtoken.io.Deserializer
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.io.*
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import org.junit.Test
|
||||
|
||||
import javax.crypto.Mac
|
||||
|
@ -60,7 +56,7 @@ class DefaultJwtParserTest {
|
|||
def p = new DefaultJwtParser().deserializeJsonWith(deserializer)
|
||||
assertSame deserializer, p.deserializer
|
||||
|
||||
def key = MacProvider.generateKey(SignatureAlgorithm.HS256)
|
||||
def key = Keys.secretKeyFor(SignatureAlgorithm.HS256)
|
||||
|
||||
String jws = Jwts.builder().claim('foo', 'bar').signWith(SignatureAlgorithm.HS256, key).compact()
|
||||
|
||||
|
@ -74,7 +70,7 @@ class DefaultJwtParserTest {
|
|||
String body = Encoders.BASE64URL.encode('{"hello":"world"}'.getBytes(Strings.UTF_8))
|
||||
String compact = header + '.' + body + '.'
|
||||
|
||||
SecretKey key = MacProvider.generateKey(SignatureAlgorithm.HS256)
|
||||
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256)
|
||||
Mac mac = Mac.getInstance('HmacSHA256')
|
||||
mac.init(key)
|
||||
byte[] signatureBytes = mac.doFinal(compact.getBytes(Strings.UTF_8))
|
||||
|
@ -92,7 +88,7 @@ class DefaultJwtParserTest {
|
|||
String body = Encoders.BASE64URL.encode('{"hello":"world"}'.getBytes(Strings.UTF_8))
|
||||
String compact = header + '.' + body + '.'
|
||||
|
||||
SecretKey key = MacProvider.generateKey(SignatureAlgorithm.HS256)
|
||||
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256)
|
||||
Mac mac = Mac.getInstance('HmacSHA256')
|
||||
mac.init(key)
|
||||
byte[] signatureBytes = mac.doFinal(compact.getBytes(Strings.UTF_8))
|
||||
|
@ -110,7 +106,7 @@ class DefaultJwtParserTest {
|
|||
String body = Encoders.BASE64URL.encode('{"hello":"world"}'.getBytes(Strings.UTF_8))
|
||||
String compact = header + '.' + body + '.'
|
||||
|
||||
SecretKey key = MacProvider.generateKey(SignatureAlgorithm.HS256)
|
||||
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256)
|
||||
Mac mac = Mac.getInstance('HmacSHA256')
|
||||
mac.init(key)
|
||||
byte[] signatureBytes = mac.doFinal(compact.getBytes(Strings.UTF_8))
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.jsonwebtoken.impl.crypto
|
|||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.io.Decoders
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.assertNotNull
|
||||
|
@ -15,7 +16,7 @@ class DefaultJwtSignatureValidatorTest {
|
|||
void testDeprecatedTwoArgCtor() {
|
||||
|
||||
def alg = SignatureAlgorithm.HS256
|
||||
def key = MacProvider.generateKey(alg)
|
||||
def key = Keys.secretKeyFor(alg)
|
||||
def validator = new DefaultJwtSignatureValidator(alg, key)
|
||||
|
||||
assertNotNull validator.signatureValidator
|
||||
|
@ -28,7 +29,7 @@ class DefaultJwtSignatureValidatorTest {
|
|||
void testDeprecatedThreeArgCtor() {
|
||||
|
||||
def alg = SignatureAlgorithm.HS256
|
||||
def key = MacProvider.generateKey(alg)
|
||||
def key = Keys.secretKeyFor(alg)
|
||||
def validator = new DefaultJwtSignatureValidator(DefaultSignatureValidatorFactory.INSTANCE, alg, key)
|
||||
|
||||
assertNotNull validator.signatureValidator
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.jsonwebtoken.impl.crypto
|
|||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.assertNotNull
|
||||
|
@ -16,7 +17,7 @@ class DefaultJwtSignerTest {
|
|||
void testDeprecatedTwoArgCtor() {
|
||||
|
||||
def alg = SignatureAlgorithm.HS256
|
||||
def key = MacProvider.generateKey(alg)
|
||||
def key = Keys.secretKeyFor(alg)
|
||||
def signer = new DefaultJwtSigner(alg, key)
|
||||
|
||||
assertNotNull signer.signer
|
||||
|
@ -30,7 +31,7 @@ class DefaultJwtSignerTest {
|
|||
void testDeprecatedThreeArgCtor() {
|
||||
|
||||
def alg = SignatureAlgorithm.HS256
|
||||
def key = MacProvider.generateKey(alg)
|
||||
def key = Keys.secretKeyFor(alg)
|
||||
def signer = new DefaultJwtSigner(DefaultSignerFactory.INSTANCE, alg, key)
|
||||
|
||||
assertNotNull signer.signer
|
||||
|
|
|
@ -16,15 +16,19 @@
|
|||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
class DefaultSignatureValidatorFactoryTest {
|
||||
|
||||
@Test
|
||||
void testNoneAlgorithm() {
|
||||
try {
|
||||
new DefaultSignatureValidatorFactory().createSignatureValidator(SignatureAlgorithm.NONE, MacProvider.generateKey())
|
||||
new DefaultSignatureValidatorFactory().createSignatureValidator(
|
||||
SignatureAlgorithm.NONE, Keys.secretKeyFor(SignatureAlgorithm.HS256))
|
||||
fail()
|
||||
} catch (IllegalArgumentException iae) {
|
||||
assertEquals iae.message, "The 'NONE' algorithm cannot be used for signing."
|
||||
|
|
|
@ -16,8 +16,11 @@
|
|||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
class DefaultSignerFactoryTest {
|
||||
|
||||
|
@ -27,7 +30,7 @@ class DefaultSignerFactoryTest {
|
|||
def factory = new DefaultSignerFactory();
|
||||
|
||||
try {
|
||||
factory.createSigner(SignatureAlgorithm.NONE, MacProvider.generateKey());
|
||||
factory.createSigner(SignatureAlgorithm.NONE, Keys.secretKeyFor(SignatureAlgorithm.HS256))
|
||||
fail();
|
||||
} catch (IllegalArgumentException iae) {
|
||||
assertEquals iae.message, "The 'NONE' algorithm cannot be used for signing."
|
||||
|
|
|
@ -17,8 +17,8 @@ package io.jsonwebtoken.impl.crypto
|
|||
|
||||
import io.jsonwebtoken.JwtException
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import io.jsonwebtoken.io.Decoders
|
||||
import io.jsonwebtoken.security.SignatureException
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -143,6 +143,14 @@ class EllipticCurveSignatureValidatorTest {
|
|||
EllipticCurveProvider.transcodeSignatureToDER(signature)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPaddedSignatureToDER() {
|
||||
def signature = new byte[32]
|
||||
SignatureProvider.DEFAULT_SECURE_RANDOM.nextBytes(signature)
|
||||
signature[0] = 0 as byte
|
||||
EllipticCurveProvider.transcodeSignatureToDER(signature) //no exception
|
||||
}
|
||||
|
||||
@Test
|
||||
void edgeCaseSignatureToConcatLengthTest() {
|
||||
try {
|
||||
|
|
|
@ -17,7 +17,8 @@ package io.jsonwebtoken.impl.crypto
|
|||
|
||||
import io.jsonwebtoken.JwtException
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import io.jsonwebtoken.security.SignatureException
|
||||
import org.junit.Test
|
||||
|
||||
import java.security.InvalidKeyException
|
||||
|
@ -31,37 +32,40 @@ class EllipticCurveSignerTest {
|
|||
|
||||
@Test
|
||||
void testConstructorWithoutECAlg() {
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.HS256
|
||||
try {
|
||||
new EllipticCurveSigner(SignatureAlgorithm.HS256, MacProvider.generateKey());
|
||||
fail('EllipticCurveSigner should reject non ECPrivateKeys');
|
||||
new EllipticCurveSigner(alg, Keys.secretKeyFor(alg))
|
||||
fail('EllipticCurveSigner should reject non ECPrivateKeys')
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertEquals expected.message, 'SignatureAlgorithm must be an Elliptic Curve algorithm.';
|
||||
assertEquals expected.message, 'SignatureAlgorithm must be an Elliptic Curve algorithm.'
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructorWithoutECPrivateKey() {
|
||||
def key = MacProvider.generateKey()
|
||||
def key = Keys.secretKeyFor(SignatureAlgorithm.HS256)
|
||||
try {
|
||||
new EllipticCurveSigner(SignatureAlgorithm.ES256, key);
|
||||
new EllipticCurveSigner(SignatureAlgorithm.ES256, key)
|
||||
fail('EllipticCurveSigner should reject non ECPrivateKey instances.')
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertEquals expected.message, "Elliptic Curve signatures must be computed using an EC PrivateKey. The specified key of " +
|
||||
"type " + key.getClass().getName() + " is not an EC PrivateKey.";
|
||||
"type " + key.getClass().getName() + " is not an EC PrivateKey."
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoSignWithInvalidKeyException() {
|
||||
|
||||
KeyPair kp = EllipticCurveProvider.generateKeyPair()
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
PrivateKey privateKey = kp.getPrivate();
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.ES256
|
||||
|
||||
KeyPair kp = Keys.keyPairFor(alg)
|
||||
PublicKey publicKey = kp.getPublic()
|
||||
PrivateKey privateKey = kp.getPrivate()
|
||||
|
||||
String msg = 'foo'
|
||||
final InvalidKeyException ex = new InvalidKeyException(msg)
|
||||
|
||||
def signer = new EllipticCurveSigner(SignatureAlgorithm.ES256, privateKey) {
|
||||
def signer = new EllipticCurveSigner(alg, privateKey) {
|
||||
@Override
|
||||
protected byte[] doSign(byte[] data) throws InvalidKeyException, java.security.SignatureException {
|
||||
throw ex
|
||||
|
|
|
@ -17,31 +17,39 @@ package io.jsonwebtoken.impl.crypto
|
|||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
import javax.crypto.SecretKey
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
|
||||
class MacProviderTest {
|
||||
|
||||
private void testHmac(SignatureAlgorithm alg) {
|
||||
testHmac(alg, MacProvider.generateKey(alg))
|
||||
}
|
||||
|
||||
private void testHmac(SignatureAlgorithm alg, SecretKey key) {
|
||||
assertEquals alg.jcaName, key.algorithm
|
||||
assertEquals alg.digestLength / 8 as int, key.encoded.length
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefault() {
|
||||
byte[] bytes = MacProvider.generateKey().encoded
|
||||
assertEquals 64, bytes.length
|
||||
testHmac(SignatureAlgorithm.HS512, MacProvider.generateKey())
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHS256() {
|
||||
byte[] bytes = MacProvider.generateKey(SignatureAlgorithm.HS256).encoded
|
||||
assertEquals 32, bytes.length
|
||||
testHmac(SignatureAlgorithm.HS256)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHS384() {
|
||||
byte[] bytes = MacProvider.generateKey(SignatureAlgorithm.HS384).encoded
|
||||
assertEquals 48, bytes.length
|
||||
testHmac(SignatureAlgorithm.HS384)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHS512() {
|
||||
byte[] bytes = MacProvider.generateKey(SignatureAlgorithm.HS512).encoded
|
||||
assertEquals 64, bytes.length
|
||||
testHmac(SignatureAlgorithm.HS512)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,47 @@
|
|||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import io.jsonwebtoken.security.SignatureException
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
import javax.crypto.Mac
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.Key
|
||||
import java.security.NoSuchAlgorithmException
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class MacSignerTest {
|
||||
|
||||
private static final Random rng = new Random(); //doesn't need to be secure - we're just testing
|
||||
|
||||
@Test
|
||||
void testCtorArgNotASecretKey() {
|
||||
|
||||
def key = new Key() {
|
||||
@Override
|
||||
String getAlgorithm() {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
String getFormat() {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] getEncoded() {
|
||||
return new byte[0]
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
new MacSigner(SignatureAlgorithm.HS256, key)
|
||||
fail()
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoSuchAlgorithmException() {
|
||||
byte[] key = new byte[32];
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
|
||||
import javax.crypto.KeyGenerator
|
||||
import java.security.NoSuchAlgorithmException
|
||||
|
||||
import static org.easymock.EasyMock.eq
|
||||
import static org.easymock.EasyMock.expect
|
||||
import static org.junit.Assert.*
|
||||
import static org.powermock.api.easymock.PowerMock.*
|
||||
|
||||
/**
|
||||
* This needs to be a separate class beyond MacProviderTest because it mocks the KeyGenerator class which messes up
|
||||
* the other implementation tests in MacProviderTest.
|
||||
*/
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest([KeyGenerator])
|
||||
class PowermockMacProviderTest {
|
||||
|
||||
@Test
|
||||
void testNoSuchAlgorithm() {
|
||||
|
||||
mockStatic(KeyGenerator)
|
||||
|
||||
def alg = SignatureAlgorithm.HS256
|
||||
def ex = new NoSuchAlgorithmException('foo')
|
||||
|
||||
expect(KeyGenerator.getInstance(eq(alg.jcaName))).andThrow(ex)
|
||||
|
||||
replay KeyGenerator
|
||||
|
||||
try {
|
||||
MacProvider.generateKey(alg)
|
||||
fail()
|
||||
} catch (IllegalStateException e) {
|
||||
assertEquals 'The HmacSHA256 algorithm is not available. This should never happen on JDK 7 or later - ' +
|
||||
'please report this to the JJWT developers.', e.message
|
||||
assertSame ex, e.getCause()
|
||||
}
|
||||
|
||||
verify KeyGenerator
|
||||
|
||||
reset KeyGenerator
|
||||
}
|
||||
}
|
|
@ -16,7 +16,8 @@
|
|||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import io.jsonwebtoken.security.SignatureException
|
||||
import org.junit.Test
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.KeyPair
|
||||
|
@ -25,7 +26,6 @@ import java.security.interfaces.RSAPrivateKey
|
|||
import java.security.interfaces.RSAPublicKey
|
||||
import java.security.spec.PSSParameterSpec
|
||||
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class RsaProviderTest {
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import io.jsonwebtoken.security.SignatureException
|
||||
import org.junit.Test
|
||||
|
||||
import java.security.*
|
||||
|
@ -30,7 +31,7 @@ class RsaSignatureValidatorTest {
|
|||
@Test
|
||||
void testConstructorWithNonRsaKey() {
|
||||
try {
|
||||
new RsaSignatureValidator(SignatureAlgorithm.RS256, MacProvider.generateKey());
|
||||
new RsaSignatureValidator(SignatureAlgorithm.RS256, Keys.secretKeyFor(SignatureAlgorithm.HS256));
|
||||
fail()
|
||||
} catch (IllegalArgumentException iae) {
|
||||
assertEquals "RSA Signature validation requires either a RSAPublicKey or RSAPrivateKey instance.", iae.message
|
||||
|
@ -40,17 +41,16 @@ class RsaSignatureValidatorTest {
|
|||
@Test
|
||||
void testDoVerifyWithInvalidKeyException() {
|
||||
|
||||
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyGenerator.initialize(1024);
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.RS256
|
||||
|
||||
KeyPair kp = keyGenerator.genKeyPair();
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
PrivateKey privateKey = kp.getPrivate();
|
||||
KeyPair kp = Keys.keyPairFor(alg)
|
||||
PublicKey publicKey = kp.getPublic()
|
||||
PrivateKey privateKey = kp.getPrivate()
|
||||
|
||||
String msg = 'foo'
|
||||
final InvalidKeyException ex = new InvalidKeyException(msg)
|
||||
|
||||
RsaSignatureValidator v = new RsaSignatureValidator(SignatureAlgorithm.RS256, publicKey) {
|
||||
RsaSignatureValidator v = new RsaSignatureValidator(alg, publicKey) {
|
||||
@Override
|
||||
protected boolean doVerify(Signature sig, PublicKey pk, byte[] data, byte[] signature) throws InvalidKeyException, java.security.SignatureException {
|
||||
throw ex;
|
||||
|
|
|
@ -16,17 +16,12 @@
|
|||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import io.jsonwebtoken.security.SignatureException
|
||||
import org.junit.Test
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.KeyPair
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.MessageDigest
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.security.*
|
||||
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class RsaSignerTest {
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import io.jsonwebtoken.security.SignatureException
|
||||
import org.junit.Test
|
||||
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.Signature
|
||||
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class SignatureProviderTest {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package io.jsonwebtoken.crypto
|
||||
package io.jsonwebtoken.security
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.junit.Test
|
||||
|
@ -10,6 +10,7 @@ import java.security.PublicKey
|
|||
import java.security.interfaces.ECPrivateKey
|
||||
import java.security.interfaces.ECPublicKey
|
||||
import java.security.interfaces.RSAPrivateKey
|
||||
import java.security.interfaces.RSAPublicKey
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
|
@ -27,11 +28,9 @@ class KeysImplTest {
|
|||
|
||||
String name = alg.name()
|
||||
|
||||
int bitLength = name.equalsIgnoreCase("NONE") ? 0 : name.substring(2).toInteger()
|
||||
|
||||
if (name.startsWith('H')) {
|
||||
if (alg.isHmac()) {
|
||||
SecretKey key = Keys.secretKeyFor(alg)
|
||||
assertEquals bitLength, key.getEncoded().length * 8 //convert byte count to bit count
|
||||
assertEquals alg.minKeyLength, key.getEncoded().length * 8 //convert byte count to bit count
|
||||
assertEquals alg.jcaName, key.algorithm
|
||||
} else {
|
||||
try {
|
||||
|
@ -51,40 +50,40 @@ class KeysImplTest {
|
|||
for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
|
||||
String name = alg.name()
|
||||
int bitLength = name.equalsIgnoreCase("NONE") ? 0 : name.substring(2).toInteger()
|
||||
|
||||
if (name.startsWith('R') || name.startsWith('P')) {
|
||||
if (alg.isRsa()) {
|
||||
|
||||
KeyPair pair = Keys.keyPairFor(alg)
|
||||
assertNotNull pair
|
||||
|
||||
PublicKey pub = pair.getPublic()
|
||||
assert pub instanceof RSAPublicKey
|
||||
assertEquals alg.familyName, pub.algorithm
|
||||
assertEquals alg.digestLength * 8, pub.modulus.bitLength()
|
||||
|
||||
PrivateKey priv = pair.getPrivate()
|
||||
assert priv instanceof RSAPrivateKey
|
||||
assertEquals alg.familyName, pub.algorithm
|
||||
assertEquals alg.familyName, priv.algorithm
|
||||
assertEquals bitLength * 8, priv.modulus.bitLength()
|
||||
assertEquals alg.digestLength * 8, priv.modulus.bitLength()
|
||||
|
||||
} else if (name.startsWith('E')) {
|
||||
} else if (alg.isEllipticCurve()) {
|
||||
|
||||
KeyPair pair = Keys.keyPairFor(alg);
|
||||
assertNotNull pair
|
||||
|
||||
if (alg == SignatureAlgorithm.ES512) {
|
||||
bitLength = 521
|
||||
}
|
||||
|
||||
String asn1oid = "secp${bitLength}r1"
|
||||
String asn1oid = "secp${alg.minKeyLength}r1"
|
||||
|
||||
PublicKey pub = pair.getPublic()
|
||||
assert pub instanceof ECPublicKey
|
||||
assertEquals "ECDSA", pub.algorithm
|
||||
assertEquals asn1oid, pub.params.name
|
||||
assertEquals bitLength, pub.params.order.bitLength()
|
||||
assertEquals alg.minKeyLength, pub.params.order.bitLength()
|
||||
|
||||
PrivateKey priv = pair.getPrivate()
|
||||
assert priv instanceof ECPrivateKey
|
||||
assertEquals "ECDSA", priv.algorithm
|
||||
assertEquals asn1oid, priv.params.name
|
||||
assertEquals alg.minKeyLength, priv.params.order.bitLength()
|
||||
|
||||
} else {
|
||||
try {
|
Loading…
Reference in New Issue