|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.ec; |
|
|
|
import java.io.IOException; |
|
import java.math.BigInteger; |
|
import java.security.*; |
|
import java.security.spec.AlgorithmParameterSpec; |
|
import java.security.spec.ECGenParameterSpec; |
|
import java.security.spec.ECParameterSpec; |
|
import java.security.spec.ECPoint; |
|
import java.security.spec.InvalidParameterSpecException; |
|
import java.util.Optional; |
|
|
|
import sun.security.jca.JCAUtil; |
|
import sun.security.util.ECUtil; |
|
import sun.security.util.math.*; |
|
import sun.security.ec.point.*; |
|
import static sun.security.util.SecurityProviderConstants.DEF_EC_KEY_SIZE; |
|
import static sun.security.ec.ECOperations.IntermediateValueException; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final class ECKeyPairGenerator extends KeyPairGeneratorSpi { |
|
|
|
private static final int KEY_SIZE_MIN = 112; |
|
private static final int KEY_SIZE_MAX = 571; |
|
|
|
|
|
private SecureRandom random; |
|
|
|
|
|
private int keySize; |
|
|
|
|
|
private AlgorithmParameterSpec params = null; |
|
|
|
|
|
|
|
*/ |
|
public ECKeyPairGenerator() { |
|
|
|
initialize(DEF_EC_KEY_SIZE, null); |
|
} |
|
|
|
|
|
@Override |
|
public void initialize(int keySize, SecureRandom random) { |
|
|
|
checkKeySize(keySize); |
|
this.params = ECUtil.getECParameterSpec(null, keySize); |
|
if (params == null) { |
|
throw new InvalidParameterException( |
|
"No EC parameters available for key size " + keySize + " bits"); |
|
} |
|
this.random = random; |
|
} |
|
|
|
|
|
@Override |
|
public void initialize(AlgorithmParameterSpec params, SecureRandom random) |
|
throws InvalidAlgorithmParameterException { |
|
|
|
ECParameterSpec ecSpec = null; |
|
|
|
if (params instanceof ECParameterSpec) { |
|
ECParameterSpec ecParams = (ECParameterSpec) params; |
|
ecSpec = ECUtil.getECParameterSpec(null, ecParams); |
|
if (ecSpec == null) { |
|
throw new InvalidAlgorithmParameterException( |
|
"Unsupported curve: " + params); |
|
} |
|
} else if (params instanceof ECGenParameterSpec) { |
|
String name = ((ECGenParameterSpec) params).getName(); |
|
ecSpec = ECUtil.getECParameterSpec(null, name); |
|
if (ecSpec == null) { |
|
throw new InvalidAlgorithmParameterException( |
|
"Unknown curve name: " + name); |
|
} |
|
} else { |
|
throw new InvalidAlgorithmParameterException( |
|
"ECParameterSpec or ECGenParameterSpec required for EC"); |
|
} |
|
|
|
|
|
ensureCurveIsSupported(ecSpec); |
|
this.params = ecSpec; |
|
|
|
this.keySize = ecSpec.getCurve().getField().getFieldSize(); |
|
this.random = random; |
|
} |
|
|
|
private static void ensureCurveIsSupported(ECParameterSpec ecSpec) |
|
throws InvalidAlgorithmParameterException { |
|
|
|
AlgorithmParameters ecParams = ECUtil.getECParameters(null); |
|
byte[] encodedParams; |
|
try { |
|
ecParams.init(ecSpec); |
|
encodedParams = ecParams.getEncoded(); |
|
} catch (InvalidParameterSpecException ex) { |
|
throw new InvalidAlgorithmParameterException( |
|
"Unsupported curve: " + ecSpec.toString()); |
|
} catch (IOException ex) { |
|
throw new RuntimeException(ex); |
|
} |
|
if (!isCurveSupported(encodedParams)) { |
|
throw new InvalidAlgorithmParameterException( |
|
"Unsupported curve: " + ecParams.toString()); |
|
} |
|
} |
|
|
|
|
|
@Override |
|
public KeyPair generateKeyPair() { |
|
|
|
if (random == null) { |
|
random = JCAUtil.getSecureRandom(); |
|
} |
|
|
|
try { |
|
Optional<KeyPair> kp = generateKeyPairImpl(random); |
|
if (kp.isPresent()) { |
|
return kp.get(); |
|
} |
|
return generateKeyPairNative(random); |
|
} catch (Exception ex) { |
|
throw new ProviderException(ex); |
|
} |
|
} |
|
|
|
private byte[] generatePrivateScalar(SecureRandom random, |
|
ECOperations ecOps, int seedSize) { |
|
// Attempt to create the private scalar in a loop that uses new random |
|
// input each time. The chance of failure is very small assuming the |
|
|
|
int numAttempts = 128; |
|
byte[] seedArr = new byte[seedSize]; |
|
for (int i = 0; i < numAttempts; i++) { |
|
random.nextBytes(seedArr); |
|
try { |
|
return ecOps.seedToScalar(seedArr); |
|
} catch (IntermediateValueException ex) { |
|
// try again in the next iteration |
|
} |
|
} |
|
|
|
throw new ProviderException("Unable to produce private key after " |
|
+ numAttempts + " attempts"); |
|
} |
|
|
|
private Optional<KeyPair> generateKeyPairImpl(SecureRandom random) |
|
throws InvalidKeyException { |
|
|
|
ECParameterSpec ecParams = (ECParameterSpec) params; |
|
|
|
Optional<ECOperations> opsOpt = ECOperations.forParameters(ecParams); |
|
if (!opsOpt.isPresent()) { |
|
return Optional.empty(); |
|
} |
|
ECOperations ops = opsOpt.get(); |
|
IntegerFieldModuloP field = ops.getField(); |
|
int numBits = ecParams.getOrder().bitLength(); |
|
int seedBits = numBits + 64; |
|
int seedSize = (seedBits + 7) / 8; |
|
byte[] privArr = generatePrivateScalar(random, ops, seedSize); |
|
|
|
ECPoint genPoint = ecParams.getGenerator(); |
|
ImmutableIntegerModuloP x = field.getElement(genPoint.getAffineX()); |
|
ImmutableIntegerModuloP y = field.getElement(genPoint.getAffineY()); |
|
AffinePoint affGen = new AffinePoint(x, y); |
|
Point pub = ops.multiply(affGen, privArr); |
|
AffinePoint affPub = pub.asAffine(); |
|
|
|
PrivateKey privateKey = new ECPrivateKeyImpl(privArr, ecParams); |
|
|
|
ECPoint w = new ECPoint(affPub.getX().asBigInteger(), |
|
affPub.getY().asBigInteger()); |
|
PublicKey publicKey = new ECPublicKeyImpl(w, ecParams); |
|
|
|
return Optional.of(new KeyPair(publicKey, privateKey)); |
|
} |
|
|
|
private KeyPair generateKeyPairNative(SecureRandom random) |
|
throws Exception { |
|
|
|
ECParameterSpec ecParams = (ECParameterSpec) params; |
|
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, ecParams); |
|
|
|
|
|
byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2]; |
|
random.nextBytes(seed); |
|
Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed); |
|
|
|
// The 'params' object supplied above is equivalent to the native |
|
// one so there is no need to fetch it. |
|
|
|
BigInteger s = new BigInteger(1, (byte[]) keyBytes[0]); |
|
|
|
PrivateKey privateKey = new ECPrivateKeyImpl(s, ecParams); |
|
|
|
|
|
byte[] pubKey = (byte[]) keyBytes[1]; |
|
ECPoint w = ECUtil.decodePoint(pubKey, ecParams.getCurve()); |
|
PublicKey publicKey = new ECPublicKeyImpl(w, ecParams); |
|
|
|
return new KeyPair(publicKey, privateKey); |
|
} |
|
|
|
private void checkKeySize(int keySize) throws InvalidParameterException { |
|
if (keySize < KEY_SIZE_MIN) { |
|
throw new InvalidParameterException |
|
("Key size must be at least " + KEY_SIZE_MIN + " bits"); |
|
} |
|
if (keySize > KEY_SIZE_MAX) { |
|
throw new InvalidParameterException |
|
("Key size must be at most " + KEY_SIZE_MAX + " bits"); |
|
} |
|
this.keySize = keySize; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static native boolean isCurveSupported(byte[] encodedParams); |
|
|
|
|
|
|
|
|
|
*/ |
|
private static native Object[] generateECKeyPair(int keySize, |
|
byte[] encodedParams, byte[] seed) throws GeneralSecurityException; |
|
} |