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.security.InvalidKeyException;
20 import java.security.NoSuchAlgorithmException;
21 import java.security.PrivateKey;
22 import java.security.PublicKey;
23 import java.security.interfaces.ECPrivateKey;
24 import java.security.interfaces.RSAPrivateKey;
25 import java.security.spec.ECParameterSpec;
26 import java.security.spec.InvalidKeySpecException;
27 import java.security.spec.PKCS8EncodedKeySpec;
28 import java.security.spec.X509EncodedKeySpec;
29 import javax.crypto.SecretKey;
30 
31 public class OpenSSLKey {
32     private final NativeRef.EVP_PKEY ctx;
33 
34     private final OpenSSLEngine engine;
35 
36     private final String alias;
37 
38     private final boolean wrapped;
39 
OpenSSLKey(long ctx)40     public OpenSSLKey(long ctx) {
41         this(ctx, false);
42     }
43 
OpenSSLKey(long ctx, boolean wrapped)44     public OpenSSLKey(long ctx, boolean wrapped) {
45         this.ctx = new NativeRef.EVP_PKEY(ctx);
46         engine = null;
47         alias = null;
48         this.wrapped = wrapped;
49     }
50 
OpenSSLKey(long ctx, OpenSSLEngine engine, String alias)51     public OpenSSLKey(long ctx, OpenSSLEngine engine, String alias) {
52         this.ctx = new NativeRef.EVP_PKEY(ctx);
53         this.engine = engine;
54         this.alias = alias;
55         this.wrapped = false;
56     }
57 
58     /**
59      * Returns the EVP_PKEY context for use in JNI calls.
60      */
getNativeRef()61     public NativeRef.EVP_PKEY getNativeRef() {
62         return ctx;
63     }
64 
getEngine()65     OpenSSLEngine getEngine() {
66         return engine;
67     }
68 
isEngineBased()69     boolean isEngineBased() {
70         return engine != null;
71     }
72 
getAlias()73     public String getAlias() {
74         return alias;
75     }
76 
isWrapped()77     public boolean isWrapped() {
78         return wrapped;
79     }
80 
fromPrivateKey(PrivateKey key)81     public static OpenSSLKey fromPrivateKey(PrivateKey key) throws InvalidKeyException {
82         if (key instanceof OpenSSLKeyHolder) {
83             return ((OpenSSLKeyHolder) key).getOpenSSLKey();
84         }
85 
86         final String keyFormat = key.getFormat();
87         if (keyFormat == null) {
88             return wrapPrivateKey(key);
89         } else if (!"PKCS#8".equals(key.getFormat())) {
90             throw new InvalidKeyException("Unknown key format " + keyFormat);
91         }
92 
93         final byte[] encoded = key.getEncoded();
94         if (encoded == null) {
95             throw new InvalidKeyException("Key encoding is null");
96         }
97 
98         return new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(key.getEncoded()));
99     }
100 
101     /**
102      * Gets an {@code OpenSSLKey} instance backed by the provided private key. The resulting key is
103      * usable only by this provider's TLS/SSL stack.
104      *
105      * @param privateKey private key.
106      * @param publicKey corresponding public key or {@code null} if not available. Some opaque
107      *        private keys cannot be used by the TLS/SSL stack without the public key.
108      */
fromPrivateKeyForTLSStackOnly( PrivateKey privateKey, PublicKey publicKey)109     public static OpenSSLKey fromPrivateKeyForTLSStackOnly(
110             PrivateKey privateKey, PublicKey publicKey) throws InvalidKeyException {
111         OpenSSLKey result = getOpenSSLKey(privateKey);
112         if (result != null) {
113             return result;
114         }
115 
116         result = fromKeyMaterial(privateKey);
117         if (result != null) {
118             return result;
119         }
120 
121         return wrapJCAPrivateKeyForTLSStackOnly(privateKey, publicKey);
122     }
123 
124     /**
125      * Gets an {@code OpenSSLKey} instance backed by the provided EC private key. The resulting key
126      * is usable only by this provider's TLS/SSL stack.
127      *
128      * @param key private key.
129      * @param ecParams EC parameters {@code null} if not available. Some opaque private keys cannot
130      *        be used by the TLS/SSL stack without the parameters because the private key itself
131      *        might not expose the parameters.
132      */
fromECPrivateKeyForTLSStackOnly( PrivateKey key, ECParameterSpec ecParams)133     public static OpenSSLKey fromECPrivateKeyForTLSStackOnly(
134             PrivateKey key, ECParameterSpec ecParams) throws InvalidKeyException {
135         OpenSSLKey result = getOpenSSLKey(key);
136         if (result != null) {
137             return result;
138         }
139 
140         result = fromKeyMaterial(key);
141         if (result != null) {
142             return result;
143         }
144 
145         return OpenSSLECPrivateKey.wrapJCAPrivateKeyForTLSStackOnly(key, ecParams);
146     }
147 
148     /**
149      * Gets the {@code OpenSSLKey} instance of the provided key.
150      *
151      * @return instance or {@code null} if the {@code key} is not backed by OpenSSL's
152      *         {@code EVP_PKEY}.
153      */
getOpenSSLKey(PrivateKey key)154     private static OpenSSLKey getOpenSSLKey(PrivateKey key) {
155         if (key instanceof OpenSSLKeyHolder) {
156             return ((OpenSSLKeyHolder) key).getOpenSSLKey();
157         }
158 
159         if ("RSA".equals(key.getAlgorithm())) {
160             return Platform.wrapRsaKey(key);
161         }
162 
163         return null;
164     }
165 
166     /**
167      * Gets an {@code OpenSSLKey} instance initialized with the key material of the provided key.
168      *
169      * @return instance or {@code null} if the {@code key} does not export its key material in a
170      *         suitable format.
171      */
fromKeyMaterial(PrivateKey key)172     private static OpenSSLKey fromKeyMaterial(PrivateKey key) {
173         if (!"PKCS#8".equals(key.getFormat())) {
174             return null;
175         }
176         byte[] encoded = key.getEncoded();
177         if (encoded == null) {
178             return null;
179         }
180         return new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(encoded));
181     }
182 
183     /**
184      * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations
185      * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the
186      * provider which accepts the key.
187      */
wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey, PublicKey publicKey)188     private static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey,
189             PublicKey publicKey) throws InvalidKeyException {
190         String keyAlgorithm = privateKey.getAlgorithm();
191         if ("RSA".equals(keyAlgorithm)) {
192             return OpenSSLRSAPrivateKey.wrapJCAPrivateKeyForTLSStackOnly(privateKey, publicKey);
193         } else if ("EC".equals(keyAlgorithm)) {
194             return OpenSSLECPrivateKey.wrapJCAPrivateKeyForTLSStackOnly(privateKey, publicKey);
195         } else {
196             throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm);
197         }
198     }
199 
wrapPrivateKey(PrivateKey key)200     private static OpenSSLKey wrapPrivateKey(PrivateKey key) throws InvalidKeyException {
201         if (key instanceof RSAPrivateKey) {
202             return OpenSSLRSAPrivateKey.wrapPlatformKey((RSAPrivateKey) key);
203         } else if (key instanceof ECPrivateKey) {
204             return OpenSSLECPrivateKey.wrapPlatformKey((ECPrivateKey) key);
205         } else {
206             throw new InvalidKeyException("Unknown key type: " + key.toString());
207         }
208     }
209 
fromPublicKey(PublicKey key)210     public static OpenSSLKey fromPublicKey(PublicKey key) throws InvalidKeyException {
211         if (key instanceof OpenSSLKeyHolder) {
212             return ((OpenSSLKeyHolder) key).getOpenSSLKey();
213         }
214 
215         if (!"X.509".equals(key.getFormat())) {
216             throw new InvalidKeyException("Unknown key format " + key.getFormat());
217         }
218 
219         final byte[] encoded = key.getEncoded();
220         if (encoded == null) {
221             throw new InvalidKeyException("Key encoding is null");
222         }
223 
224         try {
225             return new OpenSSLKey(NativeCrypto.d2i_PUBKEY(key.getEncoded()));
226         } catch (Exception e) {
227             throw new InvalidKeyException(e);
228         }
229     }
230 
getPublicKey()231     public PublicKey getPublicKey() throws NoSuchAlgorithmException {
232         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
233             case NativeConstants.EVP_PKEY_RSA:
234                 return new OpenSSLRSAPublicKey(this);
235             case NativeConstants.EVP_PKEY_EC:
236                 return new OpenSSLECPublicKey(this);
237             default:
238                 throw new NoSuchAlgorithmException("unknown PKEY type");
239         }
240     }
241 
getPublicKey(X509EncodedKeySpec keySpec, int type)242     static PublicKey getPublicKey(X509EncodedKeySpec keySpec, int type)
243             throws InvalidKeySpecException {
244         X509EncodedKeySpec x509KeySpec = keySpec;
245 
246         final OpenSSLKey key;
247         try {
248             key = new OpenSSLKey(NativeCrypto.d2i_PUBKEY(x509KeySpec.getEncoded()));
249         } catch (Exception e) {
250             throw new InvalidKeySpecException(e);
251         }
252 
253         if (NativeCrypto.EVP_PKEY_type(key.getNativeRef()) != type) {
254             throw new InvalidKeySpecException("Unexpected key type");
255         }
256 
257         try {
258             return key.getPublicKey();
259         } catch (NoSuchAlgorithmException e) {
260             throw new InvalidKeySpecException(e);
261         }
262     }
263 
getPrivateKey()264     public PrivateKey getPrivateKey() throws NoSuchAlgorithmException {
265         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
266             case NativeConstants.EVP_PKEY_RSA:
267                 return new OpenSSLRSAPrivateKey(this);
268             case NativeConstants.EVP_PKEY_EC:
269                 return new OpenSSLECPrivateKey(this);
270             default:
271                 throw new NoSuchAlgorithmException("unknown PKEY type");
272         }
273     }
274 
getPrivateKey(PKCS8EncodedKeySpec keySpec, int type)275     static PrivateKey getPrivateKey(PKCS8EncodedKeySpec keySpec, int type)
276             throws InvalidKeySpecException {
277         PKCS8EncodedKeySpec pkcs8KeySpec = keySpec;
278 
279         final OpenSSLKey key;
280         try {
281             key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(pkcs8KeySpec.getEncoded()));
282         } catch (Exception e) {
283             throw new InvalidKeySpecException(e);
284         }
285 
286         if (NativeCrypto.EVP_PKEY_type(key.getNativeRef()) != type) {
287             throw new InvalidKeySpecException("Unexpected key type");
288         }
289 
290         try {
291             return key.getPrivateKey();
292         } catch (NoSuchAlgorithmException e) {
293             throw new InvalidKeySpecException(e);
294         }
295     }
296 
getSecretKey(String algorithm)297     public SecretKey getSecretKey(String algorithm) throws NoSuchAlgorithmException {
298         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
299             case NativeConstants.EVP_PKEY_HMAC:
300                 return new OpenSSLSecretKey(algorithm, this);
301             default:
302                 throw new NoSuchAlgorithmException("unknown PKEY type");
303         }
304     }
305 
306     @Override
equals(Object o)307     public boolean equals(Object o) {
308         if (o == this) {
309             return true;
310         }
311 
312         if (!(o instanceof OpenSSLKey)) {
313             return false;
314         }
315 
316         OpenSSLKey other = (OpenSSLKey) o;
317         if (ctx.equals(other.getNativeRef())) {
318             return true;
319         }
320 
321         /*
322          * ENGINE-based keys must be checked in a special way.
323          */
324         if (engine == null) {
325             if (other.getEngine() != null) {
326                 return false;
327             }
328         } else if (!engine.equals(other.getEngine())) {
329             return false;
330         } else {
331             if (alias != null) {
332                 return alias.equals(other.getAlias());
333             } else if (other.getAlias() != null) {
334                 return false;
335             }
336         }
337 
338         return NativeCrypto.EVP_PKEY_cmp(ctx, other.getNativeRef()) == 1;
339     }
340 
341     @Override
hashCode()342     public int hashCode() {
343         int hash = 1;
344         hash = hash * 17 + ctx.hashCode();
345         hash = hash * 31 + (int) (engine == null ? 0 : engine.getEngineContext());
346         return hash;
347     }
348 }
349