1 /* 2 * Copyright (c) 2009, 2017, 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 37 import sun.security.ec.ECPrivateKeyImpl; 38 import sun.security.ec.ECPublicKeyImpl; 39 import sun.security.jca.JCAUtil; 40 import sun.security.util.ECParameters; 41 import sun.security.util.ECUtil; 42 43 /** 44 * EC keypair generator. 45 * Standard algorithm, minimum key length is 112 bits, maximum is 571 bits. 46 * 47 * @since 1.7 48 */ 49 public final class ECKeyPairGenerator extends KeyPairGeneratorSpi { 50 51 private static final int KEY_SIZE_MIN = 112; // min bits (see ecc_impl.h) 52 private static final int KEY_SIZE_MAX = 571; // max bits (see ecc_impl.h) 53 private static final int KEY_SIZE_DEFAULT = 256; 54 55 // used to seed the keypair generator 56 private SecureRandom random; 57 58 // size of the key to generate, KEY_SIZE_MIN <= keySize <= KEY_SIZE_MAX 59 private int keySize; 60 61 // parameters specified via init, if any 62 private AlgorithmParameterSpec params = null; 63 64 /** 65 * Constructs a new ECKeyPairGenerator. 66 */ 67 public ECKeyPairGenerator() { 68 // initialize to default in case the app does not call initialize() 69 initialize(KEY_SIZE_DEFAULT, null); 70 } 71 72 // initialize the generator. See JCA doc 73 @Override 74 public void initialize(int keySize, SecureRandom random) { 75 76 checkKeySize(keySize); 77 this.params = ECUtil.getECParameterSpec(null, keySize); 78 if (params == null) { 79 throw new InvalidParameterException( 80 "No EC parameters available for key size " + keySize + " bits"); 81 } 82 this.random = random; 83 } 84 85 // second initialize method. See JCA doc 86 @Override 87 public void initialize(AlgorithmParameterSpec params, SecureRandom random) 88 throws InvalidAlgorithmParameterException { 89 90 ECParameterSpec ecSpec = null; 91 92 if (params instanceof ECParameterSpec) { 93 ecSpec = ECUtil.getECParameterSpec(null, 94 (ECParameterSpec)params); 95 if (ecSpec == null) { 96 throw new InvalidAlgorithmParameterException( 97 "Unsupported curve: " + params); 98 } 99 } else if (params instanceof ECGenParameterSpec) { 100 String name = ((ECGenParameterSpec)params).getName(); 101 ecSpec = ECUtil.getECParameterSpec(null, name); 102 if (ecSpec == null) { 103 throw new InvalidAlgorithmParameterException( 104 "Unknown curve name: " + name); 105 } 106 } else { 107 throw new InvalidAlgorithmParameterException( 108 "ECParameterSpec or ECGenParameterSpec required for EC"); 109 } 110 111 // Not all known curves are supported by the native implementation 112 ensureCurveIsSupported(ecSpec); 113 this.params = ecSpec; 114 115 this.keySize = 116 ((ECParameterSpec)this.params).getCurve().getField().getFieldSize(); 117 this.random = random; 118 } 119 120 private static void ensureCurveIsSupported(ECParameterSpec ecSpec) 121 throws InvalidAlgorithmParameterException { 122 123 AlgorithmParameters ecParams = ECUtil.getECParameters(null); 124 byte[] encodedParams; 125 try { 126 ecParams.init(ecSpec); 127 encodedParams = ecParams.getEncoded(); 128 } catch (InvalidParameterSpecException ex) { 129 throw new InvalidAlgorithmParameterException( 130 "Unsupported curve: " + ecSpec.toString()); 131 } catch (IOException ex) { 132 throw new RuntimeException(ex); 133 } 134 if (!isCurveSupported(encodedParams)) { 135 throw new InvalidAlgorithmParameterException( 136 "Unsupported curve: " + ecParams.toString()); 137 } 138 } 139 140 // generate the keypair. See JCA doc 141 @Override 142 public KeyPair generateKeyPair() { 143 144 byte[] encodedParams = 145 ECUtil.encodeECParameterSpec(null, (ECParameterSpec)params); 146 147 // seed is twice the key size (in bytes) plus 1 148 byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2]; 149 if (random == null) { 150 random = JCAUtil.getSecureRandom(); 151 } 152 random.nextBytes(seed); 153 154 try { 155 156 Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed); 157 158 // The 'params' object supplied above is equivalent to the native 159 // one so there is no need to fetch it. 160 // keyBytes[0] is the encoding of the native private key 161 BigInteger s = new BigInteger(1, (byte[])keyBytes[0]); 162 163 PrivateKey privateKey = 164 new ECPrivateKeyImpl(s, (ECParameterSpec)params); 165 166 // keyBytes[1] is the encoding of the native public key 167 ECPoint w = ECUtil.decodePoint((byte[])keyBytes[1], 168 ((ECParameterSpec)params).getCurve()); 169 PublicKey publicKey = 170 new ECPublicKeyImpl(w, (ECParameterSpec)params); 171 172 return new KeyPair(publicKey, privateKey); 173 174 } catch (Exception e) { 175 throw new ProviderException(e); 176 } 177 } 178 179 private void checkKeySize(int keySize) throws InvalidParameterException { 180 if (keySize < KEY_SIZE_MIN) { 181 throw new InvalidParameterException 182 ("Key size must be at least " + KEY_SIZE_MIN + " bits"); 183 } 184 if (keySize > KEY_SIZE_MAX) { 185 throw new InvalidParameterException 186 ("Key size must be at most " + KEY_SIZE_MAX + " bits"); 187 } 188 this.keySize = keySize; 189 } 190 191 /** 192 * Checks whether the curve in the encoded parameters is supported by the 193 * native implementation. 194 * 195 * @param encodedParams encoded parameters in the same form accepted 196 * by generateECKeyPair 197 * @return true if and only if generateECKeyPair will succeed for 198 * the supplied parameters 199 */ 200 private static native boolean isCurveSupported(byte[] encodedParams); 201 202 /* 203 * Generates the keypair and returns a 2-element array of encoding bytes. 204 * The first one is for the private key, the second for the public key. 205 */ 206 private static native Object[] generateECKeyPair(int keySize, 207 byte[] encodedParams, byte[] seed) throws GeneralSecurityException; 208 }