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