|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.ec; |
|
|
|
import java.math.*; |
|
import java.security.*; |
|
import java.security.interfaces.*; |
|
import java.security.spec.*; |
|
import java.util.Optional; |
|
|
|
import javax.crypto.*; |
|
import javax.crypto.spec.*; |
|
|
|
import sun.security.util.ArrayUtil; |
|
import sun.security.util.ECUtil; |
|
import sun.security.util.math.*; |
|
import sun.security.ec.point.*; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final class ECDHKeyAgreement extends KeyAgreementSpi { |
|
|
|
|
|
private ECPrivateKey privateKey; |
|
|
|
|
|
private ECPublicKey publicKey; |
|
|
|
|
|
private int secretLen; |
|
|
|
|
|
|
|
*/ |
|
public ECDHKeyAgreement() { |
|
} |
|
|
|
|
|
@Override |
|
protected void engineInit(Key key, SecureRandom random) |
|
throws InvalidKeyException { |
|
if (!(key instanceof PrivateKey)) { |
|
throw new InvalidKeyException |
|
("Key must be instance of PrivateKey"); |
|
} |
|
privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key); |
|
publicKey = null; |
|
} |
|
|
|
|
|
@Override |
|
protected void engineInit(Key key, AlgorithmParameterSpec params, |
|
SecureRandom random) throws InvalidKeyException, |
|
InvalidAlgorithmParameterException { |
|
if (params != null) { |
|
throw new InvalidAlgorithmParameterException |
|
("Parameters not supported"); |
|
} |
|
engineInit(key, random); |
|
} |
|
|
|
|
|
@Override |
|
protected Key engineDoPhase(Key key, boolean lastPhase) |
|
throws InvalidKeyException, IllegalStateException { |
|
if (privateKey == null) { |
|
throw new IllegalStateException("Not initialized"); |
|
} |
|
if (publicKey != null) { |
|
throw new IllegalStateException("Phase already executed"); |
|
} |
|
if (!lastPhase) { |
|
throw new IllegalStateException |
|
("Only two party agreement supported, lastPhase must be true"); |
|
} |
|
if (!(key instanceof ECPublicKey)) { |
|
throw new InvalidKeyException |
|
("Key must be a PublicKey with algorithm EC"); |
|
} |
|
|
|
this.publicKey = (ECPublicKey) key; |
|
|
|
ECParameterSpec params = publicKey.getParams(); |
|
int keyLenBits = params.getCurve().getField().getFieldSize(); |
|
secretLen = (keyLenBits + 7) >> 3; |
|
|
|
return null; |
|
} |
|
|
|
private static void validateCoordinate(BigInteger c, BigInteger mod) { |
|
if (c.compareTo(BigInteger.ZERO) < 0) { |
|
throw new ProviderException("invalid coordinate"); |
|
} |
|
|
|
if (c.compareTo(mod) >= 0) { |
|
throw new ProviderException("invalid coordinate"); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static void validate(ECOperations ops, ECPublicKey key) { |
|
|
|
|
|
BigInteger x = key.getW().getAffineX(); |
|
BigInteger y = key.getW().getAffineY(); |
|
|
|
BigInteger p = ops.getField().getSize(); |
|
validateCoordinate(x, p); |
|
validateCoordinate(y, p); |
|
|
|
|
|
EllipticCurve curve = key.getParams().getCurve(); |
|
BigInteger rhs = x.modPow(BigInteger.valueOf(3), p).add(curve.getA() |
|
.multiply(x)).add(curve.getB()).mod(p); |
|
BigInteger lhs = y.modPow(BigInteger.valueOf(2), p).mod(p); |
|
if (!rhs.equals(lhs)) { |
|
throw new ProviderException("point is not on curve"); |
|
} |
|
|
|
|
|
ImmutableIntegerModuloP xElem = ops.getField().getElement(x); |
|
ImmutableIntegerModuloP yElem = ops.getField().getElement(y); |
|
AffinePoint affP = new AffinePoint(xElem, yElem); |
|
byte[] order = key.getParams().getOrder().toByteArray(); |
|
ArrayUtil.reverse(order); |
|
Point product = ops.multiply(affP, order); |
|
if (!ops.isNeutral(product)) { |
|
throw new ProviderException("point has incorrect order"); |
|
} |
|
|
|
} |
|
|
|
|
|
@Override |
|
protected byte[] engineGenerateSecret() throws IllegalStateException { |
|
if ((privateKey == null) || (publicKey == null)) { |
|
throw new IllegalStateException("Not initialized correctly"); |
|
} |
|
|
|
Optional<byte[]> resultOpt = deriveKeyImpl(privateKey, publicKey); |
|
return resultOpt.orElseGet( |
|
() -> deriveKeyNative(privateKey, publicKey) |
|
); |
|
} |
|
|
|
|
|
@Override |
|
protected int engineGenerateSecret(byte[] sharedSecret, int |
|
offset) throws IllegalStateException, ShortBufferException { |
|
if (offset + secretLen > sharedSecret.length) { |
|
throw new ShortBufferException("Need " + secretLen |
|
+ " bytes, only " + (sharedSecret.length - offset) |
|
+ " available"); |
|
} |
|
byte[] secret = engineGenerateSecret(); |
|
System.arraycopy(secret, 0, sharedSecret, offset, secret.length); |
|
return secret.length; |
|
} |
|
|
|
|
|
@Override |
|
protected SecretKey engineGenerateSecret(String algorithm) |
|
throws IllegalStateException, NoSuchAlgorithmException, |
|
InvalidKeyException { |
|
if (algorithm == null) { |
|
throw new NoSuchAlgorithmException("Algorithm must not be null"); |
|
} |
|
if (!(algorithm.equals("TlsPremasterSecret"))) { |
|
throw new NoSuchAlgorithmException |
|
("Only supported for algorithm TlsPremasterSecret"); |
|
} |
|
return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret"); |
|
} |
|
|
|
private static |
|
Optional<byte[]> deriveKeyImpl(ECPrivateKey priv, ECPublicKey pubKey) { |
|
|
|
ECParameterSpec ecSpec = priv.getParams(); |
|
EllipticCurve curve = ecSpec.getCurve(); |
|
Optional<ECOperations> opsOpt = ECOperations.forParameters(ecSpec); |
|
if (!opsOpt.isPresent()) { |
|
return Optional.empty(); |
|
} |
|
ECOperations ops = opsOpt.get(); |
|
if (! (priv instanceof ECPrivateKeyImpl)) { |
|
return Optional.empty(); |
|
} |
|
ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) priv; |
|
byte[] sArr = privImpl.getArrayS(); |
|
|
|
// to match the native implementation, validate the public key here |
|
|
|
validate(ops, pubKey); |
|
|
|
IntegerFieldModuloP field = ops.getField(); |
|
|
|
MutableIntegerModuloP scalar = field.getElement(sArr).mutable(); |
|
SmallValue cofactor = |
|
field.getSmallValue(priv.getParams().getCofactor()); |
|
scalar.setProduct(cofactor); |
|
int keySize = (curve.getField().getFieldSize() + 7) / 8; |
|
byte[] privArr = scalar.asByteArray(keySize); |
|
|
|
ImmutableIntegerModuloP x = |
|
field.getElement(pubKey.getW().getAffineX()); |
|
ImmutableIntegerModuloP y = |
|
field.getElement(pubKey.getW().getAffineY()); |
|
AffinePoint affPub = new AffinePoint(x, y); |
|
Point product = ops.multiply(affPub, privArr); |
|
if (ops.isNeutral(product)) { |
|
throw new ProviderException("Product is zero"); |
|
} |
|
AffinePoint affProduct = product.asAffine(); |
|
|
|
byte[] result = affProduct.getX().asByteArray(keySize); |
|
ArrayUtil.reverse(result); |
|
|
|
return Optional.of(result); |
|
} |
|
|
|
private static |
|
byte[] deriveKeyNative(ECPrivateKey privateKey, ECPublicKey publicKey) { |
|
|
|
ECParameterSpec params = privateKey.getParams(); |
|
byte[] s = privateKey.getS().toByteArray(); |
|
byte[] encodedParams = |
|
ECUtil.encodeECParameterSpec(null, params); |
|
|
|
byte[] publicValue; |
|
if (publicKey instanceof ECPublicKeyImpl) { |
|
ECPublicKeyImpl ecPub = (ECPublicKeyImpl) publicKey; |
|
publicValue = ecPub.getEncodedPublicValue(); |
|
} else { |
|
publicValue = |
|
ECUtil.encodePoint(publicKey.getW(), params.getCurve()); |
|
} |
|
|
|
try { |
|
return deriveKey(s, publicValue, encodedParams); |
|
|
|
} catch (GeneralSecurityException e) { |
|
throw new ProviderException("Could not derive key", e); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static native byte[] deriveKey(byte[] s, byte[] w, |
|
byte[] encodedParams) throws GeneralSecurityException; |
|
} |