1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.conscrypt;
18 
19 import java.io.IOException;
20 import java.io.NotSerializableException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 import java.math.BigInteger;
24 import java.security.InvalidAlgorithmParameterException;
25 import java.security.InvalidKeyException;
26 import java.security.PrivateKey;
27 import java.security.PublicKey;
28 import java.security.interfaces.ECKey;
29 import java.security.interfaces.ECPrivateKey;
30 import java.security.spec.ECParameterSpec;
31 import java.security.spec.ECPrivateKeySpec;
32 import java.security.spec.InvalidKeySpecException;
33 import java.util.Arrays;
34 
35 public final class OpenSSLECPrivateKey implements ECPrivateKey, OpenSSLKeyHolder {
36     private static final long serialVersionUID = -4036633595001083922L;
37 
38     private static final String ALGORITHM = "EC";
39 
40     protected transient OpenSSLKey key;
41 
42     protected transient OpenSSLECGroupContext group;
43 
OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key)44     public OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key) {
45         this.group = group;
46         this.key = key;
47     }
48 
OpenSSLECPrivateKey(OpenSSLKey key)49     public OpenSSLECPrivateKey(OpenSSLKey key) {
50         this.group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
51                 NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
52         this.key = key;
53     }
54 
OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec)55     public OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec) throws InvalidKeySpecException {
56         try {
57             group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams());
58             final BigInteger privKey = ecKeySpec.getS();
59             key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(), null,
60                     privKey.toByteArray()));
61         } catch (Exception e) {
62             throw new InvalidKeySpecException(e);
63         }
64     }
65 
wrapPlatformKey(ECPrivateKey ecPrivateKey)66     public static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
67         OpenSSLECGroupContext group;
68         try {
69             group = OpenSSLECGroupContext.getInstance(ecPrivateKey.getParams());
70         } catch (InvalidAlgorithmParameterException e) {
71             throw new InvalidKeyException("Unknown group parameters", e);
72         }
73         return wrapPlatformKey(ecPrivateKey, group);
74     }
75 
76     /**
77      * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations
78      * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the
79      * provider which accepts the key.
80      */
wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey, PublicKey publicKey)81     static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey,
82             PublicKey publicKey) throws InvalidKeyException {
83         ECParameterSpec params = null;
84         if (privateKey instanceof ECKey) {
85             params = ((ECKey) privateKey).getParams();
86         } else if (publicKey instanceof ECKey) {
87             params = ((ECKey) publicKey).getParams();
88         }
89         if (params == null) {
90             throw new InvalidKeyException("EC parameters not available. Private: " + privateKey
91                     + ", public: " + publicKey);
92         }
93         return wrapJCAPrivateKeyForTLSStackOnly(privateKey, params);
94     }
95 
96     /**
97      * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations
98      * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the
99      * provider which accepts the key.
100      */
wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey, ECParameterSpec params)101     static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey,
102             ECParameterSpec params) throws InvalidKeyException {
103         if (params == null) {
104             if (privateKey instanceof ECKey) {
105                 params = ((ECKey) privateKey).getParams();
106             }
107         }
108         if (params == null) {
109             throw new InvalidKeyException("EC parameters not available: " + privateKey);
110         }
111 
112         OpenSSLECGroupContext group;
113         try {
114             group = OpenSSLECGroupContext.getInstance(params);
115         } catch (InvalidAlgorithmParameterException e) {
116             throw new InvalidKeyException("Invalid EC parameters: " + params);
117         }
118 
119         return new OpenSSLKey(
120                 NativeCrypto.getECPrivateKeyWrapper(privateKey, group.getNativeRef()), true);
121     }
122 
wrapPlatformKey(ECPrivateKey ecPrivateKey, OpenSSLECGroupContext group)123     private static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey,
124             OpenSSLECGroupContext group) throws InvalidKeyException {
125         return new OpenSSLKey(NativeCrypto.getECPrivateKeyWrapper(ecPrivateKey,
126                 group.getNativeRef()), true);
127     }
128 
getInstance(ECPrivateKey ecPrivateKey)129     public static OpenSSLKey getInstance(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
130         try {
131             OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecPrivateKey
132                     .getParams());
133 
134             /**
135              * If the key is not encodable (PKCS11-like key), then wrap it and
136              * use JNI upcalls to satisfy requests.
137              */
138             if (ecPrivateKey.getFormat() == null) {
139                 return wrapPlatformKey(ecPrivateKey, group);
140             }
141 
142             final BigInteger privKey = ecPrivateKey.getS();
143             return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(), null,
144                     privKey.toByteArray()));
145         } catch (Exception e) {
146             throw new InvalidKeyException(e);
147         }
148     }
149 
150     @Override
getAlgorithm()151     public String getAlgorithm() {
152         return ALGORITHM;
153     }
154 
155     @Override
getFormat()156     public String getFormat() {
157         /*
158          * If we're using an OpenSSL ENGINE, there's no guarantee we can export
159          * the key. Returning {@code null} tells the caller that there's no
160          * encoded format.
161          */
162         if (key.isEngineBased()) {
163             return null;
164         }
165 
166         return "PKCS#8";
167     }
168 
169     @Override
getEncoded()170     public byte[] getEncoded() {
171         /*
172          * If we're using an OpenSSL ENGINE, there's no guarantee we can export
173          * the key. Returning {@code null} tells the caller that there's no
174          * encoded format.
175          */
176         if (key.isEngineBased()) {
177             return null;
178         }
179 
180         return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getNativeRef());
181     }
182 
183     @Override
getParams()184     public ECParameterSpec getParams() {
185         return group.getECParameterSpec();
186     }
187 
188     @Override
getS()189     public BigInteger getS() {
190         if (key.isEngineBased()) {
191             throw new UnsupportedOperationException("private key value S cannot be extracted");
192         }
193 
194         return getPrivateKey();
195     }
196 
getPrivateKey()197     private BigInteger getPrivateKey() {
198         return new BigInteger(NativeCrypto.EC_KEY_get_private_key(key.getNativeRef()));
199     }
200 
201     @Override
getOpenSSLKey()202     public OpenSSLKey getOpenSSLKey() {
203         return key;
204     }
205 
206     @Override
equals(Object o)207     public boolean equals(Object o) {
208         if (o == this) {
209             return true;
210         }
211 
212         if (o instanceof OpenSSLECPrivateKey) {
213             OpenSSLECPrivateKey other = (OpenSSLECPrivateKey) o;
214             return key.equals(other.key);
215         }
216 
217         if (!(o instanceof ECPrivateKey)) {
218             return false;
219         }
220 
221         final ECPrivateKey other = (ECPrivateKey) o;
222         if (!getPrivateKey().equals(other.getS())) {
223             return false;
224         }
225 
226         final ECParameterSpec spec = getParams();
227         final ECParameterSpec otherSpec = other.getParams();
228 
229         return spec.getCurve().equals(otherSpec.getCurve())
230                 && spec.getGenerator().equals(otherSpec.getGenerator())
231                 && spec.getOrder().equals(otherSpec.getOrder())
232                 && spec.getCofactor() == otherSpec.getCofactor();
233     }
234 
235     @Override
hashCode()236     public int hashCode() {
237         return Arrays.hashCode(NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getNativeRef()));
238     }
239 
240     @Override
toString()241     public String toString() {
242         StringBuilder sb = new StringBuilder("OpenSSLECPrivateKey{");
243         sb.append("params={");
244         sb.append(NativeCrypto.EVP_PKEY_print_params(key.getNativeRef()));
245         sb.append("}}");
246         return sb.toString();
247     }
248 
readObject(ObjectInputStream stream)249     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
250         stream.defaultReadObject();
251 
252         byte[] encoded = (byte[]) stream.readObject();
253 
254         key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(encoded));
255         group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
256                 NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
257     }
258 
writeObject(ObjectOutputStream stream)259     private void writeObject(ObjectOutputStream stream) throws IOException {
260         if (key.isEngineBased()) {
261             throw new NotSerializableException("engine-based keys can not be serialized");
262         }
263 
264         stream.defaultWriteObject();
265         stream.writeObject(getEncoded());
266     }
267 }
268