mirror of https://github.com/jwtk/jjwt.git
Merge pull request #28 from jwtk/elliptic-curve
Elliptic Curve algorithms finished!
This commit is contained in:
commit
d74a921487
|
@ -75,7 +75,7 @@ try {
|
|||
|
||||
* Creating and parsing plaintext compact JWTs
|
||||
|
||||
* Creating, parsing and verifying digitally signed compact JWTs (aka JWSs) with the following algorithms:
|
||||
* Creating, parsing and verifying digitally signed compact JWTs (aka JWSs) with all standard JWS algorithms:
|
||||
* HS256: HMAC using SHA-256
|
||||
* HS384: HMAC using SHA-384
|
||||
* HS512: HMAC using SHA-512
|
||||
|
@ -85,11 +85,13 @@ try {
|
|||
* PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256
|
||||
* PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384
|
||||
* PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512
|
||||
* ES256: ECDSA using P-256 and SHA-256
|
||||
* ES384: ECDSA using P-384 and SHA-384
|
||||
* ES512: ECDSA using P-512 and SHA-512
|
||||
|
||||
## Currently Unsupported Features
|
||||
|
||||
* [Non-compact](https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-7.2) serialization and parsing.
|
||||
* Elliptic Curve signature algorithms `ES256`, `ES384` and `ES512`.
|
||||
* JWE (Encryption for JWT)
|
||||
|
||||
These feature sets will be implemented in a future release when possible. Community contributions are welcome!
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -102,7 +102,7 @@
|
|||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<version>${bouncycastle.version}</version>
|
||||
<scope>runtime</scope> <!-- Not required at compile time -->
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
|
|
@ -29,7 +29,9 @@ import java.util.Map;
|
|||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
public class Jwts {
|
||||
public final class Jwts {
|
||||
|
||||
private Jwts(){}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Header} instance suitable for <em>plaintext</em> (not digitally signed) JWTs. As this
|
||||
|
|
|
@ -26,67 +26,67 @@ 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", null, false),
|
||||
NONE("none", "No digital signature or MAC performed", "None", null, false),
|
||||
|
||||
/** JWA algorithm name for {@code HMAC using SHA-256} */
|
||||
HS256("HS256", "HMAC using SHA-256", "HmacSHA256", true),
|
||||
HS256("HS256", "HMAC using SHA-256", "HMAC", "HmacSHA256", true),
|
||||
|
||||
/** JWA algorithm name for {@code HMAC using SHA-384} */
|
||||
HS384("HS384", "HMAC using SHA-384", "HmacSHA384", true),
|
||||
HS384("HS384", "HMAC using SHA-384", "HMAC", "HmacSHA384", true),
|
||||
|
||||
/** JWA algorithm name for {@code HMAC using SHA-512} */
|
||||
HS512("HS512", "HMAC using SHA-512", "HmacSHA512", true),
|
||||
HS512("HS512", "HMAC using SHA-512", "HMAC", "HmacSHA512", true),
|
||||
|
||||
/** JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-256} */
|
||||
RS256("RS256", "RSASSA-PKCS-v1_5 using SHA-256", "SHA256withRSA", true),
|
||||
RS256("RS256", "RSASSA-PKCS-v1_5 using SHA-256", "RSA", "SHA256withRSA", true),
|
||||
|
||||
/** JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-384} */
|
||||
RS384("RS384", "RSASSA-PKCS-v1_5 using SHA-384", "SHA384withRSA", true),
|
||||
RS384("RS384", "RSASSA-PKCS-v1_5 using SHA-384", "RSA", "SHA384withRSA", true),
|
||||
|
||||
/** JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-512} */
|
||||
RS512("RS512", "RSASSA-PKCS-v1_5 using SHA-512", "SHA512withRSA", true),
|
||||
RS512("RS512", "RSASSA-PKCS-v1_5 using SHA-512", "RSA", "SHA512withRSA", true),
|
||||
|
||||
/**
|
||||
* 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", "secp256r1", false),
|
||||
ES256("ES256", "ECDSA using P-256 and SHA-256", "Elliptic Curve", "SHA256withECDSA", false),
|
||||
|
||||
/**
|
||||
* 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", "secp384r1", false),
|
||||
ES384("ES384", "ECDSA using P-384 and SHA-384", "Elliptic Curve", "SHA384withECDSA", false),
|
||||
|
||||
/**
|
||||
* JWA algorithm name for {@code ECDSA using P-512 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", "secp521r1", false),
|
||||
ES512("ES512", "ECDSA using P-512 and SHA-512", "Elliptic Curve", "SHA512withECDSA", false),
|
||||
|
||||
/**
|
||||
* 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", "SHA256withRSAandMGF1", false),
|
||||
PS256("PS256", "RSASSA-PSS using SHA-256 and MGF1 with SHA-256", "RSA", "SHA256withRSAandMGF1", false),
|
||||
|
||||
/**
|
||||
* 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", "SHA384withRSAandMGF1", false),
|
||||
PS384("PS384", "RSASSA-PSS using SHA-384 and MGF1 with SHA-384", "RSA", "SHA384withRSAandMGF1", false),
|
||||
|
||||
/**
|
||||
* 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", "SHA512withRSAandMGF1", false);
|
||||
PS512("PS512", "RSASSA-PSS using SHA-512 and MGF1 with SHA-512", "RSA", "SHA512withRSAandMGF1", false);
|
||||
|
||||
static {
|
||||
RuntimeEnvironment.enableBouncyCastleIfPossible();
|
||||
|
@ -94,12 +94,14 @@ public enum SignatureAlgorithm {
|
|||
|
||||
private final String value;
|
||||
private final String description;
|
||||
private final String familyName;
|
||||
private final String jcaName;
|
||||
private final boolean jdkStandard;
|
||||
|
||||
private SignatureAlgorithm(String value, String description, String jcaName, boolean jdkStandard) {
|
||||
SignatureAlgorithm(String value, String description, String familyName, String jcaName, boolean jdkStandard) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.familyName = familyName;
|
||||
this.jcaName = jcaName;
|
||||
this.jdkStandard = jdkStandard;
|
||||
}
|
||||
|
@ -122,6 +124,78 @@ public enum SignatureAlgorithm {
|
|||
return description;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the cryptographic family name of the signature algorithm. The value returned is according to the
|
||||
* following table:
|
||||
*
|
||||
* <table>
|
||||
* <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>
|
||||
* </table>
|
||||
*
|
||||
* @return Returns the cryptographic family name of the signature algorithm.
|
||||
*
|
||||
* @since 0.5
|
||||
*/
|
||||
public String getFamilyName() {
|
||||
return familyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the JCA algorithm used to compute the signature.
|
||||
*
|
||||
|
|
|
@ -310,14 +310,18 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
|||
return new DefaultJwtSigner(alg, key);
|
||||
}
|
||||
|
||||
public static String base64UrlEncode(Object o, String errMsg) {
|
||||
protected String base64UrlEncode(Object o, String errMsg) {
|
||||
String s;
|
||||
try {
|
||||
s = OBJECT_MAPPER.writeValueAsString(o);
|
||||
s = toJson(o);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalStateException(errMsg, e);
|
||||
}
|
||||
|
||||
return TextCodec.BASE64URL.encode(s);
|
||||
}
|
||||
|
||||
protected String toJson(Object o) throws JsonProcessingException {
|
||||
return OBJECT_MAPPER.writeValueAsString(o);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -242,7 +242,7 @@ public class DefaultJwtParser implements JwtParser {
|
|||
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() +
|
||||
" may not be used to verify " + algName + " signatures. Because the specified " +
|
||||
" may not be used to validate " + algName + " signatures. Because the specified " +
|
||||
"signing key reflects a specific and expected algorithm, and the JWT does not reflect " +
|
||||
"this algorithm, it is likely that the JWT was not expected and therefore should not be " +
|
||||
"trusted. Another possibility is that the parser was configured with the incorrect " +
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl;
|
||||
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -30,9 +32,7 @@ public class JwtMap implements Map<String,Object> {
|
|||
}
|
||||
|
||||
public JwtMap(Map<String, Object> map) {
|
||||
if (map == null) {
|
||||
throw new IllegalArgumentException("Map argument cannot be null.");
|
||||
}
|
||||
Assert.notNull(map, "Map argument cannot be null.");
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,6 @@ public class DefaultSignatureValidatorFactory implements SignatureValidatorFacto
|
|||
Assert.notNull(key, "Signing Key cannot be null.");
|
||||
|
||||
switch (alg) {
|
||||
case NONE:
|
||||
throw new IllegalArgumentException("The 'NONE' algorithm cannot be used for signing.");
|
||||
case HS256:
|
||||
case HS384:
|
||||
case HS512:
|
||||
|
@ -46,11 +44,9 @@ public class DefaultSignatureValidatorFactory implements SignatureValidatorFacto
|
|||
case ES256:
|
||||
case ES384:
|
||||
case ES512:
|
||||
throw new UnsupportedOperationException("Elliptic Curve digests are not yet supported.");
|
||||
return new EllipticCurveSignatureValidator(alg, key);
|
||||
default:
|
||||
String msg = "Unrecognized algorithm '" + alg.name() + "'. This is a bug. Please submit a ticket " +
|
||||
"via the project issue tracker.";
|
||||
throw new IllegalStateException(msg);
|
||||
throw new IllegalArgumentException("The '" + alg.name() + "' algorithm cannot be used for signing.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,6 @@ public class DefaultSignerFactory implements SignerFactory {
|
|||
Assert.notNull(key, "Signing Key cannot be null.");
|
||||
|
||||
switch (alg) {
|
||||
case NONE:
|
||||
throw new IllegalArgumentException("The 'NONE' algorithm cannot be used for signing.");
|
||||
case HS256:
|
||||
case HS384:
|
||||
case HS512:
|
||||
|
@ -46,11 +44,9 @@ public class DefaultSignerFactory implements SignerFactory {
|
|||
case ES256:
|
||||
case ES384:
|
||||
case ES512:
|
||||
throw new UnsupportedOperationException("Elliptic Curve digests are not yet supported.");
|
||||
return new EllipticCurveSigner(alg, key);
|
||||
default:
|
||||
String msg = "Unrecognized algorithm '" + alg.name() + "'. This is a bug. Please submit a ticket " +
|
||||
"via the project issue tracker.";
|
||||
throw new IllegalStateException(msg);
|
||||
throw new IllegalArgumentException("The '" + alg.name() + "' algorithm cannot be used for signing.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class EllipticCurveProvider extends SignatureProvider {
|
||||
|
||||
private static final Map<SignatureAlgorithm, String> EC_CURVE_NAMES = createEcCurveNames();
|
||||
|
||||
private static Map<SignatureAlgorithm, String> createEcCurveNames() {
|
||||
Map<SignatureAlgorithm, String> m = new HashMap<SignatureAlgorithm, String>(); //alg to ASN1 OID name
|
||||
m.put(SignatureAlgorithm.ES256, "secp256r1");
|
||||
m.put(SignatureAlgorithm.ES384, "secp384r1");
|
||||
m.put(SignatureAlgorithm.ES512, "secp521r1");
|
||||
return m;
|
||||
}
|
||||
|
||||
protected EllipticCurveProvider(SignatureAlgorithm alg, Key key) {
|
||||
super(alg, key);
|
||||
Assert.isTrue(alg.isEllipticCurve(), "SignatureAlgorithm must be an Elliptic Curve algorithm.");
|
||||
}
|
||||
|
||||
public static KeyPair generateKeyPair() {
|
||||
return generateKeyPair(SignatureAlgorithm.ES512);
|
||||
}
|
||||
|
||||
public static KeyPair generateKeyPair(SignatureAlgorithm alg) {
|
||||
return generateKeyPair(alg, SignatureProvider.DEFAULT_SECURE_RANDOM);
|
||||
}
|
||||
|
||||
public static KeyPair generateKeyPair(SignatureAlgorithm alg, SecureRandom random) {
|
||||
return generateKeyPair("ECDSA", "BC", alg, random);
|
||||
}
|
||||
|
||||
public static KeyPair generateKeyPair(String jcaAlgorithmName, String jcaProviderName, SignatureAlgorithm alg, SecureRandom random) {
|
||||
Assert.isTrue(alg != null && alg.isEllipticCurve(), "SignatureAlgorithm argument must represent an Elliptic Curve algorithm.");
|
||||
try {
|
||||
KeyPairGenerator g = KeyPairGenerator.getInstance(jcaAlgorithmName, jcaProviderName);
|
||||
String paramSpecCurveName = EC_CURVE_NAMES.get(alg);
|
||||
g.initialize(org.bouncycastle.jce.ECNamedCurveTable.getParameterSpec(paramSpecCurveName), random);
|
||||
return g.generateKeyPair();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to generate Elliptic Curve KeyPair: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
|
||||
public class EllipticCurveSignatureValidator extends EllipticCurveProvider implements SignatureValidator {
|
||||
|
||||
private static final String NO_EC_PRIVATE_KEY_MSG =
|
||||
"Elliptic Curve signature validation requires an ECPublicKey. ECPrivateKeys may not be used.";
|
||||
|
||||
private static final String EC_PUBLIC_KEY_REQD_MSG =
|
||||
"Elliptic Curve Signature validation requires either an ECPublicKey instance.";
|
||||
|
||||
public EllipticCurveSignatureValidator(SignatureAlgorithm alg, Key key) {
|
||||
super(alg, key);
|
||||
Assert.isTrue(!(key instanceof ECPrivateKey), NO_EC_PRIVATE_KEY_MSG);
|
||||
Assert.isTrue(key instanceof ECPublicKey, EC_PUBLIC_KEY_REQD_MSG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(byte[] data, byte[] signature) {
|
||||
Signature sig = createSignatureInstance();
|
||||
PublicKey publicKey = (PublicKey) key;
|
||||
try {
|
||||
return doVerify(sig, publicKey, data, signature);
|
||||
} catch (Exception e) {
|
||||
String msg = "Unable to verify Elliptic Curve signature using configured ECPublicKey. " + e.getMessage();
|
||||
throw new SignatureException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean doVerify(Signature sig, PublicKey publicKey, byte[] data, byte[] signature)
|
||||
throws InvalidKeyException, java.security.SignatureException {
|
||||
sig.initVerify(publicKey);
|
||||
sig.update(data);
|
||||
return sig.verify(signature);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Signature;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
|
||||
public class EllipticCurveSigner extends EllipticCurveProvider implements Signer {
|
||||
|
||||
public EllipticCurveSigner(SignatureAlgorithm alg, Key key) {
|
||||
super(alg, key);
|
||||
if (!(key instanceof ECPrivateKey)) {
|
||||
String msg = "Elliptic Curve signatures must be computed using an ECPrivateKey. The specified key of " +
|
||||
"type " + key.getClass().getName() + " is not an ECPrivateKey.";
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sign(byte[] data) {
|
||||
try {
|
||||
return doSign(data);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new SignatureException("Invalid Elliptic Curve PrivateKey. " + e.getMessage(), e);
|
||||
} catch (java.security.SignatureException e) {
|
||||
throw new SignatureException("Unable to calculate signature using Elliptic Curve PrivateKey. " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] doSign(byte[] data) throws InvalidKeyException, java.security.SignatureException {
|
||||
PrivateKey privateKey = (PrivateKey)key;
|
||||
Signature sig = createSignatureInstance();
|
||||
sig.initSign(privateKey);
|
||||
sig.update(data);
|
||||
return sig.sign();
|
||||
}
|
||||
}
|
|
@ -18,12 +18,46 @@ package io.jsonwebtoken.impl.crypto;
|
|||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.Key;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Signature;
|
||||
|
||||
abstract class MacProvider extends SignatureProvider {
|
||||
public abstract class MacProvider extends SignatureProvider {
|
||||
|
||||
protected MacProvider(SignatureAlgorithm alg, Key key) {
|
||||
super(alg, key);
|
||||
Assert.isTrue(alg.isHmac(), "SignatureAlgorithm must be a HMAC SHA algorithm.");
|
||||
}
|
||||
|
||||
public static SecretKey generateKey() {
|
||||
return generateKey(SignatureAlgorithm.HS512);
|
||||
}
|
||||
|
||||
public static SecretKey generateKey(SignatureAlgorithm alg) {
|
||||
return generateKey(alg, SignatureProvider.DEFAULT_SECURE_RANDOM);
|
||||
}
|
||||
|
||||
public static SecretKey generateKey(SignatureAlgorithm alg, SecureRandom random) {
|
||||
|
||||
Assert.isTrue(alg.isHmac(), "SignatureAlgorithm argument must represent an HMAC algorithm.");
|
||||
|
||||
byte[] bytes;
|
||||
|
||||
switch(alg) {
|
||||
case HS256:
|
||||
bytes = new byte[32];
|
||||
break;
|
||||
case HS384:
|
||||
bytes = new byte[48];
|
||||
break;
|
||||
default:
|
||||
bytes = new byte[64];
|
||||
}
|
||||
|
||||
random.nextBytes(bytes);
|
||||
|
||||
return new SecretKeySpec(bytes, alg.getJcaName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,12 +21,38 @@ import io.jsonwebtoken.lang.Assert;
|
|||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Signature;
|
||||
import java.security.spec.MGF1ParameterSpec;
|
||||
import java.security.spec.PSSParameterSpec;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
abstract class RsaProvider extends SignatureProvider {
|
||||
public abstract class RsaProvider extends SignatureProvider {
|
||||
|
||||
private static final Map<SignatureAlgorithm, PSSParameterSpec> PSS_PARAMETER_SPECS = createPssParameterSpecs();
|
||||
|
||||
private static Map<SignatureAlgorithm, PSSParameterSpec> createPssParameterSpecs() {
|
||||
|
||||
Map<SignatureAlgorithm, PSSParameterSpec> m = new HashMap<SignatureAlgorithm, PSSParameterSpec>();
|
||||
|
||||
MGF1ParameterSpec ps = MGF1ParameterSpec.SHA256;
|
||||
PSSParameterSpec spec = new PSSParameterSpec(ps.getDigestAlgorithm(), "MGF1", ps, 32, 1);
|
||||
m.put(SignatureAlgorithm.PS256, spec);
|
||||
|
||||
ps = MGF1ParameterSpec.SHA384;
|
||||
spec = new PSSParameterSpec(ps.getDigestAlgorithm(), "MGF1", ps, 48, 1);
|
||||
m.put(SignatureAlgorithm.PS384, spec);
|
||||
|
||||
ps = MGF1ParameterSpec.SHA512;
|
||||
spec = new PSSParameterSpec(ps.getDigestAlgorithm(), "MGF1", ps, 64, 1);
|
||||
m.put(SignatureAlgorithm.PS512, spec);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
protected RsaProvider(SignatureAlgorithm alg, Key key) {
|
||||
super(alg, key);
|
||||
|
@ -35,58 +61,51 @@ abstract class RsaProvider extends SignatureProvider {
|
|||
|
||||
protected Signature createSignatureInstance() {
|
||||
|
||||
Signature sig = newSignatureInstance();
|
||||
Signature sig = super.createSignatureInstance();
|
||||
|
||||
if (alg.name().startsWith("PS")) {
|
||||
|
||||
MGF1ParameterSpec paramSpec;
|
||||
int saltLength;
|
||||
|
||||
switch (alg) {
|
||||
case PS256:
|
||||
paramSpec = MGF1ParameterSpec.SHA256;
|
||||
saltLength = 32;
|
||||
break;
|
||||
case PS384:
|
||||
paramSpec = MGF1ParameterSpec.SHA384;
|
||||
saltLength = 48;
|
||||
break;
|
||||
case PS512:
|
||||
paramSpec = MGF1ParameterSpec.SHA512;
|
||||
saltLength = 64;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported RSASSA-PSS algorithm: " + alg);
|
||||
}
|
||||
|
||||
PSSParameterSpec pssParamSpec =
|
||||
new PSSParameterSpec(paramSpec.getDigestAlgorithm(), "MGF1", paramSpec, saltLength, 1);
|
||||
|
||||
setParameter(sig, pssParamSpec);
|
||||
PSSParameterSpec spec = PSS_PARAMETER_SPECS.get(alg);
|
||||
if (spec != null) {
|
||||
setParameter(sig, spec);
|
||||
}
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
protected Signature newSignatureInstance() {
|
||||
try {
|
||||
return Signature.getInstance(alg.getJcaName());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
String msg = "Unavailable RSA Signature algorithm.";
|
||||
if (!alg.isJdkStandard()) {
|
||||
msg += " This is not a standard JDK algorithm. Try including BouncyCastle in the runtime classpath.";
|
||||
}
|
||||
throw new SignatureException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setParameter(Signature sig, PSSParameterSpec spec) {
|
||||
try {
|
||||
sig.setParameter(spec);
|
||||
doSetParameter(sig, spec);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
String msg = "Unsupported RSASSA-PSS parameter '" + spec + "': " + e.getMessage();
|
||||
throw new SignatureException(msg, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void doSetParameter(Signature sig, PSSParameterSpec spec) throws InvalidAlgorithmParameterException {
|
||||
sig.setParameter(spec);
|
||||
}
|
||||
|
||||
public static KeyPair generateKeyPair() {
|
||||
return generateKeyPair(4096);
|
||||
}
|
||||
|
||||
public static KeyPair generateKeyPair(int keySizeInBits) {
|
||||
return generateKeyPair(keySizeInBits, SignatureProvider.DEFAULT_SECURE_RANDOM);
|
||||
}
|
||||
|
||||
public static KeyPair generateKeyPair(int keySizeInBits, SecureRandom random) {
|
||||
return generateKeyPair("RSA", keySizeInBits, random);
|
||||
}
|
||||
|
||||
protected static KeyPair generateKeyPair(String jcaAlgName, int keySizeInBits, SecureRandom random) {
|
||||
KeyPairGenerator keyGenerator;
|
||||
try {
|
||||
keyGenerator = KeyPairGenerator.getInstance(jcaAlgName);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("Unable to obtain an RSA KeyPairGenerator: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
keyGenerator.initialize(keySizeInBits, random);
|
||||
|
||||
return keyGenerator.genKeyPair();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,14 +16,27 @@
|
|||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.RuntimeEnvironment;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Signature;
|
||||
import java.util.Random;
|
||||
|
||||
abstract class SignatureProvider {
|
||||
|
||||
public static final SecureRandom DEFAULT_SECURE_RANDOM;
|
||||
|
||||
static {
|
||||
DEFAULT_SECURE_RANDOM = new SecureRandom();
|
||||
DEFAULT_SECURE_RANDOM.nextBytes(new byte[64]);
|
||||
}
|
||||
|
||||
protected final SignatureAlgorithm alg;
|
||||
protected final Key key;
|
||||
protected final Key key;
|
||||
|
||||
protected SignatureProvider(SignatureAlgorithm alg, Key key) {
|
||||
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
|
||||
|
@ -31,4 +44,24 @@ abstract class SignatureProvider {
|
|||
this.alg = alg;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
protected Signature createSignatureInstance() {
|
||||
try {
|
||||
return getSignatureInstance();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
String msg = "Unavailable " + alg.getFamilyName() + " Signature algorithm '" + alg.getJcaName() + "'.";
|
||||
if (!alg.isJdkStandard() && !isBouncyCastleAvailable()) {
|
||||
msg += " This is not a standard JDK algorithm. Try including BouncyCastle in the runtime classpath.";
|
||||
}
|
||||
throw new SignatureException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Signature getSignatureInstance() throws NoSuchAlgorithmException {
|
||||
return Signature.getInstance(alg.getJcaName());
|
||||
}
|
||||
|
||||
protected boolean isBouncyCastleAvailable() {
|
||||
return RuntimeEnvironment.BOUNCY_CASTLE_AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ public class RuntimeEnvironment {
|
|||
|
||||
private static final AtomicBoolean bcLoaded = new AtomicBoolean(false);
|
||||
|
||||
public static final boolean BOUNCY_CASTLE_AVAILABLE = Classes.isAvailable(BC_PROVIDER_CLASS_NAME);
|
||||
|
||||
public static void enableBouncyCastleIfPossible() {
|
||||
|
||||
if (bcLoaded.get()) {
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken
|
||||
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class ExpiredJwtExceptionTest {
|
||||
|
||||
@Test
|
||||
void testOverloadedConstructor() {
|
||||
def header = Jwts.header()
|
||||
def claims = Jwts.claims()
|
||||
def msg = 'foo'
|
||||
def cause = new NullPointerException()
|
||||
|
||||
def ex = new ExpiredJwtException(header, claims, msg, cause)
|
||||
|
||||
assertSame ex.header, header
|
||||
assertSame ex.claims, claims
|
||||
assertEquals ex.message, msg
|
||||
assertSame ex.cause, cause
|
||||
}
|
||||
}
|
|
@ -19,21 +19,27 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||
import io.jsonwebtoken.impl.DefaultHeader
|
||||
import io.jsonwebtoken.impl.DefaultJwsHeader
|
||||
import io.jsonwebtoken.impl.TextCodec
|
||||
import io.jsonwebtoken.impl.crypto.EllipticCurveProvider
|
||||
import io.jsonwebtoken.impl.crypto.MacProvider
|
||||
import io.jsonwebtoken.impl.crypto.RsaProvider
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import java.nio.charset.Charset
|
||||
import java.security.KeyPair
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.security.SecureRandom
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class JwtsTest {
|
||||
|
||||
@Test
|
||||
void testSubclass() {
|
||||
new Jwts()
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHeaderWithNoArgs() {
|
||||
def header = Jwts.header()
|
||||
|
@ -374,14 +380,37 @@ class JwtsTest {
|
|||
testRsa(SignatureAlgorithm.RS512, 1024, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testES256() {
|
||||
testEC(SignatureAlgorithm.ES256)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testES384() {
|
||||
testEC(SignatureAlgorithm.ES384)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testES512() {
|
||||
testEC(SignatureAlgorithm.ES512)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testES256WithPrivateKeyValidation() {
|
||||
try {
|
||||
testEC(SignatureAlgorithm.ES256, true)
|
||||
fail("EC private keys cannot be used to validate EC signatures.")
|
||||
} catch (UnsupportedJwtException e) {
|
||||
assertEquals e.cause.message, "Elliptic Curve signature validation requires an ECPublicKey. ECPrivateKeys may not be used."
|
||||
}
|
||||
}
|
||||
|
||||
//Asserts correct/expected behavior discussed in https://github.com/jwtk/jjwt/issues/20
|
||||
@Test
|
||||
void testParseClaimsJwsWithUnsignedJwt() {
|
||||
|
||||
//create random signing key for testing:
|
||||
Random random = new SecureRandom();
|
||||
byte[] key = new byte[64];
|
||||
random.nextBytes(key);
|
||||
byte[] key = MacProvider.generateKey().getEncoded()
|
||||
|
||||
String notSigned = Jwts.builder().setSubject("Foo").compact();
|
||||
|
||||
|
@ -398,9 +427,7 @@ class JwtsTest {
|
|||
void testForgedTokenWithSwappedHeaderUsingNoneAlgorithm() {
|
||||
|
||||
//create random signing key for testing:
|
||||
Random random = new SecureRandom();
|
||||
byte[] key = new byte[64];
|
||||
random.nextBytes(key);
|
||||
byte[] key = MacProvider.generateKey().getEncoded()
|
||||
|
||||
//this is a 'real', valid JWT:
|
||||
String compact = Jwts.builder().setSubject("Joe").signWith(SignatureAlgorithm.HS256, key).compact();
|
||||
|
@ -432,9 +459,7 @@ class JwtsTest {
|
|||
void testParseForgedRsaPublicKeyAsHmacTokenVerifiedWithTheRsaPrivateKey() {
|
||||
|
||||
//Create a legitimate RSA public and private key pair:
|
||||
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyGenerator.initialize(1024);
|
||||
KeyPair kp = keyGenerator.genKeyPair();
|
||||
KeyPair kp = RsaProvider.generateKeyPair(1024)
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
PrivateKey privateKey = kp.getPrivate();
|
||||
|
||||
|
@ -467,11 +492,9 @@ class JwtsTest {
|
|||
void testParseForgedRsaPublicKeyAsHmacTokenVerifiedWithTheRsaPublicKey() {
|
||||
|
||||
//Create a legitimate RSA public and private key pair:
|
||||
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyGenerator.initialize(1024);
|
||||
KeyPair kp = keyGenerator.genKeyPair();
|
||||
KeyPair kp = RsaProvider.generateKeyPair(1024)
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
PrivateKey privateKey = kp.getPrivate();
|
||||
//PrivateKey privateKey = kp.getPrivate();
|
||||
|
||||
ObjectMapper om = new ObjectMapper()
|
||||
String header = TextCodec.BASE64URL.encode(om.writeValueAsString(['alg': 'HS256']))
|
||||
|
@ -497,16 +520,9 @@ class JwtsTest {
|
|||
}
|
||||
}
|
||||
|
||||
static void testRsa(SignatureAlgorithm alg) {
|
||||
testRsa(alg, 1024, false);
|
||||
}
|
||||
static void testRsa(SignatureAlgorithm alg, int keySize=1024, boolean verifyWithPrivateKey=false) {
|
||||
|
||||
static void testRsa(SignatureAlgorithm alg, int keySize, boolean verifyWithPrivateKey) {
|
||||
|
||||
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyGenerator.initialize(keySize);
|
||||
|
||||
KeyPair kp = keyGenerator.genKeyPair();
|
||||
KeyPair kp = RsaProvider.generateKeyPair(keySize)
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
PrivateKey privateKey = kp.getPrivate();
|
||||
|
||||
|
@ -529,9 +545,7 @@ class JwtsTest {
|
|||
|
||||
static void testHmac(SignatureAlgorithm alg) {
|
||||
//create random signing key for testing:
|
||||
Random random = new SecureRandom();
|
||||
byte[] key = new byte[64];
|
||||
random.nextBytes(key);
|
||||
byte[] key = MacProvider.generateKey().encoded
|
||||
|
||||
def claims = [iss: 'joe', exp: later(), 'http://example.com/is_root':true]
|
||||
|
||||
|
@ -543,5 +557,27 @@ class JwtsTest {
|
|||
|
||||
assertEquals token.body, claims
|
||||
}
|
||||
|
||||
static void testEC(SignatureAlgorithm alg, boolean verifyWithPrivateKey=false) {
|
||||
|
||||
KeyPair pair = EllipticCurveProvider.generateKeyPair(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();
|
||||
|
||||
def key = publicKey;
|
||||
if (verifyWithPrivateKey) {
|
||||
key = privateKey;
|
||||
}
|
||||
|
||||
def token = Jwts.parser().setSigningKey(key).parse(jwt);
|
||||
|
||||
assertEquals token.header, [alg: alg.name()]
|
||||
|
||||
assertEquals token.body, claims
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken
|
||||
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class PrematureJwtExceptionTest {
|
||||
|
||||
@Test
|
||||
void testOverloadedConstructor() {
|
||||
def header = Jwts.header()
|
||||
def claims = Jwts.claims()
|
||||
def msg = 'foo'
|
||||
def cause = new NullPointerException()
|
||||
|
||||
def ex = new PrematureJwtException(header, claims, msg, cause)
|
||||
|
||||
assertSame ex.header, header
|
||||
assertSame ex.claims, claims
|
||||
assertEquals ex.message, msg
|
||||
assertSame ex.cause, cause
|
||||
}
|
||||
}
|
|
@ -54,6 +54,15 @@ class SignatureAlgorithmTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHmacFamilyName() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
if (alg.name().startsWith("HS")) {
|
||||
assertEquals alg.getFamilyName(), "HMAC"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsRsa() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
|
@ -65,6 +74,15 @@ class SignatureAlgorithmTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRsaFamilyName() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
if (alg.name().startsWith("RS") || alg.name().startsWith("PS")) {
|
||||
assertEquals alg.getFamilyName(), "RSA"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsEllipticCurve() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
|
@ -76,6 +94,15 @@ class SignatureAlgorithmTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEllipticCurveFamilyName() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
if (alg.name().startsWith("ES")) {
|
||||
assertEquals alg.getFamilyName(), "Elliptic Curve"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsJdkStandard() {
|
||||
for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl
|
||||
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class DefaultHeaderTest {
|
||||
|
||||
@Test
|
||||
void testType() {
|
||||
|
||||
def h = new DefaultHeader()
|
||||
|
||||
h.setType('foo')
|
||||
assertEquals h.getType(), 'foo'
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContentType() {
|
||||
|
||||
def h = new DefaultHeader()
|
||||
|
||||
h.setContentType('bar')
|
||||
assertEquals h.getContentType(), 'bar'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl
|
||||
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class DefaultJwsHeaderTest {
|
||||
|
||||
@Test
|
||||
void testKeyId() {
|
||||
|
||||
def h = new DefaultJwsHeader()
|
||||
|
||||
h.setKeyId('foo')
|
||||
assertEquals h.getKeyId(), 'foo'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl
|
||||
|
||||
import io.jsonwebtoken.JwsHeader
|
||||
import io.jsonwebtoken.Jwts
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class DefaultJwsTest {
|
||||
|
||||
@Test
|
||||
void testConstructor() {
|
||||
|
||||
JwsHeader header = Jwts.jwsHeader()
|
||||
def jws = new DefaultJws<String>(header, 'foo', 'sig')
|
||||
|
||||
assertSame jws.getHeader(), header
|
||||
assertEquals jws.getBody(), 'foo'
|
||||
assertEquals jws.getSignature(), 'sig'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException
|
||||
import com.fasterxml.jackson.databind.JsonMappingException
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.impl.crypto.MacProvider
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class DefaultJwtBuilderTest {
|
||||
|
||||
@Test
|
||||
void testSetHeader() {
|
||||
def h = Jwts.header()
|
||||
def b = new DefaultJwtBuilder()
|
||||
b.setHeader(h)
|
||||
assertSame b.header, h
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetHeaderFromMap() {
|
||||
def m = [foo: 'bar']
|
||||
def b = new DefaultJwtBuilder()
|
||||
b.setHeader(m)
|
||||
assertNotNull b.header
|
||||
assertEquals b.header.size(), 1
|
||||
assertEquals b.header.foo, 'bar'
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetHeaderParams() {
|
||||
def m = [a: 'b', c: 'd']
|
||||
def b = new DefaultJwtBuilder()
|
||||
b.setHeaderParams(m)
|
||||
assertNotNull b.header
|
||||
assertEquals b.header.size(), 2
|
||||
assertEquals b.header.a, 'b'
|
||||
assertEquals b.header.c, 'd'
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetHeaderParam() {
|
||||
def b = new DefaultJwtBuilder()
|
||||
b.setHeaderParam('foo', 'bar')
|
||||
assertNotNull b.header
|
||||
assertEquals b.header.size(), 1
|
||||
assertEquals b.header.foo, 'bar'
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetClaims() {
|
||||
def b = new DefaultJwtBuilder()
|
||||
def c = Jwts.claims()
|
||||
b.setClaims(c)
|
||||
assertNotNull b.claims
|
||||
assertSame b.claims, c
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClaim() {
|
||||
def b = new DefaultJwtBuilder()
|
||||
b.claim('foo', 'bar')
|
||||
assertNotNull b.claims
|
||||
assertEquals b.claims.size(), 1
|
||||
assertEquals b.claims.foo, 'bar'
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExistingClaimsAndSetClaim() {
|
||||
def b = new DefaultJwtBuilder()
|
||||
def c = Jwts.claims()
|
||||
b.setClaims(c)
|
||||
b.claim('foo', 'bar')
|
||||
assertSame b.claims, c
|
||||
assertEquals b.claims.size(), 1
|
||||
assertEquals c.size(), 1
|
||||
assertEquals b.claims.foo, 'bar'
|
||||
assertEquals c.foo, 'bar'
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveClaimBySettingNullValue() {
|
||||
def b = new DefaultJwtBuilder()
|
||||
b.claim('foo', 'bar')
|
||||
assertNotNull b.claims
|
||||
assertEquals b.claims.size(), 1
|
||||
assertEquals b.claims.foo, 'bar'
|
||||
|
||||
b.claim('foo', null)
|
||||
assertNotNull b.claims
|
||||
assertNull b.claims.foo
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompactWithoutBody() {
|
||||
def b = new DefaultJwtBuilder()
|
||||
try {
|
||||
b.compact()
|
||||
fail()
|
||||
} catch (IllegalStateException ise) {
|
||||
assertEquals ise.message, "Either 'payload' or 'claims' must be specified."
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompactWithBothPayloadAndClaims() {
|
||||
def b = new DefaultJwtBuilder()
|
||||
b.setPayload('foo')
|
||||
b.claim('a', 'b')
|
||||
try {
|
||||
b.compact()
|
||||
fail()
|
||||
} catch (IllegalStateException ise) {
|
||||
assertEquals ise.message, "Both 'payload' and 'claims' cannot both be specified. Choose either one."
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompactWithBothKeyAndKeyBytes() {
|
||||
def b = new DefaultJwtBuilder()
|
||||
b.setPayload('foo')
|
||||
def key = MacProvider.generateKey()
|
||||
b.signWith(SignatureAlgorithm.HS256, key)
|
||||
b.signWith(SignatureAlgorithm.HS256, key.encoded)
|
||||
try {
|
||||
b.compact()
|
||||
fail()
|
||||
} catch (IllegalStateException ise) {
|
||||
assertEquals ise.message, "A key object and key bytes cannot both be specified. Choose either one."
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompactWithJwsHeader() {
|
||||
def b = new DefaultJwtBuilder()
|
||||
b.setHeader(Jwts.jwsHeader().setKeyId('a'))
|
||||
b.setPayload('foo')
|
||||
def key = MacProvider.generateKey()
|
||||
b.signWith(SignatureAlgorithm.HS256, key)
|
||||
b.compact()
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBase64UrlEncodeError() {
|
||||
|
||||
def b = new DefaultJwtBuilder() {
|
||||
@Override
|
||||
protected String toJson(Object o) throws JsonProcessingException {
|
||||
throw new JsonMappingException('foo')
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
b.setPayload('foo').compact()
|
||||
fail()
|
||||
} catch (IllegalStateException ise) {
|
||||
assertEquals ise.cause.message, 'foo'
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl
|
||||
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class JwtMapTest {
|
||||
|
||||
@Test
|
||||
void testToDateFromDate() {
|
||||
|
||||
def d = new Date()
|
||||
|
||||
Date date = JwtMap.toDate(d, 'foo')
|
||||
|
||||
assertSame date, d
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToDateFromString() {
|
||||
|
||||
Date d = new Date(2015, 1, 1, 12, 0, 0)
|
||||
|
||||
String s = (d.getTime() / 1000) + '' //JWT timestamps are in seconds - need to strip millis
|
||||
|
||||
Date date = JwtMap.toDate(s, 'foo')
|
||||
|
||||
assertEquals date, d
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToDateFromNonDateObject() {
|
||||
try {
|
||||
JwtMap.toDate(new Object() { @Override public String toString() {return 'hi'} }, 'foo')
|
||||
fail()
|
||||
} catch (IllegalStateException iae) {
|
||||
assertEquals iae.message, "Cannot convert 'foo' value [hi] to Date instance."
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsKey() {
|
||||
def m = new JwtMap()
|
||||
m.put('foo', 'bar')
|
||||
assertTrue m.containsKey('foo')
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsValue() {
|
||||
def m = new JwtMap()
|
||||
m.put('foo', 'bar')
|
||||
assertTrue m.containsValue('bar')
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveByPuttingNull() {
|
||||
def m = new JwtMap()
|
||||
m.put('foo', 'bar')
|
||||
assertTrue m.containsKey('foo')
|
||||
assertTrue m.containsValue('bar')
|
||||
m.put('foo', null)
|
||||
assertFalse m.containsKey('foo')
|
||||
assertFalse m.containsValue('bar')
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPutAll() {
|
||||
def m = new JwtMap();
|
||||
m.putAll([a: 'b', c: 'd'])
|
||||
assertEquals m.size(), 2
|
||||
assertEquals m.a, 'b'
|
||||
assertEquals m.c, 'd'
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPutAllWithNullArgument() {
|
||||
def m = new JwtMap();
|
||||
m.putAll((Map)null)
|
||||
assertEquals m.size(), 0
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClear() {
|
||||
def m = new JwtMap();
|
||||
m.put('foo', 'bar')
|
||||
assertEquals m.size(), 1
|
||||
m.clear()
|
||||
assertEquals m.size(), 0
|
||||
}
|
||||
|
||||
@Test
|
||||
void testKeySet() {
|
||||
def m = new JwtMap()
|
||||
m.putAll([a: 'b', c: 'd'])
|
||||
assertEquals( m.keySet(), ['a', 'c'] as Set)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValues() {
|
||||
def m = new JwtMap()
|
||||
m.putAll([a: 'b', c: 'd'])
|
||||
assertEquals( m.values(), ['b', 'd'] as Set)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class DefaultSignatureValidatorFactoryTest {
|
||||
|
||||
@Test
|
||||
void testNoneAlgorithm() {
|
||||
try {
|
||||
new DefaultSignatureValidatorFactory().createSignatureValidator(SignatureAlgorithm.NONE, MacProvider.generateKey())
|
||||
fail()
|
||||
} catch (IllegalArgumentException iae) {
|
||||
assertEquals iae.message, "The 'NONE' algorithm cannot be used for signing."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,19 +24,13 @@ import static org.testng.Assert.*
|
|||
|
||||
class DefaultSignerFactoryTest {
|
||||
|
||||
private static final Random rng = new Random(); //doesn't need to be secure - we're just testing
|
||||
|
||||
@Test
|
||||
void testCreateSignerWithNoneAlgorithm() {
|
||||
|
||||
byte[] keyBytes = new byte[32];
|
||||
rng.nextBytes(keyBytes);
|
||||
SecretKeySpec key = new SecretKeySpec(keyBytes, "foo");
|
||||
|
||||
def factory = new DefaultSignerFactory();
|
||||
|
||||
try {
|
||||
factory.createSigner(SignatureAlgorithm.NONE, key);
|
||||
factory.createSigner(SignatureAlgorithm.NONE, MacProvider.generateKey());
|
||||
fail();
|
||||
} catch (IllegalArgumentException iae) {
|
||||
assertEquals iae.message, "The 'NONE' algorithm cannot be used for signing."
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import java.security.KeyPair
|
||||
import java.security.NoSuchProviderException
|
||||
import java.security.interfaces.ECPrivateKey
|
||||
import java.security.interfaces.ECPublicKey
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
|
||||
class EllipticCurveProviderTest {
|
||||
|
||||
@Test
|
||||
void testGenerateKeyPair() {
|
||||
KeyPair pair = EllipticCurveProvider.generateKeyPair()
|
||||
assertNotNull pair
|
||||
assertTrue pair.public instanceof ECPublicKey
|
||||
assertTrue pair.private instanceof ECPrivateKey
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateKeyPairWithInvalidProviderName() {
|
||||
try {
|
||||
EllipticCurveProvider.generateKeyPair("ECDSA", "Foo", SignatureAlgorithm.ES256, null)
|
||||
fail()
|
||||
} catch (IllegalStateException ise) {
|
||||
assertEquals ise.message, "Unable to generate Elliptic Curve KeyPair: no such provider: Foo"
|
||||
assertTrue ise.cause instanceof NoSuchProviderException
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.PublicKey
|
||||
import java.security.Signature
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class EllipticCurveSignatureValidatorTest {
|
||||
|
||||
@Test
|
||||
void testDoVerifyWithInvalidKeyException() {
|
||||
|
||||
String msg = 'foo'
|
||||
final InvalidKeyException ex = new InvalidKeyException(msg)
|
||||
|
||||
def v = new EllipticCurveSignatureValidator(SignatureAlgorithm.ES512, EllipticCurveProvider.generateKeyPair().public) {
|
||||
@Override
|
||||
protected boolean doVerify(Signature sig, PublicKey pk, byte[] data, byte[] signature) throws InvalidKeyException, java.security.SignatureException {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[16]
|
||||
byte[] signature = new byte[16]
|
||||
SignatureProvider.DEFAULT_SECURE_RANDOM.nextBytes(bytes)
|
||||
SignatureProvider.DEFAULT_SECURE_RANDOM.nextBytes(signature)
|
||||
|
||||
try {
|
||||
v.isValid(bytes, signature)
|
||||
fail();
|
||||
} catch (SignatureException se) {
|
||||
assertEquals se.message, 'Unable to verify Elliptic Curve signature using configured ECPublicKey. ' + msg
|
||||
assertSame se.cause, ex
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.KeyPair
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class EllipticCurveSignerTest {
|
||||
|
||||
@Test
|
||||
void testConstructorWithoutECAlg() {
|
||||
try {
|
||||
new EllipticCurveSigner(SignatureAlgorithm.HS256, MacProvider.generateKey());
|
||||
fail('EllipticCurveSigner should reject non ECPrivateKeys');
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertEquals expected.message, 'SignatureAlgorithm must be an Elliptic Curve algorithm.';
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructorWithoutECPrivateKey() {
|
||||
def key = MacProvider.generateKey()
|
||||
try {
|
||||
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 ECPrivateKey. The specified key of " +
|
||||
"type " + key.getClass().getName() + " is not an ECPrivateKey.";
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoSignWithInvalidKeyException() {
|
||||
|
||||
KeyPair kp = EllipticCurveProvider.generateKeyPair()
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
PrivateKey privateKey = kp.getPrivate();
|
||||
|
||||
String msg = 'foo'
|
||||
final InvalidKeyException ex = new InvalidKeyException(msg)
|
||||
|
||||
def signer = new EllipticCurveSigner(SignatureAlgorithm.ES256, privateKey) {
|
||||
@Override
|
||||
protected byte[] doSign(byte[] data) throws InvalidKeyException, java.security.SignatureException {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[16]
|
||||
SignatureProvider.DEFAULT_SECURE_RANDOM.nextBytes(bytes)
|
||||
|
||||
try {
|
||||
signer.sign(bytes)
|
||||
fail();
|
||||
} catch (SignatureException se) {
|
||||
assertEquals se.message, 'Invalid Elliptic Curve PrivateKey. ' + msg
|
||||
assertSame se.cause, ex
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoSignWithJdkSignatureException() {
|
||||
|
||||
KeyPair kp = EllipticCurveProvider.generateKeyPair()
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
PrivateKey privateKey = kp.getPrivate();
|
||||
|
||||
String msg = 'foo'
|
||||
final java.security.SignatureException ex = new java.security.SignatureException(msg)
|
||||
|
||||
def signer = new EllipticCurveSigner(SignatureAlgorithm.ES256, privateKey) {
|
||||
@Override
|
||||
protected byte[] doSign(byte[] data) throws InvalidKeyException, java.security.SignatureException {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[16]
|
||||
SignatureProvider.DEFAULT_SECURE_RANDOM.nextBytes(bytes)
|
||||
|
||||
try {
|
||||
signer.sign(bytes)
|
||||
fail();
|
||||
} catch (SignatureException se) {
|
||||
assertEquals se.message, 'Unable to calculate signature using Elliptic Curve PrivateKey. ' + msg
|
||||
assertSame se.cause, ex
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
|
||||
class MacProviderTest {
|
||||
|
||||
@Test
|
||||
void testDefault() {
|
||||
byte[] bytes = MacProvider.generateKey().encoded
|
||||
assertEquals 64, bytes.length
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHS256() {
|
||||
byte[] bytes = MacProvider.generateKey(SignatureAlgorithm.HS256).encoded
|
||||
assertEquals 32, bytes.length
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHS384() {
|
||||
byte[] bytes = MacProvider.generateKey(SignatureAlgorithm.HS384).encoded
|
||||
assertEquals 48, bytes.length
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHS512() {
|
||||
byte[] bytes = MacProvider.generateKey(SignatureAlgorithm.HS512).encoded
|
||||
assertEquals 64, bytes.length
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.KeyPair
|
||||
import java.security.Signature
|
||||
import java.security.interfaces.RSAPrivateKey
|
||||
import java.security.interfaces.RSAPublicKey
|
||||
import java.security.spec.PSSParameterSpec
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class RsaProviderTest {
|
||||
|
||||
@Test
|
||||
void testGenerateKeyPair() {
|
||||
KeyPair pair = RsaProvider.generateKeyPair()
|
||||
assertNotNull pair
|
||||
assertTrue pair.public instanceof RSAPublicKey
|
||||
assertTrue pair.private instanceof RSAPrivateKey
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateKeyPairWithInvalidProviderName() {
|
||||
try {
|
||||
RsaProvider.generateKeyPair('foo', 1024, SignatureProvider.DEFAULT_SECURE_RANDOM)
|
||||
fail()
|
||||
} catch (IllegalStateException ise) {
|
||||
assertTrue ise.message.startsWith("Unable to obtain an RSA KeyPairGenerator: ")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSignatureInstanceWithInvalidPSSParameterSpecAlgorithm() {
|
||||
|
||||
def p = new RsaProvider(SignatureAlgorithm.PS256, RsaProvider.generateKeyPair(512).public) {
|
||||
@Override
|
||||
protected void doSetParameter(Signature sig, PSSParameterSpec spec) throws InvalidAlgorithmParameterException {
|
||||
throw new InvalidAlgorithmParameterException('foo')
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
p.createSignatureInstance()
|
||||
fail()
|
||||
} catch (SignatureException se) {
|
||||
assertTrue se.message.startsWith('Unsupported RSASSA-PSS parameter')
|
||||
assertEquals se.cause.message, 'foo'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,7 +28,6 @@ import java.security.PublicKey
|
|||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
|
||||
class RsaSignerTest {
|
||||
|
||||
private static final Random rng = new Random(); //doesn't need to be secure - we're just testing
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (C) 2015 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.Signature
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
class SignatureProviderTest {
|
||||
|
||||
@Test
|
||||
void testCreateSignatureInstanceNoSuchAlgorithm() {
|
||||
|
||||
def p = new SignatureProvider(SignatureAlgorithm.HS256, MacProvider.generateKey()) {
|
||||
@Override
|
||||
protected Signature getSignatureInstance() throws NoSuchAlgorithmException {
|
||||
throw new NoSuchAlgorithmException('foo')
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
p.createSignatureInstance()
|
||||
fail()
|
||||
} catch (SignatureException se) {
|
||||
assertEquals se.cause.message, 'foo'
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSignatureInstanceNoSuchAlgorithmNonStandardAlgorithm() {
|
||||
|
||||
def p = new SignatureProvider(SignatureAlgorithm.ES512, EllipticCurveProvider.generateKeyPair().getPublic()) {
|
||||
@Override
|
||||
protected Signature getSignatureInstance() throws NoSuchAlgorithmException {
|
||||
throw new NoSuchAlgorithmException('foo')
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
p.createSignatureInstance()
|
||||
fail()
|
||||
} catch (SignatureException se) {
|
||||
assertEquals se.cause.message, 'foo'
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSignatureInstanceNoSuchAlgorithmNonStandardAlgorithmWithoutBouncyCastle() {
|
||||
|
||||
def p = new SignatureProvider(SignatureAlgorithm.ES512, EllipticCurveProvider.generateKeyPair().getPublic()) {
|
||||
@Override
|
||||
protected Signature getSignatureInstance() throws NoSuchAlgorithmException {
|
||||
throw new NoSuchAlgorithmException('foo')
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isBouncyCastleAvailable() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
p.createSignatureInstance()
|
||||
fail()
|
||||
} catch (SignatureException se) {
|
||||
assertTrue se.message.contains('Try including BouncyCastle in the runtime classpath')
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue