1/*
2 * Copyright (c) 2006, 2013, 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.pkcs11;
27
28import java.io.IOException;
29import java.math.BigInteger;
30
31import java.security.*;
32import java.security.interfaces.*;
33import java.security.spec.*;
34
35import static sun.security.pkcs11.TemplateManager.*;
36import sun.security.pkcs11.wrapper.*;
37import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
38
39import sun.security.util.DerValue;
40import sun.security.util.ECUtil;
41
42/**
43 * EC KeyFactory implementation.
44 *
45 * @author  Andreas Sterbenz
46 * @since   1.6
47 */
48final class P11ECKeyFactory extends P11KeyFactory {
49    private static Provider sunECprovider;
50
51    private static Provider getSunECProvider() {
52        if (sunECprovider == null) {
53            sunECprovider = Security.getProvider("SunEC");
54            if (sunECprovider == null) {
55                throw new RuntimeException("Cannot load SunEC provider");
56            }
57        }
58
59        return sunECprovider;
60    }
61
62    P11ECKeyFactory(Token token, String algorithm) {
63        super(token, algorithm);
64    }
65
66    static ECParameterSpec getECParameterSpec(String name) {
67        return ECUtil.getECParameterSpec(getSunECProvider(), name);
68    }
69
70    static ECParameterSpec getECParameterSpec(int keySize) {
71        return ECUtil.getECParameterSpec(getSunECProvider(), keySize);
72    }
73
74    // Check that spec is a known supported curve and convert it to our
75    // ECParameterSpec subclass. If not possible, return null.
76    static ECParameterSpec getECParameterSpec(ECParameterSpec spec) {
77        return ECUtil.getECParameterSpec(getSunECProvider(), spec);
78    }
79
80    static ECParameterSpec decodeParameters(byte[] params) throws IOException {
81        return ECUtil.getECParameterSpec(getSunECProvider(), params);
82    }
83
84    static byte[] encodeParameters(ECParameterSpec params) {
85        return ECUtil.encodeECParameterSpec(getSunECProvider(), params);
86    }
87
88    static ECPoint decodePoint(byte[] encoded, EllipticCurve curve) throws IOException {
89        return ECUtil.decodePoint(encoded, curve);
90    }
91
92    // Used by ECDH KeyAgreement
93    static byte[] getEncodedPublicValue(PublicKey key) throws InvalidKeyException {
94        if (key instanceof ECPublicKey) {
95            ECPublicKey ecKey = (ECPublicKey)key;
96            ECPoint w = ecKey.getW();
97            ECParameterSpec params = ecKey.getParams();
98            return ECUtil.encodePoint(w, params.getCurve());
99        } else {
100            // should never occur
101            throw new InvalidKeyException
102                ("Key class not yet supported: " + key.getClass().getName());
103        }
104    }
105
106    PublicKey implTranslatePublicKey(PublicKey key) throws InvalidKeyException {
107        try {
108            if (key instanceof ECPublicKey) {
109                ECPublicKey ecKey = (ECPublicKey)key;
110                return generatePublic(
111                    ecKey.getW(),
112                    ecKey.getParams()
113                );
114            } else if ("X.509".equals(key.getFormat())) {
115                // let Sun provider parse for us, then recurse
116                byte[] encoded = key.getEncoded();
117
118                try {
119                    key = ECUtil.decodeX509ECPublicKey(encoded);
120                } catch (InvalidKeySpecException ikse) {
121                    throw new InvalidKeyException(ikse);
122                }
123
124                return implTranslatePublicKey(key);
125            } else {
126                throw new InvalidKeyException("PublicKey must be instance "
127                        + "of ECPublicKey or have X.509 encoding");
128            }
129        } catch (PKCS11Exception e) {
130            throw new InvalidKeyException("Could not create EC public key", e);
131        }
132    }
133
134    PrivateKey implTranslatePrivateKey(PrivateKey key)
135            throws InvalidKeyException {
136        try {
137            if (key instanceof ECPrivateKey) {
138                ECPrivateKey ecKey = (ECPrivateKey)key;
139                return generatePrivate(
140                    ecKey.getS(),
141                    ecKey.getParams()
142                );
143            } else if ("PKCS#8".equals(key.getFormat())) {
144                // let Sun provider parse for us, then recurse
145                byte[] encoded = key.getEncoded();
146
147                try {
148                    key = ECUtil.decodePKCS8ECPrivateKey(encoded);
149                } catch (InvalidKeySpecException ikse) {
150                    throw new InvalidKeyException(ikse);
151                }
152
153                return implTranslatePrivateKey(key);
154            } else {
155                throw new InvalidKeyException("PrivateKey must be instance "
156                        + "of ECPrivateKey or have PKCS#8 encoding");
157            }
158        } catch (PKCS11Exception e) {
159            throw new InvalidKeyException("Could not create EC private key", e);
160        }
161    }
162
163    // see JCA spec
164    protected PublicKey engineGeneratePublic(KeySpec keySpec)
165            throws InvalidKeySpecException {
166        token.ensureValid();
167        if (keySpec instanceof X509EncodedKeySpec) {
168            try {
169                byte[] encoded = ((X509EncodedKeySpec)keySpec).getEncoded();
170                PublicKey key = ECUtil.decodeX509ECPublicKey(encoded);
171                return implTranslatePublicKey(key);
172            } catch (InvalidKeyException e) {
173                throw new InvalidKeySpecException
174                        ("Could not create EC public key", e);
175            }
176        }
177        if (keySpec instanceof ECPublicKeySpec == false) {
178            throw new InvalidKeySpecException("Only ECPublicKeySpec and "
179                + "X509EncodedKeySpec supported for EC public keys");
180        }
181        try {
182            ECPublicKeySpec ec = (ECPublicKeySpec)keySpec;
183            return generatePublic(
184                ec.getW(),
185                ec.getParams()
186            );
187        } catch (PKCS11Exception e) {
188            throw new InvalidKeySpecException
189                ("Could not create EC public key", e);
190        }
191    }
192
193    // see JCA spec
194    protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
195            throws InvalidKeySpecException {
196        token.ensureValid();
197        if (keySpec instanceof PKCS8EncodedKeySpec) {
198            try {
199                byte[] encoded = ((PKCS8EncodedKeySpec)keySpec).getEncoded();
200                PrivateKey key = ECUtil.decodePKCS8ECPrivateKey(encoded);
201                return implTranslatePrivateKey(key);
202            } catch (GeneralSecurityException e) {
203                throw new InvalidKeySpecException
204                        ("Could not create EC private key", e);
205            }
206        }
207        if (keySpec instanceof ECPrivateKeySpec == false) {
208            throw new InvalidKeySpecException("Only ECPrivateKeySpec and "
209                + "PKCS8EncodedKeySpec supported for EC private keys");
210        }
211        try {
212            ECPrivateKeySpec ec = (ECPrivateKeySpec)keySpec;
213            return generatePrivate(
214                ec.getS(),
215                ec.getParams()
216            );
217        } catch (PKCS11Exception e) {
218            throw new InvalidKeySpecException
219                ("Could not create EC private key", e);
220        }
221    }
222
223    private PublicKey generatePublic(ECPoint point, ECParameterSpec params)
224            throws PKCS11Exception {
225        byte[] encodedParams =
226            ECUtil.encodeECParameterSpec(getSunECProvider(), params);
227        byte[] encodedPoint =
228            ECUtil.encodePoint(point, params.getCurve());
229
230        // Check whether the X9.63 encoding of an EC point shall be wrapped
231        // in an ASN.1 OCTET STRING
232        if (!token.config.getUseEcX963Encoding()) {
233            try {
234                encodedPoint =
235                    new DerValue(DerValue.tag_OctetString, encodedPoint)
236                        .toByteArray();
237            } catch (IOException e) {
238                throw new
239                    IllegalArgumentException("Could not DER encode point", e);
240            }
241        }
242
243        CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
244            new CK_ATTRIBUTE(CKA_CLASS, CKO_PUBLIC_KEY),
245            new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),
246            new CK_ATTRIBUTE(CKA_EC_POINT, encodedPoint),
247            new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
248        };
249        attributes = token.getAttributes
250                (O_IMPORT, CKO_PUBLIC_KEY, CKK_EC, attributes);
251        Session session = null;
252        try {
253            session = token.getObjSession();
254            long keyID = token.p11.C_CreateObject(session.id(), attributes);
255            return P11Key.publicKey
256                (session, keyID, "EC", params.getCurve().getField().getFieldSize(), attributes);
257        } finally {
258            token.releaseSession(session);
259        }
260    }
261
262    private PrivateKey generatePrivate(BigInteger s, ECParameterSpec params)
263            throws PKCS11Exception {
264        byte[] encodedParams =
265            ECUtil.encodeECParameterSpec(getSunECProvider(), params);
266        CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
267            new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY),
268            new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),
269            new CK_ATTRIBUTE(CKA_VALUE, s),
270            new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
271        };
272        attributes = token.getAttributes
273                (O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attributes);
274        Session session = null;
275        try {
276            session = token.getObjSession();
277            long keyID = token.p11.C_CreateObject(session.id(), attributes);
278            return P11Key.privateKey
279                (session, keyID, "EC", params.getCurve().getField().getFieldSize(), attributes);
280        } finally {
281            token.releaseSession(session);
282        }
283    }
284
285    <T extends KeySpec> T implGetPublicKeySpec(P11Key key, Class<T> keySpec,
286            Session[] session) throws PKCS11Exception, InvalidKeySpecException {
287        if (ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
288            session[0] = token.getObjSession();
289            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
290                new CK_ATTRIBUTE(CKA_EC_POINT),
291                new CK_ATTRIBUTE(CKA_EC_PARAMS),
292            };
293            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
294            try {
295                ECParameterSpec params = decodeParameters(attributes[1].getByteArray());
296                ECPoint point = decodePoint(attributes[0].getByteArray(), params.getCurve());
297                return keySpec.cast(new ECPublicKeySpec(point, params));
298            } catch (IOException e) {
299                throw new InvalidKeySpecException("Could not parse key", e);
300            }
301        } else { // X.509 handled in superclass
302            throw new InvalidKeySpecException("Only ECPublicKeySpec and "
303                + "X509EncodedKeySpec supported for EC public keys");
304        }
305    }
306
307    <T extends KeySpec> T implGetPrivateKeySpec(P11Key key, Class<T> keySpec,
308            Session[] session) throws PKCS11Exception, InvalidKeySpecException {
309        if (ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
310            session[0] = token.getObjSession();
311            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
312                new CK_ATTRIBUTE(CKA_VALUE),
313                new CK_ATTRIBUTE(CKA_EC_PARAMS),
314            };
315            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
316            try {
317                ECParameterSpec params = decodeParameters(attributes[1].getByteArray());
318                return keySpec.cast(
319                    new ECPrivateKeySpec(attributes[0].getBigInteger(), params));
320            } catch (IOException e) {
321                throw new InvalidKeySpecException("Could not parse key", e);
322            }
323        } else { // PKCS#8 handled in superclass
324            throw new InvalidKeySpecException("Only ECPrivateKeySpec "
325                + "and PKCS8EncodedKeySpec supported for EC private keys");
326        }
327    }
328
329    KeyFactory implGetSoftwareFactory() throws GeneralSecurityException {
330        return KeyFactory.getInstance("EC", getSunECProvider());
331    }
332
333}
334