mirror of https://github.com/jwtk/jjwt.git
Move EC curve utility functions (#803)
- Changed EC point multiplication montgomery ladder iteration to be a little faster (still has fixed number of operations of course) - Moved Weierstrass calc/utility methods to ECCurve instead of AbstractEcJwkFactory - Removed unnecessary Curves.java since StandardCurves is the preferred implementation - Renamed CurvesTest to StandardCurvesTest
This commit is contained in:
parent
095f446c37
commit
d6dac16042
|
@ -15,54 +15,26 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.impl.lang.CheckedFunction;
|
||||
import io.jsonwebtoken.impl.lang.Converters;
|
||||
import io.jsonwebtoken.impl.lang.Field;
|
||||
import io.jsonwebtoken.io.Encoders;
|
||||
import io.jsonwebtoken.security.Curve;
|
||||
import io.jsonwebtoken.security.Jwk;
|
||||
import io.jsonwebtoken.security.UnsupportedKeyException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.interfaces.ECKey;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.ECFieldFp;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.EllipticCurve;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Set;
|
||||
|
||||
abstract class AbstractEcJwkFactory<K extends Key & ECKey, J extends Jwk<K>> extends AbstractFamilyJwkFactory<K, J> {
|
||||
|
||||
private static final BigInteger TWO = BigInteger.valueOf(2);
|
||||
private static final BigInteger THREE = BigInteger.valueOf(3);
|
||||
private static final String UNSUPPORTED_CURVE_MSG = "The specified ECKey curve does not match a JWA standard curve id.";
|
||||
|
||||
protected static ECParameterSpec getCurveByJwaId(String jwaCurveId) {
|
||||
ECParameterSpec spec = null;
|
||||
Curve curve = Curves.findById(jwaCurveId);
|
||||
if (curve instanceof ECCurve) {
|
||||
ECCurve ecCurve = (ECCurve) curve;
|
||||
spec = ecCurve.toParameterSpec();
|
||||
}
|
||||
if (spec == null) {
|
||||
String msg = "Unrecognized JWA curve id '" + jwaCurveId + "'";
|
||||
protected static ECCurve getCurveByJwaId(String jwaCurveId) {
|
||||
ECCurve curve = ECCurve.findById(jwaCurveId);
|
||||
if (curve == null) {
|
||||
String msg = "Unrecognized JWA EC curve id '" + jwaCurveId + "'";
|
||||
throw new UnsupportedKeyException(msg);
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
protected static String getJwaIdByCurve(EllipticCurve curve) {
|
||||
ECCurve c = Curves.findBy(curve);
|
||||
if (c == null) {
|
||||
throw new UnsupportedKeyException(UNSUPPORTED_CURVE_MSG);
|
||||
}
|
||||
return c.getId();
|
||||
return curve;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,146 +57,7 @@ abstract class AbstractEcJwkFactory<K extends Key & ECKey, J extends Jwk<K>> ext
|
|||
return Encoders.BASE64URL.encode(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a given elliptic {@code curve} contains the specified {@code point}, {@code false}
|
||||
* otherwise. Assumes elliptic curves over finite fields adhering to the reduced (a.k.a short or narrow)
|
||||
* Weierstrass form:
|
||||
* <p>
|
||||
* <code>y<sup>2</sup> = x<sup>3</sup> + ax + b</code>
|
||||
* </p>
|
||||
*
|
||||
* @param curve the Elliptic Curve to check
|
||||
* @param point a point that may or may not be defined on the specified elliptic curve
|
||||
* @return {@code true} if a given elliptic curve contains the specified {@code point}, {@code false} otherwise.
|
||||
*/
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
static boolean contains(EllipticCurve curve, ECPoint point) {
|
||||
|
||||
if (ECPoint.POINT_INFINITY.equals(point)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final BigInteger a = curve.getA();
|
||||
final BigInteger b = curve.getB();
|
||||
final BigInteger x = point.getAffineX();
|
||||
final BigInteger y = point.getAffineY();
|
||||
|
||||
// The reduced Weierstrass form y^2 = x^3 + ax + b reflects an elliptic curve E over any field K (e.g. all real
|
||||
// numbers or all complex numbers, etc). For computational simplicity, cryptographic (e.g. NIST) elliptic curves
|
||||
// restrict K to be a field of integers modulo a prime number 'p'. As such, we apply modulo p (the field prime)
|
||||
// to the equation to account for the restricted field. For a nice overview of the math behind EC curves and
|
||||
// their application in cryptography, see
|
||||
// https://web.northeastern.edu/dummit/docs/cryptography_5_elliptic_curves_in_cryptography.pdf
|
||||
|
||||
final BigInteger p = ((ECFieldFp) curve.getField()).getP();
|
||||
|
||||
// Verify the point coordinates are in field range:
|
||||
if (x.compareTo(BigInteger.ZERO) < 0 || x.compareTo(p) >= 0 ||
|
||||
y.compareTo(BigInteger.ZERO) < 0 || y.compareTo(p) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally, assert Weierstrass form equality:
|
||||
final BigInteger lhs = y.modPow(TWO, p); //mod p to account for field prime
|
||||
final BigInteger rhs = x.modPow(THREE, p).add(a.multiply(x)).add(b).mod(p); //mod p to account for field prime
|
||||
return lhs.equals(rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply a point {@code p} by scalar {@code s} on the curve identified by {@code spec}.
|
||||
*
|
||||
* @param p the Elliptic Curve point to multiply
|
||||
* @param s the scalar value to multiply
|
||||
* @param spec the domain parameters that identify the Elliptic Curve containing point {@code p}.
|
||||
*/
|
||||
private static ECPoint multiply(ECPoint p, BigInteger s, ECParameterSpec spec) {
|
||||
if (ECPoint.POINT_INFINITY.equals(p)) {
|
||||
return p;
|
||||
}
|
||||
|
||||
EllipticCurve curve = spec.getCurve();
|
||||
BigInteger n = spec.getOrder();
|
||||
BigInteger k = s.mod(n);
|
||||
|
||||
ECPoint r0 = ECPoint.POINT_INFINITY;
|
||||
ECPoint r1 = p;
|
||||
|
||||
// Montgomery Ladder implementation to mitigate side-channel attacks (i.e. an 'add' operation and a 'double'
|
||||
// operation is calculated for every loop iteration, regardless if the 'add'' is needed or not)
|
||||
// See: https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder
|
||||
while (k.compareTo(BigInteger.ZERO) > 0) {
|
||||
ECPoint temp = add(r0, r1, curve);
|
||||
r0 = k.testBit(0) ? temp : r0;
|
||||
r1 = doublePoint(r1, curve);
|
||||
k = k.shiftRight(1);
|
||||
}
|
||||
|
||||
return r0;
|
||||
}
|
||||
|
||||
private static ECPoint add(ECPoint P, ECPoint Q, EllipticCurve curve) {
|
||||
|
||||
if (ECPoint.POINT_INFINITY.equals(P)) {
|
||||
return Q;
|
||||
} else if (ECPoint.POINT_INFINITY.equals(Q)) {
|
||||
return P;
|
||||
} else if (P.equals(Q)) {
|
||||
return doublePoint(P, curve);
|
||||
}
|
||||
|
||||
final BigInteger Px = P.getAffineX();
|
||||
final BigInteger Py = P.getAffineY();
|
||||
final BigInteger Qx = Q.getAffineX();
|
||||
final BigInteger Qy = Q.getAffineY();
|
||||
final BigInteger prime = ((ECFieldFp) curve.getField()).getP();
|
||||
final BigInteger slope = Qy.subtract(Py).multiply(Qx.subtract(Px).modInverse(prime)).mod(prime);
|
||||
final BigInteger Rx = slope.pow(2).subtract(Px).subtract(Qx).mod(prime);
|
||||
final BigInteger Ry = slope.multiply(Px.subtract(Rx)).subtract(Py).mod(prime);
|
||||
|
||||
return new ECPoint(Rx, Ry);
|
||||
}
|
||||
|
||||
private static ECPoint doublePoint(ECPoint P, EllipticCurve curve) {
|
||||
|
||||
if (ECPoint.POINT_INFINITY.equals(P)) {
|
||||
return P;
|
||||
}
|
||||
|
||||
final BigInteger Px = P.getAffineX();
|
||||
final BigInteger Py = P.getAffineY();
|
||||
final BigInteger p = ((ECFieldFp) curve.getField()).getP();
|
||||
final BigInteger a = curve.getA();
|
||||
final BigInteger s = THREE.multiply(Px.pow(2)).add(a).mod(p).multiply(TWO.multiply(Py).modInverse(p)).mod(p);
|
||||
final BigInteger x = s.pow(2).subtract(TWO.multiply(Px)).mod(p);
|
||||
final BigInteger y = s.multiply(Px.subtract(x)).subtract(Py).mod(p);
|
||||
|
||||
return new ECPoint(x, y);
|
||||
}
|
||||
|
||||
AbstractEcJwkFactory(Class<K> keyType, Set<Field<?>> fields) {
|
||||
super(DefaultEcPublicJwk.TYPE_VALUE, keyType, fields);
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
protected ECPublicKey derivePublic(KeyFactory keyFactory, ECPublicKeySpec spec) throws InvalidKeySpecException {
|
||||
return (ECPublicKey) keyFactory.generatePublic(spec);
|
||||
}
|
||||
|
||||
protected ECPublicKey derivePublic(final JwkContext<ECPrivateKey> ctx) {
|
||||
final ECPrivateKey key = ctx.getKey();
|
||||
final ECParameterSpec params = key.getParams();
|
||||
final ECPoint w = multiply(params.getGenerator(), key.getS(), params);
|
||||
final ECPublicKeySpec spec = new ECPublicKeySpec(w, params);
|
||||
return generateKey(ctx, ECPublicKey.class, new CheckedFunction<KeyFactory, ECPublicKey>() {
|
||||
@Override
|
||||
public ECPublicKey apply(KeyFactory kf) {
|
||||
try {
|
||||
return derivePublic(kf, spec);
|
||||
} catch (Exception e) {
|
||||
String msg = "Unable to derive ECPublicKey from ECPrivateKey: " + e.getMessage();
|
||||
throw new UnsupportedKeyException(msg, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022 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.security;
|
||||
|
||||
import io.jsonwebtoken.impl.lang.DefaultRegistry;
|
||||
import io.jsonwebtoken.impl.lang.Function;
|
||||
import io.jsonwebtoken.impl.lang.IdRegistry;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.lang.Registry;
|
||||
import io.jsonwebtoken.security.Curve;
|
||||
|
||||
import java.security.spec.EllipticCurve;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
public final class Curves {
|
||||
public static final Curve P_256 = new ECCurve("P-256", "secp256r1"); // JDK standard
|
||||
public static final Curve P_384 = new ECCurve("P-384", "secp384r1"); // JDK standard
|
||||
public static final Curve P_521 = new ECCurve("P-521", "secp521r1"); // JDK standard
|
||||
|
||||
private static final Collection<ECCurve> EC_CURVES = Collections.setOf((ECCurve) P_256, (ECCurve) P_384, (ECCurve) P_521);
|
||||
|
||||
static final Collection<Curve> VALUES = new LinkedHashSet<>();
|
||||
|
||||
static {
|
||||
VALUES.addAll(EC_CURVES);
|
||||
VALUES.addAll(EdwardsCurve.VALUES);
|
||||
}
|
||||
|
||||
private static final Registry<String, Curve> CURVES_BY_ID = new IdRegistry<>("Elliptic Curve", VALUES);
|
||||
private static final Registry<String, Curve> CURVES_BY_JCA_NAME = new DefaultRegistry<>(
|
||||
"Elliptic Curve", "JCA name", VALUES, new Function<Curve, String>() {
|
||||
@Override
|
||||
public String apply(Curve curve) {
|
||||
return ((DefaultCurve) curve).getJcaName();
|
||||
}
|
||||
});
|
||||
|
||||
private static final Registry<EllipticCurve, ECCurve> CURVES_BY_JCA_CURVE = new DefaultRegistry<>(
|
||||
"Elliptic Curve", "ECCurve instance", EC_CURVES, new Function<ECCurve, EllipticCurve>() {
|
||||
@Override
|
||||
public EllipticCurve apply(ECCurve curve) {
|
||||
return curve.toParameterSpec().getCurve();
|
||||
}
|
||||
});
|
||||
|
||||
//prevent instantiation
|
||||
private Curves() {
|
||||
}
|
||||
|
||||
public static Curve findById(String jwaId) {
|
||||
Assert.hasText(jwaId, "jwaId cannot be null or empty.");
|
||||
return CURVES_BY_ID.get(jwaId);
|
||||
}
|
||||
|
||||
public static Curve findByJcaName(String jcaName) {
|
||||
Assert.hasText(jcaName, "jcaName cannot be null or empty.");
|
||||
return CURVES_BY_JCA_NAME.get(jcaName);
|
||||
}
|
||||
|
||||
public static ECCurve findBy(EllipticCurve curve) {
|
||||
Assert.notNull(curve, "EllipticCurve argument cannot be null.");
|
||||
return CURVES_BY_JCA_CURVE.get(curve);
|
||||
}
|
||||
}
|
|
@ -16,17 +16,104 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.impl.lang.CheckedFunction;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.security.KeyPairBuilder;
|
||||
import io.jsonwebtoken.security.UnsupportedKeyException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.Key;
|
||||
import java.security.interfaces.ECKey;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.ECFieldFp;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.EllipticCurve;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ECCurve extends DefaultCurve {
|
||||
|
||||
private static final BigInteger TWO = BigInteger.valueOf(2);
|
||||
private static final BigInteger THREE = BigInteger.valueOf(3);
|
||||
|
||||
static final String KEY_PAIR_GENERATOR_JCA_NAME = "EC";
|
||||
|
||||
public static final ECCurve P256 = new ECCurve("P-256", "secp256r1"); // JDK standard
|
||||
public static final ECCurve P384 = new ECCurve("P-384", "secp384r1"); // JDK standard
|
||||
public static final ECCurve P521 = new ECCurve("P-521", "secp521r1"); // JDK standard
|
||||
|
||||
public static final Collection<ECCurve> VALUES = Collections.setOf(P256, P384, P521);
|
||||
private static final Map<String, ECCurve> BY_ID = new LinkedHashMap<>(3);
|
||||
private static final Map<EllipticCurve, ECCurve> BY_JCA_CURVE = new LinkedHashMap<>(3);
|
||||
|
||||
static {
|
||||
for (ECCurve curve : VALUES) {
|
||||
BY_ID.put(curve.getId(), curve);
|
||||
}
|
||||
for (ECCurve curve : VALUES) {
|
||||
BY_JCA_CURVE.put(curve.spec.getCurve(), curve);
|
||||
}
|
||||
}
|
||||
|
||||
static EllipticCurve assertJcaCurve(ECKey key) {
|
||||
Assert.notNull(key, "ECKey cannot be null.");
|
||||
ECParameterSpec spec = Assert.notNull(key.getParams(), "ECKey params() cannot be null.");
|
||||
return Assert.notNull(spec.getCurve(), "ECKey params().getCurve() cannot be null.");
|
||||
}
|
||||
|
||||
static ECCurve findById(String id) {
|
||||
return BY_ID.get(id);
|
||||
}
|
||||
|
||||
static ECCurve findByJcaCurve(EllipticCurve curve) {
|
||||
return BY_JCA_CURVE.get(curve);
|
||||
}
|
||||
|
||||
static ECCurve findByKey(Key key) {
|
||||
if (!(key instanceof ECKey)) {
|
||||
return null;
|
||||
}
|
||||
ECKey ecKey = (ECKey) key;
|
||||
ECParameterSpec spec = ecKey.getParams();
|
||||
if (spec == null) {
|
||||
return null;
|
||||
}
|
||||
EllipticCurve jcaCurve = spec.getCurve();
|
||||
ECCurve curve = BY_JCA_CURVE.get(jcaCurve);
|
||||
if (curve != null && key instanceof ECPublicKey) {
|
||||
ECPublicKey pub = (ECPublicKey) key;
|
||||
ECPoint w = pub.getW();
|
||||
if (w == null || !curve.contains(w)) { // don't support keys with a point not on its indicated curve
|
||||
curve = null;
|
||||
}
|
||||
}
|
||||
return curve;
|
||||
}
|
||||
|
||||
static ECCurve forKey(Key key) {
|
||||
ECCurve curve = findByKey(key);
|
||||
if (curve == null) {
|
||||
String msg = "Unable to determine JWA-standard Elliptic Curve for specified key: " +
|
||||
KeysBridge.toString(key);
|
||||
throw new UnsupportedKeyException(msg);
|
||||
}
|
||||
return curve;
|
||||
}
|
||||
|
||||
static ECPublicKeySpec publicKeySpec(ECPrivateKey key) throws IllegalArgumentException {
|
||||
EllipticCurve jcaCurve = assertJcaCurve(key);
|
||||
ECCurve curve = BY_JCA_CURVE.get(jcaCurve);
|
||||
Assert.notNull(curve, "There is no JWA-standard Elliptic Curve for specified ECPrivateKey.");
|
||||
final ECPoint w = curve.multiply(key.getS());
|
||||
return new ECPublicKeySpec(w, curve.spec);
|
||||
}
|
||||
|
||||
private final ECParameterSpec spec;
|
||||
|
||||
public ECCurve(String id, String jcaName) {
|
||||
|
@ -45,23 +132,161 @@ public class ECCurve extends DefaultCurve {
|
|||
return this.spec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPairBuilder keyPair() {
|
||||
return new DefaultKeyPairBuilder(KEY_PAIR_GENERATOR_JCA_NAME, toParameterSpec());
|
||||
}
|
||||
|
||||
boolean contains(Key key) {
|
||||
if (key instanceof ECPublicKey) {
|
||||
ECPublicKey pub = (ECPublicKey) key;
|
||||
ECParameterSpec pubSpec = pub.getParams();
|
||||
return pubSpec != null &&
|
||||
this.spec.getCurve().equals(pubSpec.getCurve()) &&
|
||||
contains(pub.getW());
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean contains(ECPoint point) {
|
||||
return contains(this.spec.getCurve(), point);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this elliptic curve contains the specified {@code point}, {@code false}
|
||||
* otherwise. Assumes elliptic curves over finite fields adhering to the reduced (a.k.a short or narrow)
|
||||
* Returns {@code true} if the specified curve contains the specified {@code point}, {@code false} otherwise.
|
||||
* Assumes elliptic curves over finite fields adhering to the reduced (a.k.a short or narrow)
|
||||
* Weierstrass form:
|
||||
* <p>
|
||||
* <code>y<sup>2</sup> = x<sup>3</sup> + ax + b</code>
|
||||
* </p>
|
||||
*
|
||||
* @param curve the EllipticCurve to check
|
||||
* @param point a point that may or may not be defined on this elliptic curve
|
||||
* @return {@code true} if this elliptic curve contains the specified {@code point}, {@code false} otherwise.
|
||||
* @return {@code true} if this curve contains the specified {@code point}, {@code false} otherwise.
|
||||
*/
|
||||
public boolean contains(ECPoint point) {
|
||||
return AbstractEcJwkFactory.contains(spec.getCurve(), point);
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
static boolean contains(EllipticCurve curve, ECPoint point) {
|
||||
|
||||
if (point == null || ECPoint.POINT_INFINITY.equals(point)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final BigInteger a = curve.getA();
|
||||
final BigInteger b = curve.getB();
|
||||
final BigInteger x = point.getAffineX();
|
||||
final BigInteger y = point.getAffineY();
|
||||
|
||||
// The reduced Weierstrass form y^2 = x^3 + ax + b reflects an elliptic curve E over any field K (e.g. all real
|
||||
// numbers or all complex numbers, etc). For computational simplicity, cryptographic (e.g. NIST) elliptic curves
|
||||
// restrict K to be a field of integers modulo a prime number 'p'. As such, we apply modulo p (the field prime)
|
||||
// to the equation to account for the restricted field. For a nice overview of the math behind EC curves and
|
||||
// their application in cryptography, see
|
||||
// https://web.northeastern.edu/dummit/docs/cryptography_5_elliptic_curves_in_cryptography.pdf
|
||||
|
||||
final BigInteger p = ((ECFieldFp) curve.getField()).getP();
|
||||
|
||||
// Verify the point coordinates are in field range:
|
||||
if (x.compareTo(BigInteger.ZERO) < 0 || x.compareTo(p) >= 0 ||
|
||||
y.compareTo(BigInteger.ZERO) < 0 || y.compareTo(p) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally, assert Weierstrass form equality:
|
||||
final BigInteger lhs = y.modPow(TWO, p); //mod p to account for field prime
|
||||
final BigInteger rhs = x.modPow(THREE, p).add(a.multiply(x)).add(b).mod(p); //mod p to account for field prime
|
||||
return lhs.equals(rhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPairBuilder keyPair() {
|
||||
return new DefaultKeyPairBuilder(KEY_PAIR_GENERATOR_JCA_NAME, toParameterSpec());
|
||||
/**
|
||||
* Multiply this curve's generator (aka 'base point') by scalar {@code s} on the curve.
|
||||
*
|
||||
* @param s the scalar value to multiply
|
||||
*/
|
||||
private ECPoint multiply(BigInteger s) {
|
||||
return multiply(this.spec.getGenerator(), s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply a point {@code p} by scalar {@code s} on the curve.
|
||||
*
|
||||
* @param p the Elliptic Curve point to multiply
|
||||
* @param s the scalar value to multiply
|
||||
*/
|
||||
private ECPoint multiply(ECPoint p, BigInteger s) {
|
||||
|
||||
if (ECPoint.POINT_INFINITY.equals(p)) {
|
||||
return p;
|
||||
}
|
||||
|
||||
final BigInteger n = this.spec.getOrder();
|
||||
final BigInteger k = s.mod(n);
|
||||
|
||||
ECPoint r0 = ECPoint.POINT_INFINITY;
|
||||
ECPoint r1 = p;
|
||||
|
||||
// Montgomery Ladder implementation to mitigate side-channel attacks (i.e. an 'add' operation and a 'double'
|
||||
// operation is calculated for every loop iteration, regardless if the 'add'' is needed or not)
|
||||
// See: https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder
|
||||
// while (k.compareTo(BigInteger.ZERO) > 0) {
|
||||
// ECPoint temp = add(r0, r1, curve);
|
||||
// r0 = k.testBit(0) ? temp : r0;
|
||||
// r1 = doublePoint(r1, curve);
|
||||
// k = k.shiftRight(1);
|
||||
// }
|
||||
// above implementation (k.compareTo/k.shiftRight) works correctly , but this is a little faster:
|
||||
for (int i = k.bitLength() - 1; i >= 0; i--) {
|
||||
if (k.testBit(i)) { // bit == 1
|
||||
r0 = add(r0, r1);
|
||||
r1 = doublePoint(r1);
|
||||
} else { // bit == 0
|
||||
r1 = add(r0, r1);
|
||||
r0 = doublePoint(r0);
|
||||
}
|
||||
}
|
||||
|
||||
return r0;
|
||||
}
|
||||
|
||||
private ECPoint add(ECPoint P, ECPoint Q) {
|
||||
|
||||
if (ECPoint.POINT_INFINITY.equals(P)) {
|
||||
return Q;
|
||||
} else if (ECPoint.POINT_INFINITY.equals(Q)) {
|
||||
return P;
|
||||
} else if (P.equals(Q)) {
|
||||
return doublePoint(P);
|
||||
}
|
||||
|
||||
final EllipticCurve curve = this.spec.getCurve();
|
||||
|
||||
final BigInteger Px = P.getAffineX();
|
||||
final BigInteger Py = P.getAffineY();
|
||||
final BigInteger Qx = Q.getAffineX();
|
||||
final BigInteger Qy = Q.getAffineY();
|
||||
final BigInteger prime = ((ECFieldFp) curve.getField()).getP();
|
||||
final BigInteger slope = Qy.subtract(Py).multiply(Qx.subtract(Px).modInverse(prime)).mod(prime);
|
||||
final BigInteger Rx = slope.pow(2).subtract(Px).subtract(Qx).mod(prime);
|
||||
final BigInteger Ry = slope.multiply(Px.subtract(Rx)).subtract(Py).mod(prime);
|
||||
|
||||
return new ECPoint(Rx, Ry);
|
||||
}
|
||||
|
||||
private ECPoint doublePoint(ECPoint P) {
|
||||
|
||||
if (ECPoint.POINT_INFINITY.equals(P)) {
|
||||
return P;
|
||||
}
|
||||
|
||||
final EllipticCurve curve = this.spec.getCurve();
|
||||
final BigInteger Px = P.getAffineX();
|
||||
final BigInteger Py = P.getAffineY();
|
||||
final BigInteger p = ((ECFieldFp) curve.getField()).getP();
|
||||
final BigInteger a = curve.getA();
|
||||
final BigInteger s = THREE.multiply(Px.pow(2)).add(a).mod(p).multiply(TWO.multiply(Py).modInverse(p)).mod(p);
|
||||
final BigInteger x = s.pow(2).subtract(TWO.multiply(Px)).mod(p);
|
||||
final BigInteger y = s.multiply(Px.subtract(x)).subtract(Py).mod(p);
|
||||
|
||||
return new ECPoint(x, y);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,14 +22,16 @@ import io.jsonwebtoken.lang.Assert;
|
|||
import io.jsonwebtoken.lang.Strings;
|
||||
import io.jsonwebtoken.security.EcPrivateJwk;
|
||||
import io.jsonwebtoken.security.EcPublicJwk;
|
||||
import io.jsonwebtoken.security.UnsupportedKeyException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPrivateKeySpec;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
class EcPrivateJwkFactory extends AbstractEcJwkFactory<ECPrivateKey, EcPrivateJwk> {
|
||||
|
||||
|
@ -46,6 +48,27 @@ class EcPrivateJwkFactory extends AbstractEcJwkFactory<ECPrivateKey, EcPrivateJw
|
|||
return super.supportsKeyValues(ctx) && ctx.containsKey(DefaultEcPrivateJwk.D.getId());
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
protected ECPublicKey derivePublic(KeyFactory keyFactory, ECPublicKeySpec spec) throws InvalidKeySpecException {
|
||||
return (ECPublicKey) keyFactory.generatePublic(spec);
|
||||
}
|
||||
|
||||
protected ECPublicKey derivePublic(final JwkContext<ECPrivateKey> ctx) {
|
||||
final ECPrivateKey key = ctx.getKey();
|
||||
return generateKey(ctx, ECPublicKey.class, new CheckedFunction<KeyFactory, ECPublicKey>() {
|
||||
@Override
|
||||
public ECPublicKey apply(KeyFactory kf) {
|
||||
try {
|
||||
ECPublicKeySpec spec = ECCurve.publicKeySpec(key);
|
||||
return derivePublic(kf, spec);
|
||||
} catch (Exception e) {
|
||||
String msg = "Unable to derive ECPublicKey from ECPrivateKey: " + e.getMessage();
|
||||
throw new UnsupportedKeyException(msg, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EcPrivateJwk createJwkFromKey(JwkContext<ECPrivateKey> ctx) {
|
||||
|
||||
|
@ -93,9 +116,8 @@ class EcPrivateJwkFactory extends AbstractEcJwkFactory<ECPrivateKey, EcPrivateJw
|
|||
JwkContext<ECPublicKey> pubCtx = new DefaultJwkContext<>(DefaultEcPublicJwk.FIELDS, ctx);
|
||||
EcPublicJwk pubJwk = EcPublicJwkFactory.INSTANCE.createJwk(pubCtx);
|
||||
|
||||
ECParameterSpec spec = getCurveByJwaId(curveId);
|
||||
final ECPrivateKeySpec privateSpec = new ECPrivateKeySpec(d, spec);
|
||||
|
||||
ECCurve curve = getCurveByJwaId(curveId);
|
||||
final ECPrivateKeySpec privateSpec = new ECPrivateKeySpec(d, curve.toParameterSpec());
|
||||
ECPrivateKey key = generateKey(ctx, new CheckedFunction<KeyFactory, ECPrivateKey>() {
|
||||
@Override
|
||||
public ECPrivateKey apply(KeyFactory kf) throws Exception {
|
||||
|
|
|
@ -21,6 +21,7 @@ import io.jsonwebtoken.impl.lang.RequiredFieldReader;
|
|||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.security.EcPublicJwk;
|
||||
import io.jsonwebtoken.security.InvalidKeyException;
|
||||
import io.jsonwebtoken.security.UnsupportedKeyException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyFactory;
|
||||
|
@ -33,6 +34,8 @@ import java.util.Map;
|
|||
|
||||
class EcPublicJwkFactory extends AbstractEcJwkFactory<ECPublicKey, EcPublicJwk> {
|
||||
|
||||
private static final String UNSUPPORTED_CURVE_MSG = "The specified ECKey curve does not match a JWA standard curve id.";
|
||||
|
||||
static final EcPublicJwkFactory INSTANCE = new EcPublicJwkFactory();
|
||||
|
||||
EcPublicJwkFactory() {
|
||||
|
@ -54,6 +57,14 @@ class EcPublicJwkFactory extends AbstractEcJwkFactory<ECPublicKey, EcPublicJwk>
|
|||
return String.format(fmt, curveId, jwk);
|
||||
}
|
||||
|
||||
protected static String getJwaIdByCurve(EllipticCurve curve) {
|
||||
ECCurve c = ECCurve.findByJcaCurve(curve);
|
||||
if (c == null) {
|
||||
throw new UnsupportedKeyException(UNSUPPORTED_CURVE_MSG);
|
||||
}
|
||||
return c.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EcPublicJwk createJwkFromKey(JwkContext<ECPublicKey> ctx) {
|
||||
|
||||
|
@ -64,7 +75,7 @@ class EcPublicJwkFactory extends AbstractEcJwkFactory<ECPublicKey, EcPublicJwk>
|
|||
ECPoint point = key.getW();
|
||||
|
||||
String curveId = getJwaIdByCurve(curve);
|
||||
if (!contains(curve, point)) {
|
||||
if (!ECCurve.contains(curve, point)) {
|
||||
String msg = keyContainsErrorMessage(curveId);
|
||||
throw new InvalidKeyException(msg);
|
||||
}
|
||||
|
@ -89,16 +100,15 @@ class EcPublicJwkFactory extends AbstractEcJwkFactory<ECPublicKey, EcPublicJwk>
|
|||
BigInteger x = reader.get(DefaultEcPublicJwk.X);
|
||||
BigInteger y = reader.get(DefaultEcPublicJwk.Y);
|
||||
|
||||
ECParameterSpec spec = getCurveByJwaId(curveId);
|
||||
ECCurve curve = getCurveByJwaId(curveId);
|
||||
ECPoint point = new ECPoint(x, y);
|
||||
|
||||
if (!contains(spec.getCurve(), point)) {
|
||||
if (!curve.contains(point)) {
|
||||
String msg = jwkContainsErrorMessage(curveId, ctx);
|
||||
throw new InvalidKeyException(msg);
|
||||
}
|
||||
|
||||
final ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, spec);
|
||||
|
||||
final ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, curve.toParameterSpec());
|
||||
ECPublicKey key = generateKey(ctx, new CheckedFunction<KeyFactory, ECPublicKey>() {
|
||||
@Override
|
||||
public ECPublicKey apply(KeyFactory kf) throws Exception {
|
||||
|
|
|
@ -19,6 +19,7 @@ import io.jsonwebtoken.JwtException;
|
|||
import io.jsonwebtoken.impl.lang.Bytes;
|
||||
import io.jsonwebtoken.impl.lang.CheckedFunction;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
import io.jsonwebtoken.security.InvalidKeyException;
|
||||
import io.jsonwebtoken.security.KeyPairBuilder;
|
||||
|
@ -44,8 +45,7 @@ final class EcSignatureAlgorithm extends AbstractSignatureAlgorithm {
|
|||
|
||||
private static final String REQD_ORDER_BIT_LENGTH_MSG = "orderBitLength must equal 256, 384, or 521.";
|
||||
|
||||
private static final String DER_ENCODING_SYS_PROPERTY_NAME =
|
||||
"io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator.derEncodingSupported";
|
||||
private static final String DER_ENCODING_SYS_PROPERTY_NAME = "io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator.derEncodingSupported";
|
||||
|
||||
private static final String ES256_OID = "1.2.840.10045.4.3.2";
|
||||
private static final String ES384_OID = "1.2.840.10045.4.3.3";
|
||||
|
@ -104,13 +104,12 @@ final class EcSignatureAlgorithm extends AbstractSignatureAlgorithm {
|
|||
static final EcSignatureAlgorithm ES384 = new EcSignatureAlgorithm(384, ES384_OID);
|
||||
static final EcSignatureAlgorithm ES512 = new EcSignatureAlgorithm(521, ES512_OID);
|
||||
|
||||
private static final Map<String, SignatureAlgorithm> ALGS_BY_OID;
|
||||
private static final Map<String, SignatureAlgorithm> BY_OID = new LinkedHashMap<>(3);
|
||||
|
||||
static {
|
||||
ALGS_BY_OID = new LinkedHashMap<>(3);
|
||||
ALGS_BY_OID.put(ES256_OID, ES256);
|
||||
ALGS_BY_OID.put(ES384_OID, ES384);
|
||||
ALGS_BY_OID.put(ES512_OID, ES512);
|
||||
for (EcSignatureAlgorithm alg : Collections.of(ES256, ES384, ES512)) {
|
||||
BY_OID.put(alg.OID, alg);
|
||||
}
|
||||
}
|
||||
|
||||
static SignatureAlgorithm findByKey(Key key) {
|
||||
|
@ -121,7 +120,7 @@ final class EcSignatureAlgorithm extends AbstractSignatureAlgorithm {
|
|||
}
|
||||
algName = algName.toUpperCase(Locale.ENGLISH);
|
||||
|
||||
SignatureAlgorithm alg = ALGS_BY_OID.get(algName);
|
||||
SignatureAlgorithm alg = BY_OID.get(algName);
|
||||
if (alg != null) {
|
||||
return alg;
|
||||
}
|
||||
|
@ -154,8 +153,7 @@ final class EcSignatureAlgorithm extends AbstractSignatureAlgorithm {
|
|||
|
||||
@Override
|
||||
public KeyPairBuilder keyPair() {
|
||||
return new DefaultKeyPairBuilder(ECCurve.KEY_PAIR_GENERATOR_JCA_NAME, this.KEY_PAIR_GEN_PARAMS)
|
||||
.random(Randoms.secureRandom());
|
||||
return new DefaultKeyPairBuilder(ECCurve.KEY_PAIR_GENERATOR_JCA_NAME, this.KEY_PAIR_GEN_PARAMS).random(Randoms.secureRandom());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -172,10 +170,7 @@ final class EcSignatureAlgorithm extends AbstractSignatureAlgorithm {
|
|||
int concatByteLength = sigFieldByteLength * 2;
|
||||
|
||||
if (concatByteLength != this.signatureByteLength) {
|
||||
String msg = "The provided Elliptic Curve " + keyType(signing) + " key's size (aka Order bit length) is " +
|
||||
Bytes.bitsMsg(orderBitLength) + ", but the '" + name + "' algorithm requires EC Keys with " +
|
||||
Bytes.bitsMsg(this.orderBitLength) + " per " +
|
||||
"[RFC 7518, Section 3.4](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).";
|
||||
String msg = "The provided Elliptic Curve " + keyType(signing) + " key's size (aka Order bit length) is " + Bytes.bitsMsg(orderBitLength) + ", but the '" + name + "' algorithm requires EC Keys with " + Bytes.bitsMsg(this.orderBitLength) + " per " + "[RFC 7518, Section 3.4](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).";
|
||||
throw new InvalidKeyException(msg);
|
||||
}
|
||||
}
|
||||
|
@ -229,9 +224,7 @@ final class EcSignatureAlgorithm extends AbstractSignatureAlgorithm {
|
|||
if (concatSignature[0] == 0x30 && "true".equalsIgnoreCase(System.getProperty(DER_ENCODING_SYS_PROPERTY_NAME))) {
|
||||
derSignature = concatSignature;
|
||||
} else {
|
||||
String msg = "Provided signature is " + Bytes.bytesMsg(concatSignature.length) + " but " +
|
||||
getId() + " signatures must be exactly " + Bytes.bytesMsg(signatureByteLength) + " per " +
|
||||
"[RFC 7518, Section 3.4 (validation)](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).";
|
||||
String msg = "Provided signature is " + Bytes.bytesMsg(concatSignature.length) + " but " + getId() + " signatures must be exactly " + Bytes.bytesMsg(signatureByteLength) + " per " + "[RFC 7518, Section 3.4 (validation)](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).";
|
||||
throw new SignatureException(msg);
|
||||
}
|
||||
} else {
|
||||
|
@ -300,10 +293,7 @@ final class EcSignatureAlgorithm extends AbstractSignatureAlgorithm {
|
|||
int rawLen = Math.max(i, j);
|
||||
rawLen = Math.max(rawLen, outputLength / 2);
|
||||
|
||||
if ((derSignature[offset - 1] & 0xff) != derSignature.length - offset
|
||||
|| (derSignature[offset - 1] & 0xff) != 2 + rLength + 2 + sLength
|
||||
|| derSignature[offset] != 2
|
||||
|| derSignature[offset + 2 + rLength] != 2) {
|
||||
if ((derSignature[offset - 1] & 0xff) != derSignature.length - offset || (derSignature[offset - 1] & 0xff) != 2 + rLength + 2 + sLength || derSignature[offset] != 2 || derSignature[offset + 2 + rLength] != 2) {
|
||||
throw new JwtException("Invalid ECDSA signature format");
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import io.jsonwebtoken.impl.lang.RequiredFieldReader;
|
|||
import io.jsonwebtoken.lang.Arrays;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.security.AeadAlgorithm;
|
||||
import io.jsonwebtoken.security.Curve;
|
||||
import io.jsonwebtoken.security.DecryptionKeyRequest;
|
||||
import io.jsonwebtoken.security.DynamicJwkBuilder;
|
||||
import io.jsonwebtoken.security.EcPublicJwk;
|
||||
|
@ -50,9 +51,6 @@ import java.security.Provider;
|
|||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.ECKey;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
|
||||
/**
|
||||
* @since JJWT_RELEASE_VERSION
|
||||
|
@ -70,9 +68,9 @@ class EcdhKeyAlgorithm extends CryptoAlgorithm implements KeyAlgorithm<PublicKey
|
|||
private static final String CONCAT_KDF_HASH_ALG_NAME = "SHA-256";
|
||||
private static final ConcatKDF CONCAT_KDF = new ConcatKDF(CONCAT_KDF_HASH_ALG_NAME);
|
||||
public static final String KEK_TYPE_MESSAGE = "Key Encryption Key must be a " + ECKey.class.getName() +
|
||||
" or valid Edwards Curve PublicKey instance.";
|
||||
" or Edwards Curve PublicKey on a supported curve.";
|
||||
public static final String KDK_TYPE_MESSAGE = "Key Decryption Key must be a " + ECKey.class.getName() +
|
||||
" or valid Edwards Curve PrivateKey instance.";
|
||||
" or Edwards Curve PrivateKey on a supported curve.";
|
||||
|
||||
private final KeyAlgorithm<SecretKey, SecretKey> WRAP_ALG;
|
||||
|
||||
|
@ -91,16 +89,8 @@ class EcdhKeyAlgorithm extends CryptoAlgorithm implements KeyAlgorithm<PublicKey
|
|||
this.WRAP_ALG = Assert.notNull(wrapAlg, "Wrap algorithm cannot be null.");
|
||||
}
|
||||
|
||||
//visible for testing, for non-Edwards elliptic curves
|
||||
protected KeyPair generateKeyPair(final Request<?> request, final ECParameterSpec spec) {
|
||||
Assert.notNull(spec, "request key params cannot be null.");
|
||||
JcaTemplate template = new JcaTemplate(ECCurve.KEY_PAIR_GENERATOR_JCA_NAME, request.getProvider(),
|
||||
ensureSecureRandom(request));
|
||||
return template.generateKeyPair(spec);
|
||||
}
|
||||
|
||||
//visible for testing, for Edwards elliptic curves
|
||||
protected KeyPair generateKeyPair(SecureRandom random, EdwardsCurve curve, Provider provider) {
|
||||
protected KeyPair generateKeyPair(Curve curve, Provider provider, SecureRandom random) {
|
||||
return curve.keyPair().provider(provider).random(random).build();
|
||||
}
|
||||
|
||||
|
@ -170,8 +160,9 @@ class EcdhKeyAlgorithm extends CryptoAlgorithm implements KeyAlgorithm<PublicKey
|
|||
EdwardsCurve curve;
|
||||
try {
|
||||
curve = EdwardsCurve.forKey(key);
|
||||
} catch (Exception e) {
|
||||
throw new UnsupportedKeyException(exMsg + " Cause: " + e.getMessage(), e);
|
||||
} catch (Throwable t) {
|
||||
String msg = exMsg + " Cause: " + t.getMessage();
|
||||
throw new UnsupportedKeyException(msg, t);
|
||||
}
|
||||
Assert.stateNotNull(curve, "EdwardsCurve instance cannot be null.");
|
||||
if (curve.isSignatureCurve()) {
|
||||
|
@ -182,34 +173,38 @@ class EcdhKeyAlgorithm extends CryptoAlgorithm implements KeyAlgorithm<PublicKey
|
|||
return curve;
|
||||
}
|
||||
|
||||
private static ECCurve assertEcCurve(Key key, String exMsg) {
|
||||
ECCurve curve;
|
||||
try {
|
||||
curve = ECCurve.forKey(key);
|
||||
} catch (Throwable t) {
|
||||
String msg = exMsg + " Cause: " + t.getMessage();
|
||||
throw new UnsupportedKeyException(msg, t);
|
||||
}
|
||||
return Assert.stateNotNull(curve, "ECCurve instance cannot be null.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyResult getEncryptionKey(KeyRequest<PublicKey> request) throws SecurityException {
|
||||
Assert.notNull(request, "Request cannot be null.");
|
||||
JweHeader header = Assert.notNull(request.getHeader(), "Request JweHeader cannot be null.");
|
||||
PublicKey publicKey = Assert.notNull(request.getPayload(), "Encryption PublicKey cannot be null.");
|
||||
|
||||
KeyPair pair; // generated (ephemeral) key pair
|
||||
final SecureRandom random = ensureSecureRandom(request);
|
||||
DynamicJwkBuilder<?, ?> jwkBuilder = Jwks.builder().random(random).provider(request.getProvider());
|
||||
|
||||
Curve curve;
|
||||
if (publicKey instanceof ECKey) {
|
||||
ECKey ecPublicKey = (ECKey) publicKey;
|
||||
ECParameterSpec spec = Assert.notNull(ecPublicKey.getParams(),
|
||||
"Encryption PublicKey params cannot be null.");
|
||||
curve = assertEcCurve(publicKey, KEK_TYPE_MESSAGE);
|
||||
// note: we don't need to validate if specified key's point is on a supported curve here
|
||||
// because that will automatically be asserted when using Jwks.builder().... below
|
||||
pair = generateKeyPair(request, spec);
|
||||
// assert pair key types:
|
||||
KeyPairs.getKey(pair, ECPublicKey.class);
|
||||
KeyPairs.getKey(pair, ECPrivateKey.class);
|
||||
} else { // it must be an edwards curve key
|
||||
EdwardsCurve curve = assertAgreement(publicKey, KEK_TYPE_MESSAGE);
|
||||
Provider provider = request.getProvider();
|
||||
request = new DefaultKeyRequest<>(request.getPayload(), provider, random,
|
||||
request.getHeader(), request.getEncryptionAlgorithm());
|
||||
pair = generateKeyPair(random, curve, provider);
|
||||
jwkBuilder.provider(provider);
|
||||
curve = assertAgreement(publicKey, KEK_TYPE_MESSAGE);
|
||||
}
|
||||
Assert.stateNotNull(curve, "Internal implementation state: Curve cannot be null.");
|
||||
|
||||
// Generate our ephemeral key pair:
|
||||
final SecureRandom random = ensureSecureRandom(request);
|
||||
final Provider provider = request.getProvider();
|
||||
DynamicJwkBuilder<?, ?> jwkBuilder = Jwks.builder().random(random).provider(provider);
|
||||
KeyPair pair = generateKeyPair(curve, provider, random);
|
||||
|
||||
Assert.stateNotNull(pair, "Internal implementation state: KeyPair cannot be null.");
|
||||
|
||||
|
@ -227,6 +222,15 @@ class EcdhKeyAlgorithm extends CryptoAlgorithm implements KeyAlgorithm<PublicKey
|
|||
return result;
|
||||
}
|
||||
|
||||
private <T> T assertEpk(Class<T> clazz, Object epk) {
|
||||
if (!clazz.isInstance(epk)) {
|
||||
String msg = "JWE Header " + DefaultJweHeader.EPK + " value is not a supported Elliptic Curve " +
|
||||
"Public JWK. Value: " + epk;
|
||||
throw new UnsupportedKeyException(msg);
|
||||
}
|
||||
return clazz.cast(epk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey getDecryptionKey(DecryptionKeyRequest<PrivateKey> request) throws SecurityException {
|
||||
|
||||
|
@ -237,27 +241,17 @@ class EcdhKeyAlgorithm extends CryptoAlgorithm implements KeyAlgorithm<PublicKey
|
|||
PublicJwk<?> epk = reader.get(DefaultJweHeader.EPK);
|
||||
|
||||
if (privateKey instanceof ECKey) {
|
||||
ECKey ecPrivateKey = (ECKey) privateKey;
|
||||
if (!(epk instanceof EcPublicJwk)) {
|
||||
String msg = "JWE Header " + DefaultJweHeader.EPK + " value is not a supported Elliptic Curve " +
|
||||
"Public JWK. Value: " + epk;
|
||||
throw new UnsupportedKeyException(msg);
|
||||
}
|
||||
EcPublicJwk ecEpk = (EcPublicJwk) epk;
|
||||
ECCurve curve = assertEcCurve(privateKey, KDK_TYPE_MESSAGE);
|
||||
EcPublicJwk ecEpk = assertEpk(EcPublicJwk.class, epk);
|
||||
// While the EPK might be on a JWA-supported NIST curve, it must be on the private key's exact curve:
|
||||
if (!EcPublicJwkFactory.contains(ecPrivateKey.getParams().getCurve(), ecEpk.toKey().getW())) {
|
||||
if (!curve.contains(ecEpk.toKey())) {
|
||||
String msg = "JWE Header " + DefaultJweHeader.EPK + " value does not represent " +
|
||||
"a point on the expected curve.";
|
||||
throw new InvalidKeyException(msg);
|
||||
}
|
||||
} else { // it must be an Edwards Curve key
|
||||
EdwardsCurve privateKeyCurve = assertAgreement(privateKey, KDK_TYPE_MESSAGE);
|
||||
if (!(epk instanceof OctetPublicJwk)) {
|
||||
String msg = "JWE Header " + DefaultJweHeader.EPK + " value is not a supported Elliptic Curve " +
|
||||
"Public JWK. Value: " + epk;
|
||||
throw new UnsupportedKeyException(msg);
|
||||
}
|
||||
OctetPublicJwk<?> oEpk = (OctetPublicJwk<?>) epk;
|
||||
OctetPublicJwk<?> oEpk = assertEpk(OctetPublicJwk.class, epk);
|
||||
EdwardsCurve epkCurve = EdwardsCurve.forKey(oEpk.toKey());
|
||||
if (!privateKeyCurve.equals(epkCurve)) {
|
||||
String msg = "JWE Header " + DefaultJweHeader.EPK + " value does not represent a point " +
|
||||
|
|
|
@ -17,11 +17,28 @@ package io.jsonwebtoken.impl.security;
|
|||
|
||||
import io.jsonwebtoken.impl.lang.DelegatingRegistry;
|
||||
import io.jsonwebtoken.impl.lang.IdRegistry;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.security.Curve;
|
||||
|
||||
@SuppressWarnings("unused") // used via reflection in io.jsonwebtoken.Jwks.CRV
|
||||
public final class StandardCurves extends DelegatingRegistry<String, Curve> {
|
||||
|
||||
public StandardCurves() {
|
||||
super(new IdRegistry<>("Elliptic Curve", Curves.VALUES, false));
|
||||
super(new IdRegistry<>("Elliptic Curve", Collections.<Curve>of(
|
||||
ECCurve.P256,
|
||||
ECCurve.P384,
|
||||
ECCurve.P521,
|
||||
EdwardsCurve.X25519,
|
||||
EdwardsCurve.X448,
|
||||
EdwardsCurve.Ed25519,
|
||||
EdwardsCurve.Ed448
|
||||
), false));
|
||||
}
|
||||
|
||||
// public static Curve findByKey(Key key) {
|
||||
// Curve curve = ECCurve.findByKey(key);
|
||||
// if (curve == null) {
|
||||
// curve = EdwardsCurve.findByKey(key);
|
||||
// }
|
||||
// return curve;
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -15,20 +15,27 @@
|
|||
*/
|
||||
package io.jsonwebtoken
|
||||
|
||||
import io.jsonwebtoken.impl.security.Curves
|
||||
import io.jsonwebtoken.impl.security.ECCurve
|
||||
import io.jsonwebtoken.impl.security.EdwardsCurve
|
||||
import io.jsonwebtoken.impl.security.StandardCurves
|
||||
import io.jsonwebtoken.security.Jwks
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.assertSame
|
||||
import static org.junit.Assert.assertTrue
|
||||
|
||||
class JwksCRVTest {
|
||||
|
||||
@Test
|
||||
void testRegistry() {
|
||||
assertTrue Jwks.CRV.get() instanceof StandardCurves
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInstances() {
|
||||
assertSame Curves.P_256, Jwks.CRV.P256
|
||||
assertSame Curves.P_384, Jwks.CRV.P384
|
||||
assertSame Curves.P_521, Jwks.CRV.P521
|
||||
assertSame ECCurve.P256, Jwks.CRV.P256
|
||||
assertSame ECCurve.P384, Jwks.CRV.P384
|
||||
assertSame ECCurve.P521, Jwks.CRV.P521
|
||||
assertSame EdwardsCurve.X25519, Jwks.CRV.X25519
|
||||
assertSame EdwardsCurve.X448, Jwks.CRV.X448
|
||||
assertSame EdwardsCurve.Ed25519, Jwks.CRV.Ed25519
|
||||
|
|
|
@ -15,16 +15,10 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.security.Jwk
|
||||
|
||||
import io.jsonwebtoken.security.UnsupportedKeyException
|
||||
import org.junit.Test
|
||||
|
||||
import java.security.KeyFactory
|
||||
import java.security.interfaces.ECPrivateKey
|
||||
import java.security.interfaces.ECPublicKey
|
||||
import java.security.spec.*
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
|
@ -37,98 +31,8 @@ class AbstractEcJwkFactoryTest {
|
|||
AbstractEcJwkFactory.getCurveByJwaId(id)
|
||||
fail()
|
||||
} catch (UnsupportedKeyException e) {
|
||||
String msg = "Unrecognized JWA curve id '$id'"
|
||||
String msg = "Unrecognized JWA EC curve id '$id'"
|
||||
assertEquals msg, e.getMessage()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnsupportedCurve() {
|
||||
def curve = new EllipticCurve(new TestECField(fieldSize: 1), BigInteger.ONE, BigInteger.TEN)
|
||||
try {
|
||||
AbstractEcJwkFactory.getJwaIdByCurve(curve)
|
||||
fail()
|
||||
} catch (UnsupportedKeyException e) {
|
||||
assertEquals AbstractEcJwkFactory.UNSUPPORTED_CURVE_MSG, e.getMessage()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiplyInfinity() {
|
||||
ECParameterSpec spec = AbstractEcJwkFactory.getCurveByJwaId('P-256')
|
||||
def result = AbstractEcJwkFactory.multiply(ECPoint.POINT_INFINITY, BigInteger.valueOf(1), spec)
|
||||
assertEquals ECPoint.POINT_INFINITY, result
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoubleInfinity() {
|
||||
ECParameterSpec spec = AbstractEcJwkFactory.getCurveByJwaId('P-256')
|
||||
def curve = spec.getCurve()
|
||||
def result = AbstractEcJwkFactory.doublePoint(ECPoint.POINT_INFINITY, curve)
|
||||
assertEquals ECPoint.POINT_INFINITY, result
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddInfinity() {
|
||||
ECParameterSpec spec = AbstractEcJwkFactory.getCurveByJwaId('P-256')
|
||||
def curve = spec.getCurve()
|
||||
ECPoint point = new ECPoint(BigInteger.valueOf(1), BigInteger.valueOf(2)) // any point is fine for this test
|
||||
def result = AbstractEcJwkFactory.add(ECPoint.POINT_INFINITY, point, curve)
|
||||
//adding infinity to a point should return the point:
|
||||
assertEquals point, result
|
||||
//adding a point to infinity should return the point:
|
||||
result = AbstractEcJwkFactory.add(point, ECPoint.POINT_INFINITY, curve)
|
||||
assertEquals point, result
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddSamePointDoublesIt() {
|
||||
def pair = Jwts.SIG.ES256.keyPair().build()
|
||||
def pub = pair.getPublic() as ECPublicKey
|
||||
|
||||
def spec = pub.getParams()
|
||||
def curve = spec.getCurve()
|
||||
def point = pub.getW()
|
||||
|
||||
def doubled = AbstractEcJwkFactory.doublePoint(point, curve)
|
||||
def added = AbstractEcJwkFactory.add(point, point, curve)
|
||||
assertEquals doubled, added
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDerivePublicFails() {
|
||||
|
||||
def pair = Jwts.SIG.ES256.keyPair().build()
|
||||
def priv = pair.getPrivate() as ECPrivateKey
|
||||
|
||||
final def context = new DefaultJwkContext(DefaultEcPrivateJwk.FIELDS)
|
||||
context.setKey(priv)
|
||||
|
||||
def ex = new InvalidKeySpecException("invalid")
|
||||
|
||||
def factory = new AbstractEcJwkFactory(ECPrivateKey.class, DefaultEcPrivateJwk.FIELDS) {
|
||||
@Override
|
||||
protected Jwk createJwkFromKey(JwkContext ctx) {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Jwk createJwkFromValues(JwkContext ctx) {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ECPublicKey derivePublic(KeyFactory keyFactory, ECPublicKeySpec spec) throws InvalidKeySpecException {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
factory.derivePublic(context)
|
||||
fail()
|
||||
} catch (UnsupportedKeyException expected) {
|
||||
String msg = 'Unable to derive ECPublicKey from ECPrivateKey: invalid'
|
||||
assertEquals msg, expected.getMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,19 +15,80 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.security
|
||||
|
||||
|
||||
import io.jsonwebtoken.security.UnsupportedKeyException
|
||||
import org.junit.Test
|
||||
|
||||
import java.security.PublicKey
|
||||
import java.security.interfaces.ECPublicKey
|
||||
import java.security.spec.ECParameterSpec
|
||||
import java.security.spec.ECPoint
|
||||
import java.security.spec.EllipticCurve
|
||||
|
||||
import static org.junit.Assert.assertFalse
|
||||
import static org.junit.Assert.assertTrue
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class ECCurveTest {
|
||||
|
||||
static void assertContains(ECCurve curve, PublicKey pub) {
|
||||
assertTrue(curve.contains(pub))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsKeyTrue() {
|
||||
assertContains(ECCurve.P256, TestKeys.ES256.pair.public)
|
||||
assertContains(ECCurve.P384, TestKeys.ES384.pair.public)
|
||||
assertContains(ECCurve.P521, TestKeys.ES512.pair.public)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsKeyNull() {
|
||||
ECCurve.VALUES.each {
|
||||
assertFalse(it.contains(null))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsNonECPublicKey() {
|
||||
ECCurve.VALUES.each {
|
||||
assertFalse it.contains(TestKeys.HS256)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsKeyNullParams() {
|
||||
ECCurve.VALUES.each {
|
||||
assertFalse it.contains(new TestECPublicKey())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsKeyNullJcaCurve() {
|
||||
def src = TestKeys.ES256.pair.public.getParams() as ECParameterSpec
|
||||
def spec = new ECParameterSpec(src.curve, src.generator, src.order, src.cofactor) {
|
||||
@Override
|
||||
EllipticCurve getCurve() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
def key = new TestECKey(params: spec)
|
||||
ECCurve.VALUES.each {
|
||||
assertFalse it.contains(key)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsKeyBadWPoint() {
|
||||
ECCurve.VALUES.each {
|
||||
def src = it.keyPair().build().public
|
||||
def spec = src.getParams() as ECParameterSpec
|
||||
def key = new TestECPublicKey(params: spec, w: new ECPoint(BigInteger.ONE, BigInteger.ONE))
|
||||
assertFalse it.contains(key)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsTrue() {
|
||||
ECCurve curve = (ECCurve) Curves.P_256
|
||||
ECCurve curve = ECCurve.P256
|
||||
def pair = curve.keyPair().build()
|
||||
ECPublicKey ecPub = (ECPublicKey) pair.getPublic()
|
||||
assertTrue(curve.contains(ecPub.getW()))
|
||||
|
@ -35,6 +96,109 @@ class ECCurveTest {
|
|||
|
||||
@Test
|
||||
void testContainsFalse() {
|
||||
assertFalse(((ECCurve) Curves.P_256).contains(new ECPoint(BigInteger.ONE, BigInteger.ONE)))
|
||||
assertFalse(ECCurve.P256.contains(new ECPoint(BigInteger.ONE, BigInteger.ONE)))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByJcaEllipticCurve() {
|
||||
ECCurve.VALUES.each {
|
||||
it.equals(ECCurve.findByJcaCurve(it.toParameterSpec().getCurve()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiplyInfinity() {
|
||||
ECCurve.VALUES.each {
|
||||
def result = it.multiply(ECPoint.POINT_INFINITY, BigInteger.valueOf(1))
|
||||
assertEquals ECPoint.POINT_INFINITY, result
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoubleInfinity() {
|
||||
ECCurve.VALUES.each {
|
||||
def result = it.doublePoint(ECPoint.POINT_INFINITY)
|
||||
assertEquals ECPoint.POINT_INFINITY, result
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddInfinity() {
|
||||
ECCurve.VALUES.each {
|
||||
def curve = it.spec.getCurve()
|
||||
ECPoint point = new ECPoint(BigInteger.valueOf(1), BigInteger.valueOf(2)) // any point is fine for this test
|
||||
def result = it.add(ECPoint.POINT_INFINITY, point)
|
||||
//adding infinity to a point should return the point:
|
||||
assertEquals point, result
|
||||
//adding a point to infinity should return the point:
|
||||
result = it.add(point, ECPoint.POINT_INFINITY)
|
||||
assertEquals point, result
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddSamePointDoublesIt() {
|
||||
ECCurve.VALUES.each {
|
||||
def pair = it.keyPair().build()
|
||||
def pub = pair.getPublic() as ECPublicKey
|
||||
def point = pub.getW()
|
||||
def doubled = it.doublePoint(point)
|
||||
def added = it.add(point, point)
|
||||
assertEquals doubled, added
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByKeyNull() {
|
||||
assertNull ECCurve.findByKey(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByKeyNotECKey() {
|
||||
assertNull ECCurve.findByKey(TestKeys.HS256)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByKeyNullParams() {
|
||||
assertNull ECCurve.findByKey(new TestECKey())
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByKeyNullJcaCurve() {
|
||||
def src = TestKeys.ES256.pair.public.getParams() as ECParameterSpec
|
||||
def spec = new ECParameterSpec(src.curve, src.generator, src.order, src.cofactor) {
|
||||
@Override
|
||||
EllipticCurve getCurve() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
assertNull ECCurve.findByKey(new TestECKey(params: spec))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByKeyWithNullWPoint() {
|
||||
def spec = TestKeys.ES256.pair.public.getParams() as ECParameterSpec
|
||||
assertNull ECCurve.findByKey(new TestECPublicKey(params: spec))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByKeyWithWPointNotOnCurve() {
|
||||
def spec = TestKeys.ES256.pair.public.getParams() as ECParameterSpec
|
||||
def key = new TestECPublicKey(params: spec, w: new ECPoint(BigInteger.ONE, BigInteger.ONE))
|
||||
assertNull ECCurve.findByKey(key)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testForKeyWithNonECPublicKey() {
|
||||
def key = TestKeys.HS256
|
||||
try {
|
||||
ECCurve.forKey(key)
|
||||
fail()
|
||||
} catch (UnsupportedKeyException expected) {
|
||||
String msg = "Unable to determine JWA-standard Elliptic Curve for specified key: " +
|
||||
"${KeysBridge.toString(key)}"
|
||||
assertEquals msg, expected.getMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,17 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.security.MalformedKeyException
|
||||
import io.jsonwebtoken.security.UnsupportedKeyException
|
||||
import org.junit.Test
|
||||
|
||||
import java.security.KeyFactory
|
||||
import java.security.interfaces.ECPrivateKey
|
||||
import java.security.interfaces.ECPublicKey
|
||||
import java.security.spec.ECPublicKeySpec
|
||||
import java.security.spec.InvalidKeySpecException
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
|
@ -36,4 +44,31 @@ class EcPrivateJwkFactoryTest {
|
|||
assertEquals msg, expected.getMessage()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDerivePublicFails() {
|
||||
|
||||
def pair = Jwts.SIG.ES256.keyPair().build()
|
||||
def priv = pair.getPrivate() as ECPrivateKey
|
||||
|
||||
final def context = new DefaultJwkContext(DefaultEcPrivateJwk.FIELDS)
|
||||
context.setKey(priv)
|
||||
|
||||
def ex = new InvalidKeySpecException("invalid")
|
||||
|
||||
def factory = new EcPrivateJwkFactory() {
|
||||
@Override
|
||||
protected ECPublicKey derivePublic(KeyFactory keyFactory, ECPublicKeySpec spec) throws InvalidKeySpecException {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
factory.derivePublic(context)
|
||||
fail()
|
||||
} catch (UnsupportedKeyException expected) {
|
||||
String msg = 'Unable to derive ECPublicKey from ECPrivateKey: invalid'
|
||||
assertEquals msg, expected.getMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,11 @@ package io.jsonwebtoken.impl.security
|
|||
import io.jsonwebtoken.security.InvalidKeyException
|
||||
import io.jsonwebtoken.security.Jwks
|
||||
import io.jsonwebtoken.security.MalformedKeyException
|
||||
import io.jsonwebtoken.security.UnsupportedKeyException
|
||||
import org.junit.Test
|
||||
|
||||
import java.security.spec.EllipticCurve
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
|
@ -72,4 +75,15 @@ class EcPublicJwkFactoryTest {
|
|||
assertEquals msg, expected.getMessage()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnsupportedCurve() {
|
||||
def curve = new EllipticCurve(new TestECField(fieldSize: 1), BigInteger.ONE, BigInteger.TEN)
|
||||
try {
|
||||
EcPublicJwkFactory.getJwaIdByCurve(curve)
|
||||
fail()
|
||||
} catch (UnsupportedKeyException e) {
|
||||
assertEquals EcPublicJwkFactory.UNSUPPORTED_CURVE_MSG, e.getMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ class EcSignatureAlgorithmTest {
|
|||
|
||||
@Test
|
||||
void testFindOidKeys() {
|
||||
for(def alg : EcSignatureAlgorithm.ALGS_BY_OID.values()) {
|
||||
for(def alg : EcSignatureAlgorithm.BY_OID.values()) {
|
||||
String name = "${alg.getId()}_OID"
|
||||
String oid = EcSignatureAlgorithm.metaClass.getAttribute(EcSignatureAlgorithm, name) as String
|
||||
assertEquals oid, alg.OID
|
||||
|
|
|
@ -124,9 +124,9 @@ class EcdhKeyAlgorithmTest {
|
|||
alg.getEncryptionKey(request)
|
||||
fail()
|
||||
} catch (UnsupportedKeyException expected) {
|
||||
String msg = 'Key Encryption Key must be a java.security.interfaces.ECKey or valid Edwards Curve ' +
|
||||
'PublicKey instance. Cause: sun.security.rsa.RSAPublicKeyImpl with algorithm \'RSA\' is not a ' +
|
||||
'recognized Edwards Curve key.'
|
||||
String msg = 'Key Encryption Key must be a java.security.interfaces.ECKey or Edwards Curve ' +
|
||||
'PublicKey on a supported curve. Cause: sun.security.rsa.RSAPublicKeyImpl with ' +
|
||||
'algorithm \'RSA\' is not a recognized Edwards Curve key.'
|
||||
assertEquals msg, expected.getMessage()
|
||||
}
|
||||
}
|
||||
|
@ -142,9 +142,9 @@ class EcdhKeyAlgorithmTest {
|
|||
alg.getDecryptionKey(request)
|
||||
fail()
|
||||
} catch (UnsupportedKeyException expected) {
|
||||
String msg = 'Key Decryption Key must be a java.security.interfaces.ECKey or valid Edwards Curve ' +
|
||||
'PrivateKey instance. Cause: sun.security.rsa.RSAPrivateCrtKeyImpl with algorithm \'RSA\' is ' +
|
||||
'not a recognized Edwards Curve key.'
|
||||
String msg = 'Key Decryption Key must be a java.security.interfaces.ECKey or Edwards Curve ' +
|
||||
'PrivateKey on a supported curve. Cause: sun.security.rsa.RSAPrivateCrtKeyImpl with ' +
|
||||
'algorithm \'RSA\' is not a recognized Edwards Curve key.'
|
||||
assertEquals msg, expected.getMessage()
|
||||
}
|
||||
}
|
||||
|
@ -221,4 +221,17 @@ class EcdhKeyAlgorithmTest {
|
|||
assertEquals msg, expected.getMessage()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAssertEcCurveFails() {
|
||||
def key = TestKeys.HS256
|
||||
try {
|
||||
EcdhKeyAlgorithm.assertEcCurve(key, 'foo.')
|
||||
fail()
|
||||
} catch (UnsupportedKeyException expected) {
|
||||
String msg = "foo. Cause: Unable to determine JWA-standard Elliptic Curve for specified " +
|
||||
"key: ${KeysBridge.toString(key)}"
|
||||
assertEquals msg, expected.getMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@ import org.junit.Test
|
|||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.KeyPair
|
||||
import java.security.spec.ECParameterSpec
|
||||
import java.security.Provider
|
||||
import java.security.SecureRandom
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
|
@ -98,7 +99,7 @@ class RFC7518AppendixCTest {
|
|||
|
||||
//ensure keypair reflects required RFC test value:
|
||||
@Override
|
||||
protected KeyPair generateKeyPair(Request request, ECParameterSpec spec) {
|
||||
protected KeyPair generateKeyPair(Curve curve, Provider provider, SecureRandom random) {
|
||||
return aliceJwk.toKeyPair().toJavaKeyPair()
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,9 @@ import javax.crypto.SecretKey
|
|||
import javax.crypto.spec.SecretKeySpec
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.KeyPair
|
||||
import java.security.Provider
|
||||
import java.security.SecureRandom
|
||||
import java.security.interfaces.RSAPublicKey
|
||||
import java.security.spec.ECParameterSpec
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
|
||||
|
@ -634,7 +635,7 @@ class RFC7520Section5Test {
|
|||
def RFC_EPK = Jwks.parser().build().parse(FIGURE_111) as EcPrivateJwk
|
||||
def alg = new EcdhKeyAlgorithm(wrapAlg) {
|
||||
@Override
|
||||
protected KeyPair generateKeyPair(Request request, ECParameterSpec spec) {
|
||||
protected KeyPair generateKeyPair(Curve curve, Provider provider, SecureRandom random) {
|
||||
return new KeyPair(RFC_EPK.toPublicJwk().toKey(), RFC_EPK.toKey()) // ensure RFC value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package io.jsonwebtoken.impl.security
|
|||
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.impl.RfcTests
|
||||
import io.jsonwebtoken.security.Curve
|
||||
import io.jsonwebtoken.security.Jwks
|
||||
import io.jsonwebtoken.security.OctetPrivateJwk
|
||||
import io.jsonwebtoken.security.OctetPublicJwk
|
||||
|
@ -154,7 +155,7 @@ class RFC8037AppendixATest {
|
|||
// ensure this is used during key algorithm execution per the RFC test case:
|
||||
def alg = new EcdhKeyAlgorithm(Jwts.KEY.A128KW) {
|
||||
@Override
|
||||
protected KeyPair generateKeyPair(SecureRandom random, EdwardsCurve curve, Provider provider) {
|
||||
protected KeyPair generateKeyPair(Curve curve, Provider provider, SecureRandom random) {
|
||||
return ephemJwk.toKeyPair().toJavaKeyPair()
|
||||
}
|
||||
}
|
||||
|
@ -245,7 +246,7 @@ class RFC8037AppendixATest {
|
|||
// ensure this is used during key algorithm execution per the RFC test case:
|
||||
def alg = new EcdhKeyAlgorithm(Jwts.KEY.A256KW) {
|
||||
@Override
|
||||
protected KeyPair generateKeyPair(SecureRandom random, EdwardsCurve curve, Provider provider) {
|
||||
protected KeyPair generateKeyPair(Curve curve, Provider provider, SecureRandom random) {
|
||||
return ephemJwk.toKeyPair().toJavaKeyPair()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,42 +15,25 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.security.Jwks
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertTrue
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class CurvesTest {
|
||||
class StandardCurvesTest {
|
||||
|
||||
@Test
|
||||
void testCtor() {
|
||||
new Curves() // test coverage only
|
||||
}
|
||||
static final StandardCurves curves = (StandardCurves) Jwks.CRV.get()
|
||||
|
||||
@Test
|
||||
void testFindById() {
|
||||
Curves.VALUES.each {
|
||||
it.equals(Curves.findById(it.getId()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByJcaName() {
|
||||
Curves.VALUES.each {
|
||||
it.equals(Curves.findByJcaName(it.getJcaName()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByEllipticCurve() {
|
||||
Curves.EC_CURVES.each {
|
||||
it.equals(Curves.findBy(it.toParameterSpec().getCurve()))
|
||||
curves.values().each {
|
||||
assertSame it, curves.get(it.getId())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testKeyPairBuilders() {
|
||||
Curves.VALUES.each {
|
||||
curves.values().each {
|
||||
def pair = it.keyPair().build()
|
||||
if (it instanceof ECCurve) {
|
||||
assertEquals ECCurve.KEY_PAIR_GENERATOR_JCA_NAME, pair.getPublic().getAlgorithm()
|
Loading…
Reference in New Issue