1 /* 2 * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.ec; 27 28 import java.io.IOException; 29 import java.math.BigInteger; 30 import java.security.*; 31 import java.security.spec.AlgorithmParameterSpec; 32 import java.security.spec.ECGenParameterSpec; 33 import java.security.spec.ECParameterSpec; 34 import java.security.spec.ECPoint; 35 import java.security.spec.InvalidParameterSpecException; 36 import java.util.Arrays; 37 import java.util.Optional; 38 39 import sun.security.jca.JCAUtil; 40 import sun.security.util.ECUtil; 41 import sun.security.util.math.*; 42 import sun.security.ec.point.*; 43 import static sun.security.util.SecurityProviderConstants.DEF_EC_KEY_SIZE; 44 import static sun.security.ec.ECOperations.IntermediateValueException; 45 46 /** 47 * EC keypair generator. 48 * Standard algorithm, minimum key length is 112 bits, maximum is 571 bits. 49 * 50 * @since 1.7 51 */ 52 public final class ECKeyPairGenerator extends KeyPairGeneratorSpi { 53 54 private static final int KEY_SIZE_MIN = 112; // min bits (see ecc_impl.h) 55 private static final int KEY_SIZE_MAX = 571; // max bits (see ecc_impl.h) 56 57 // used to seed the keypair generator 58 private SecureRandom random; 59 60 // size of the key to generate, KEY_SIZE_MIN <= keySize <= KEY_SIZE_MAX 61 private int keySize; 62 63 // parameters specified via init, if any 64 private AlgorithmParameterSpec params = null; 65 66 /** 67 * Constructs a new ECKeyPairGenerator. 68 */ ECKeyPairGenerator()69 public ECKeyPairGenerator() { 70 // initialize to default in case the app does not call initialize() 71 initialize(DEF_EC_KEY_SIZE, null); 72 } 73 74 // initialize the generator. See JCA doc 75 @Override initialize(int keySize, SecureRandom random)76 public void initialize(int keySize, SecureRandom random) { 77 78 checkKeySize(keySize); 79 this.params = ECUtil.getECParameterSpec(null, keySize); 80 if (params == null) { 81 throw new InvalidParameterException( 82 "No EC parameters available for key size " + keySize + " bits"); 83 } 84 this.random = random; 85 } 86 87 // second initialize method. See JCA doc 88 @Override initialize(AlgorithmParameterSpec params, SecureRandom random)89 public void initialize(AlgorithmParameterSpec params, SecureRandom random) 90 throws InvalidAlgorithmParameterException { 91 92 ECParameterSpec ecSpec = null; 93 94 if (params instanceof ECParameterSpec) { 95 ECParameterSpec ecParams = (ECParameterSpec) params; 96 ecSpec = ECUtil.getECParameterSpec(null, ecParams); 97 if (ecSpec == null) { 98 throw new InvalidAlgorithmParameterException( 99 "Curve not supported: " + params); 100 } 101 } else if (params instanceof ECGenParameterSpec) { 102 String name = ((ECGenParameterSpec) params).getName(); 103 ecSpec = ECUtil.getECParameterSpec(null, name); 104 if (ecSpec == null) { 105 throw new InvalidAlgorithmParameterException( 106 "Unknown curve name: " + name); 107 } 108 } else { 109 throw new InvalidAlgorithmParameterException( 110 "ECParameterSpec or ECGenParameterSpec required for EC"); 111 } 112 113 // Not all known curves are supported by the native implementation 114 ensureCurveIsSupported(ecSpec); 115 this.params = ecSpec; 116 117 this.keySize = ecSpec.getCurve().getField().getFieldSize(); 118 this.random = random; 119 } 120 ensureCurveIsSupported(ECParameterSpec ecSpec)121 private static void ensureCurveIsSupported(ECParameterSpec ecSpec) 122 throws InvalidAlgorithmParameterException { 123 124 // Check if ecSpec is a valid curve 125 AlgorithmParameters ecParams = ECUtil.getECParameters(null); 126 try { 127 ecParams.init(ecSpec); 128 } catch (InvalidParameterSpecException ex) { 129 throw new InvalidAlgorithmParameterException( 130 "Curve not supported: " + ecSpec.toString()); 131 } 132 133 // Check if the java implementation supports this curve 134 if (ECOperations.forParameters(ecSpec).isEmpty()) { 135 throw new InvalidAlgorithmParameterException( 136 "Curve not supported: " + ecSpec.toString()); 137 } 138 } 139 140 // generate the keypair. See JCA doc 141 @Override generateKeyPair()142 public KeyPair generateKeyPair() { 143 144 if (random == null) { 145 random = JCAUtil.getSecureRandom(); 146 } 147 148 try { 149 Optional<KeyPair> kp = generateKeyPairImpl(random); 150 if (kp.isPresent()) { 151 return kp.get(); 152 } 153 } catch (Exception ex) { 154 throw new ProviderException(ex); 155 } 156 throw new ProviderException("Curve not supported: " + 157 params.toString()); 158 } 159 generatePrivateScalar(SecureRandom random, ECOperations ecOps, int seedSize)160 private byte[] generatePrivateScalar(SecureRandom random, 161 ECOperations ecOps, int seedSize) { 162 // Attempt to create the private scalar in a loop that uses new random 163 // input each time. The chance of failure is very small assuming the 164 // implementation derives the nonce using extra bits 165 int numAttempts = 128; 166 byte[] seedArr = new byte[seedSize]; 167 for (int i = 0; i < numAttempts; i++) { 168 random.nextBytes(seedArr); 169 try { 170 return ecOps.seedToScalar(seedArr); 171 } catch (IntermediateValueException ex) { 172 // try again in the next iteration 173 } 174 } 175 176 throw new ProviderException("Unable to produce private key after " 177 + numAttempts + " attempts"); 178 } 179 generateKeyPairImpl(SecureRandom random)180 private Optional<KeyPair> generateKeyPairImpl(SecureRandom random) 181 throws InvalidKeyException { 182 183 ECParameterSpec ecParams = (ECParameterSpec) params; 184 185 Optional<ECOperations> opsOpt = ECOperations.forParameters(ecParams); 186 if (opsOpt.isEmpty()) { 187 return Optional.empty(); 188 } 189 ECOperations ops = opsOpt.get(); 190 IntegerFieldModuloP field = ops.getField(); 191 int numBits = ecParams.getOrder().bitLength(); 192 int seedBits = numBits + 64; 193 int seedSize = (seedBits + 7) / 8; 194 byte[] privArr = generatePrivateScalar(random, ops, seedSize); 195 196 ECPoint genPoint = ecParams.getGenerator(); 197 ImmutableIntegerModuloP x = field.getElement(genPoint.getAffineX()); 198 ImmutableIntegerModuloP y = field.getElement(genPoint.getAffineY()); 199 AffinePoint affGen = new AffinePoint(x, y); 200 Point pub = ops.multiply(affGen, privArr); 201 AffinePoint affPub = pub.asAffine(); 202 203 PrivateKey privateKey = new ECPrivateKeyImpl(privArr, ecParams); 204 Arrays.fill(privArr, (byte)0); 205 206 ECPoint w = new ECPoint(affPub.getX().asBigInteger(), 207 affPub.getY().asBigInteger()); 208 PublicKey publicKey = new ECPublicKeyImpl(w, ecParams); 209 210 return Optional.of(new KeyPair(publicKey, privateKey)); 211 } 212 checkKeySize(int keySize)213 private void checkKeySize(int keySize) throws InvalidParameterException { 214 if (keySize < KEY_SIZE_MIN) { 215 throw new InvalidParameterException 216 ("Key size must be at least " + KEY_SIZE_MIN + " bits"); 217 } 218 if (keySize > KEY_SIZE_MAX) { 219 throw new InvalidParameterException 220 ("Key size must be at most " + KEY_SIZE_MAX + " bits"); 221 } 222 this.keySize = keySize; 223 } 224 } 225