• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.  Oracle designates this
9  * particular file as subject to the "Classpath" exception as provided
10  * by Oracle in the LICENSE file that accompanied this code.
11  *
12  * This code is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * version 2 for more details (a copy is included in the LICENSE file that
16  * accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License version
19  * 2 along with this work; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23  * or visit www.oracle.com if you need additional information or have any
24  * questions.
25  */
26 
27 package sun.security.ec;
28 
29 import java.security.*;
30 import java.security.interfaces.*;
31 import java.security.spec.*;
32 
33 /**
34  * KeyFactory for EC keys. Keys must be instances of PublicKey or PrivateKey
35  * and getAlgorithm() must return "EC". For such keys, it supports conversion
36  * between the following:
37  *
38  * For public keys:
39  *  . PublicKey with an X.509 encoding
40  *  . ECPublicKey
41  *  . ECPublicKeySpec
42  *  . X509EncodedKeySpec
43  *
44  * For private keys:
45  *  . PrivateKey with a PKCS#8 encoding
46  *  . ECPrivateKey
47  *  . ECPrivateKeySpec
48  *  . PKCS8EncodedKeySpec
49  *
50  * @since   1.6
51  * @author  Andreas Sterbenz
52  */
53 public final class ECKeyFactory extends KeyFactorySpi {
54 
55     // Used by translateKey() and the SunPKCS11 provider
56     public final static KeyFactory INSTANCE;
57 
58     // Internal provider object we can obtain the KeyFactory and
59     // AlgorithmParameters from. Used by ECParameters and AlgorithmId.
60     // This can go away once we have EC always available in the SUN provider.
61     // Used by ECParameters and AlgorithmId.
62     public final static Provider ecInternalProvider;
63 
64     static {
65         final Provider p = new Provider("SunEC-Internal", 1.0d, null) {};
AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { p.put("KeyFactory.EC", ECKeyFactory.class.getName()); p.put("AlgorithmParameters.EC", ECParameters.class.getName()); p.put("Alg.Alias.AlgorithmParameters.1.2.840.10045.2.1", "EC"); return null; } })66         AccessController.doPrivileged(new PrivilegedAction<Void>() {
67             public Void run() {
68                 // Android changed : replace string reference with class name. It
69                 // makes no difference in this case since these classes are already
70                 // in direct use here.
71                 p.put("KeyFactory.EC", ECKeyFactory.class.getName());
72                 p.put("AlgorithmParameters.EC", ECParameters.class.getName());
73                 p.put("Alg.Alias.AlgorithmParameters.1.2.840.10045.2.1", "EC");
74                 return null;
75             }
76         });
77         try {
78             INSTANCE = KeyFactory.getInstance("EC", p);
79         } catch (NoSuchAlgorithmException e) {
80             throw new RuntimeException(e);
81         }
82         ecInternalProvider = p;
83     }
84 
ECKeyFactory()85     public ECKeyFactory() {
86         // empty
87     }
88 
89     /**
90      * Static method to convert Key into a useable instance of
91      * ECPublicKey or ECPrivateKey. Check the key and convert it
92      * to a Sun key if necessary. If the key is not an EC key
93      * or cannot be used, throw an InvalidKeyException.
94      *
95      * The difference between this method and engineTranslateKey() is that
96      * we do not convert keys of other providers that are already an
97      * instance of ECPublicKey or ECPrivateKey.
98      *
99      * To be used by future Java ECDSA and ECDH implementations.
100      */
toECKey(Key key)101     public static ECKey toECKey(Key key) throws InvalidKeyException {
102         if (key instanceof ECKey) {
103             ECKey ecKey = (ECKey)key;
104             checkKey(ecKey);
105             return ecKey;
106         } else {
107             return (ECKey)INSTANCE.translateKey(key);
108         }
109     }
110 
111     /**
112      * Check that the given EC key is valid.
113      */
checkKey(ECKey key)114     private static void checkKey(ECKey key) throws InvalidKeyException {
115         // check for subinterfaces, omit additional checks for our keys
116         if (key instanceof ECPublicKey) {
117             if (key instanceof ECPublicKeyImpl) {
118                 return;
119             }
120         } else if (key instanceof ECPrivateKey) {
121             if (key instanceof ECPrivateKeyImpl) {
122                 return;
123             }
124         } else {
125             throw new InvalidKeyException("Neither a public nor a private key");
126         }
127         // ECKey does not extend Key, so we need to do a cast
128         String keyAlg = ((Key)key).getAlgorithm();
129         if (keyAlg.equals("EC") == false) {
130             throw new InvalidKeyException("Not an EC key: " + keyAlg);
131         }
132         // XXX further sanity checks about whether this key uses supported
133         // fields, point formats, etc. would go here
134     }
135 
136     /**
137      * Translate an EC key into a Sun EC key. If conversion is
138      * not possible, throw an InvalidKeyException.
139      * See also JCA doc.
140      */
engineTranslateKey(Key key)141     protected Key engineTranslateKey(Key key) throws InvalidKeyException {
142         if (key == null) {
143             throw new InvalidKeyException("Key must not be null");
144         }
145         String keyAlg = key.getAlgorithm();
146         if (keyAlg.equals("EC") == false) {
147             throw new InvalidKeyException("Not an EC key: " + keyAlg);
148         }
149         if (key instanceof PublicKey) {
150             return implTranslatePublicKey((PublicKey)key);
151         } else if (key instanceof PrivateKey) {
152             return implTranslatePrivateKey((PrivateKey)key);
153         } else {
154             throw new InvalidKeyException("Neither a public nor a private key");
155         }
156     }
157 
158     // see JCA doc
engineGeneratePublic(KeySpec keySpec)159     protected PublicKey engineGeneratePublic(KeySpec keySpec)
160             throws InvalidKeySpecException {
161         try {
162             return implGeneratePublic(keySpec);
163         } catch (InvalidKeySpecException e) {
164             throw e;
165         } catch (GeneralSecurityException e) {
166             throw new InvalidKeySpecException(e);
167         }
168     }
169 
170     // see JCA doc
engineGeneratePrivate(KeySpec keySpec)171     protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
172             throws InvalidKeySpecException {
173         try {
174             return implGeneratePrivate(keySpec);
175         } catch (InvalidKeySpecException e) {
176             throw e;
177         } catch (GeneralSecurityException e) {
178             throw new InvalidKeySpecException(e);
179         }
180     }
181 
182     // internal implementation of translateKey() for public keys. See JCA doc
implTranslatePublicKey(PublicKey key)183     private PublicKey implTranslatePublicKey(PublicKey key)
184             throws InvalidKeyException {
185         if (key instanceof ECPublicKey) {
186             if (key instanceof ECPublicKeyImpl) {
187                 return key;
188             }
189             ECPublicKey ecKey = (ECPublicKey)key;
190             return new ECPublicKeyImpl(
191                 ecKey.getW(),
192                 ecKey.getParams()
193             );
194         } else if ("X.509".equals(key.getFormat())) {
195             byte[] encoded = key.getEncoded();
196             return new ECPublicKeyImpl(encoded);
197         } else {
198             throw new InvalidKeyException("Public keys must be instance "
199                 + "of ECPublicKey or have X.509 encoding");
200         }
201     }
202 
203     // internal implementation of translateKey() for private keys. See JCA doc
implTranslatePrivateKey(PrivateKey key)204     private PrivateKey implTranslatePrivateKey(PrivateKey key)
205             throws InvalidKeyException {
206         if (key instanceof ECPrivateKey) {
207             if (key instanceof ECPrivateKeyImpl) {
208                 return key;
209             }
210             ECPrivateKey ecKey = (ECPrivateKey)key;
211             return new ECPrivateKeyImpl(
212                 ecKey.getS(),
213                 ecKey.getParams()
214             );
215         } else if ("PKCS#8".equals(key.getFormat())) {
216             return new ECPrivateKeyImpl(key.getEncoded());
217         } else {
218             throw new InvalidKeyException("Private keys must be instance "
219                 + "of ECPrivateKey or have PKCS#8 encoding");
220         }
221     }
222 
223     // internal implementation of generatePublic. See JCA doc
implGeneratePublic(KeySpec keySpec)224     private PublicKey implGeneratePublic(KeySpec keySpec)
225             throws GeneralSecurityException {
226         if (keySpec instanceof X509EncodedKeySpec) {
227             X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec;
228             return new ECPublicKeyImpl(x509Spec.getEncoded());
229         } else if (keySpec instanceof ECPublicKeySpec) {
230             ECPublicKeySpec ecSpec = (ECPublicKeySpec)keySpec;
231             return new ECPublicKeyImpl(
232                 ecSpec.getW(),
233                 ecSpec.getParams()
234             );
235         } else {
236             throw new InvalidKeySpecException("Only ECPublicKeySpec "
237                 + "and X509EncodedKeySpec supported for EC public keys");
238         }
239     }
240 
241     // internal implementation of generatePrivate. See JCA doc
implGeneratePrivate(KeySpec keySpec)242     private PrivateKey implGeneratePrivate(KeySpec keySpec)
243             throws GeneralSecurityException {
244         if (keySpec instanceof PKCS8EncodedKeySpec) {
245             PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec;
246             return new ECPrivateKeyImpl(pkcsSpec.getEncoded());
247         } else if (keySpec instanceof ECPrivateKeySpec) {
248             ECPrivateKeySpec ecSpec = (ECPrivateKeySpec)keySpec;
249             return new ECPrivateKeyImpl(ecSpec.getS(), ecSpec.getParams());
250         } else {
251             throw new InvalidKeySpecException("Only ECPrivateKeySpec "
252                 + "and PKCS8EncodedKeySpec supported for EC private keys");
253         }
254     }
255 
engineGetKeySpec(Key key, Class<T> keySpec)256     protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
257             throws InvalidKeySpecException {
258         try {
259             // convert key to one of our keys
260             // this also verifies that the key is a valid EC key and ensures
261             // that the encoding is X.509/PKCS#8 for public/private keys
262             key = engineTranslateKey(key);
263         } catch (InvalidKeyException e) {
264             throw new InvalidKeySpecException(e);
265         }
266         if (key instanceof ECPublicKey) {
267             ECPublicKey ecKey = (ECPublicKey)key;
268             if (ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
269                 return (T) new ECPublicKeySpec(
270                     ecKey.getW(),
271                     ecKey.getParams()
272                 );
273             } else if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
274                 return (T) new X509EncodedKeySpec(key.getEncoded());
275             } else {
276                 throw new InvalidKeySpecException
277                         ("KeySpec must be ECPublicKeySpec or "
278                         + "X509EncodedKeySpec for EC public keys");
279             }
280         } else if (key instanceof ECPrivateKey) {
281             if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
282                 return (T) new PKCS8EncodedKeySpec(key.getEncoded());
283             } else if (ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
284                 ECPrivateKey ecKey = (ECPrivateKey)key;
285                 return (T) new ECPrivateKeySpec(
286                     ecKey.getS(),
287                     ecKey.getParams()
288                 );
289             } else {
290                 throw new InvalidKeySpecException
291                         ("KeySpec must be ECPrivateKeySpec or "
292                         + "PKCS8EncodedKeySpec for EC private keys");
293             }
294         } else {
295             // should not occur, caught in engineTranslateKey()
296             throw new InvalidKeySpecException("Neither public nor private key");
297         }
298     }
299 }
300