1/* 2 * Copyright (c) 2006, 2012, 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 26package sun.security.ec; 27 28import java.security.*; 29import java.security.interfaces.*; 30import java.security.spec.*; 31 32/** 33 * KeyFactory for EC keys. Keys must be instances of PublicKey or PrivateKey 34 * and getAlgorithm() must return "EC". For such keys, it supports conversion 35 * between the following: 36 * 37 * For public keys: 38 * . PublicKey with an X.509 encoding 39 * . ECPublicKey 40 * . ECPublicKeySpec 41 * . X509EncodedKeySpec 42 * 43 * For private keys: 44 * . PrivateKey with a PKCS#8 encoding 45 * . ECPrivateKey 46 * . ECPrivateKeySpec 47 * . PKCS8EncodedKeySpec 48 * 49 * @since 1.6 50 * @author Andreas Sterbenz 51 */ 52public final class ECKeyFactory extends KeyFactorySpi { 53 54 // Used by translateKey() 55 private static KeyFactory instance; 56 57 private static KeyFactory getInstance() { 58 if (instance == null) { 59 try { 60 instance = KeyFactory.getInstance("EC", "SunEC"); 61 } catch (NoSuchProviderException e) { 62 throw new RuntimeException(e); 63 } catch (NoSuchAlgorithmException e) { 64 throw new RuntimeException(e); 65 } 66 } 67 68 return instance; 69 } 70 71 public ECKeyFactory() { 72 // empty 73 } 74 75 /** 76 * Static method to convert Key into a useable instance of 77 * ECPublicKey or ECPrivateKey. Check the key and convert it 78 * to a Sun key if necessary. If the key is not an EC key 79 * or cannot be used, throw an InvalidKeyException. 80 * 81 * The difference between this method and engineTranslateKey() is that 82 * we do not convert keys of other providers that are already an 83 * instance of ECPublicKey or ECPrivateKey. 84 * 85 * To be used by future Java ECDSA and ECDH implementations. 86 */ 87 public static ECKey toECKey(Key key) throws InvalidKeyException { 88 if (key instanceof ECKey) { 89 ECKey ecKey = (ECKey)key; 90 checkKey(ecKey); 91 return ecKey; 92 } else { 93 /* 94 * We don't call the engineTranslateKey method directly 95 * because KeyFactory.translateKey adds code to loop through 96 * all key factories. 97 */ 98 return (ECKey)getInstance().translateKey(key); 99 } 100 } 101 102 /** 103 * Check that the given EC key is valid. 104 */ 105 private static void checkKey(ECKey key) throws InvalidKeyException { 106 // check for subinterfaces, omit additional checks for our keys 107 if (key instanceof ECPublicKey) { 108 if (key instanceof ECPublicKeyImpl) { 109 return; 110 } 111 } else if (key instanceof ECPrivateKey) { 112 if (key instanceof ECPrivateKeyImpl) { 113 return; 114 } 115 } else { 116 throw new InvalidKeyException("Neither a public nor a private key"); 117 } 118 // ECKey does not extend Key, so we need to do a cast 119 String keyAlg = ((Key)key).getAlgorithm(); 120 if (keyAlg.equals("EC") == false) { 121 throw new InvalidKeyException("Not an EC key: " + keyAlg); 122 } 123 // XXX further sanity checks about whether this key uses supported 124 // fields, point formats, etc. would go here 125 } 126 127 /** 128 * Translate an EC key into a Sun EC key. If conversion is 129 * not possible, throw an InvalidKeyException. 130 * See also JCA doc. 131 */ 132 protected Key engineTranslateKey(Key key) throws InvalidKeyException { 133 if (key == null) { 134 throw new InvalidKeyException("Key must not be null"); 135 } 136 String keyAlg = key.getAlgorithm(); 137 if (keyAlg.equals("EC") == false) { 138 throw new InvalidKeyException("Not an EC key: " + keyAlg); 139 } 140 if (key instanceof PublicKey) { 141 return implTranslatePublicKey((PublicKey)key); 142 } else if (key instanceof PrivateKey) { 143 return implTranslatePrivateKey((PrivateKey)key); 144 } else { 145 throw new InvalidKeyException("Neither a public nor a private key"); 146 } 147 } 148 149 // see JCA doc 150 protected PublicKey engineGeneratePublic(KeySpec keySpec) 151 throws InvalidKeySpecException { 152 try { 153 return implGeneratePublic(keySpec); 154 } catch (InvalidKeySpecException e) { 155 throw e; 156 } catch (GeneralSecurityException e) { 157 throw new InvalidKeySpecException(e); 158 } 159 } 160 161 // see JCA doc 162 protected PrivateKey engineGeneratePrivate(KeySpec keySpec) 163 throws InvalidKeySpecException { 164 try { 165 return implGeneratePrivate(keySpec); 166 } catch (InvalidKeySpecException e) { 167 throw e; 168 } catch (GeneralSecurityException e) { 169 throw new InvalidKeySpecException(e); 170 } 171 } 172 173 // internal implementation of translateKey() for public keys. See JCA doc 174 private PublicKey implTranslatePublicKey(PublicKey key) 175 throws InvalidKeyException { 176 if (key instanceof ECPublicKey) { 177 if (key instanceof ECPublicKeyImpl) { 178 return key; 179 } 180 ECPublicKey ecKey = (ECPublicKey)key; 181 return new ECPublicKeyImpl( 182 ecKey.getW(), 183 ecKey.getParams() 184 ); 185 } else if ("X.509".equals(key.getFormat())) { 186 byte[] encoded = key.getEncoded(); 187 return new ECPublicKeyImpl(encoded); 188 } else { 189 throw new InvalidKeyException("Public keys must be instance " 190 + "of ECPublicKey or have X.509 encoding"); 191 } 192 } 193 194 // internal implementation of translateKey() for private keys. See JCA doc 195 private PrivateKey implTranslatePrivateKey(PrivateKey key) 196 throws InvalidKeyException { 197 if (key instanceof ECPrivateKey) { 198 if (key instanceof ECPrivateKeyImpl) { 199 return key; 200 } 201 ECPrivateKey ecKey = (ECPrivateKey)key; 202 return new ECPrivateKeyImpl( 203 ecKey.getS(), 204 ecKey.getParams() 205 ); 206 } else if ("PKCS#8".equals(key.getFormat())) { 207 return new ECPrivateKeyImpl(key.getEncoded()); 208 } else { 209 throw new InvalidKeyException("Private keys must be instance " 210 + "of ECPrivateKey or have PKCS#8 encoding"); 211 } 212 } 213 214 // internal implementation of generatePublic. See JCA doc 215 private PublicKey implGeneratePublic(KeySpec keySpec) 216 throws GeneralSecurityException { 217 if (keySpec instanceof X509EncodedKeySpec) { 218 X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec; 219 return new ECPublicKeyImpl(x509Spec.getEncoded()); 220 } else if (keySpec instanceof ECPublicKeySpec) { 221 ECPublicKeySpec ecSpec = (ECPublicKeySpec)keySpec; 222 return new ECPublicKeyImpl( 223 ecSpec.getW(), 224 ecSpec.getParams() 225 ); 226 } else { 227 throw new InvalidKeySpecException("Only ECPublicKeySpec " 228 + "and X509EncodedKeySpec supported for EC public keys"); 229 } 230 } 231 232 // internal implementation of generatePrivate. See JCA doc 233 private PrivateKey implGeneratePrivate(KeySpec keySpec) 234 throws GeneralSecurityException { 235 if (keySpec instanceof PKCS8EncodedKeySpec) { 236 PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec; 237 return new ECPrivateKeyImpl(pkcsSpec.getEncoded()); 238 } else if (keySpec instanceof ECPrivateKeySpec) { 239 ECPrivateKeySpec ecSpec = (ECPrivateKeySpec)keySpec; 240 return new ECPrivateKeyImpl(ecSpec.getS(), ecSpec.getParams()); 241 } else { 242 throw new InvalidKeySpecException("Only ECPrivateKeySpec " 243 + "and PKCS8EncodedKeySpec supported for EC private keys"); 244 } 245 } 246 247 protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec) 248 throws InvalidKeySpecException { 249 try { 250 // convert key to one of our keys 251 // this also verifies that the key is a valid EC key and ensures 252 // that the encoding is X.509/PKCS#8 for public/private keys 253 key = engineTranslateKey(key); 254 } catch (InvalidKeyException e) { 255 throw new InvalidKeySpecException(e); 256 } 257 if (key instanceof ECPublicKey) { 258 ECPublicKey ecKey = (ECPublicKey)key; 259 if (ECPublicKeySpec.class.isAssignableFrom(keySpec)) { 260 return keySpec.cast(new ECPublicKeySpec( 261 ecKey.getW(), 262 ecKey.getParams() 263 )); 264 } else if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) { 265 return keySpec.cast(new X509EncodedKeySpec(key.getEncoded())); 266 } else { 267 throw new InvalidKeySpecException 268 ("KeySpec must be ECPublicKeySpec or " 269 + "X509EncodedKeySpec for EC public keys"); 270 } 271 } else if (key instanceof ECPrivateKey) { 272 if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) { 273 return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded())); 274 } else if (ECPrivateKeySpec.class.isAssignableFrom(keySpec)) { 275 ECPrivateKey ecKey = (ECPrivateKey)key; 276 return keySpec.cast(new ECPrivateKeySpec( 277 ecKey.getS(), 278 ecKey.getParams() 279 )); 280 } else { 281 throw new InvalidKeySpecException 282 ("KeySpec must be ECPrivateKeySpec or " 283 + "PKCS8EncodedKeySpec for EC private keys"); 284 } 285 } else { 286 // should not occur, caught in engineTranslateKey() 287 throw new InvalidKeySpecException("Neither public nor private key"); 288 } 289 } 290} 291