• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.InputStream;
20 import java.security.InvalidKeyException;
21 import java.security.NoSuchAlgorithmException;
22 import java.security.PrivateKey;
23 import java.security.PublicKey;
24 import java.security.interfaces.ECPrivateKey;
25 import java.security.interfaces.RSAPrivateKey;
26 import java.security.spec.ECParameterSpec;
27 import java.security.spec.InvalidKeySpecException;
28 import java.security.spec.PKCS8EncodedKeySpec;
29 import java.security.spec.X509EncodedKeySpec;
30 
31 /**
32  * Represents a BoringSSL {@code EVP_PKEY}.
33  */
34 final class OpenSSLKey {
35     private final NativeRef.EVP_PKEY ctx;
36 
37     private final boolean wrapped;
38 
OpenSSLKey(long ctx)39     OpenSSLKey(long ctx) {
40         this(ctx, false);
41     }
42 
OpenSSLKey(long ctx, boolean wrapped)43     OpenSSLKey(long ctx, boolean wrapped) {
44         this.ctx = new NativeRef.EVP_PKEY(ctx);
45         this.wrapped = wrapped;
46     }
47 
48     /**
49      * Returns the EVP_PKEY context for use in JNI calls.
50      */
getNativeRef()51     NativeRef.EVP_PKEY getNativeRef() {
52         return ctx;
53     }
54 
isWrapped()55     boolean isWrapped() {
56         return wrapped;
57     }
58 
fromPrivateKey(PrivateKey key)59     static OpenSSLKey fromPrivateKey(PrivateKey key) throws InvalidKeyException {
60         if (key instanceof OpenSSLKeyHolder) {
61             return ((OpenSSLKeyHolder) key).getOpenSSLKey();
62         }
63 
64         final String keyFormat = key.getFormat();
65         if (keyFormat == null) {
66             return wrapPrivateKey(key);
67         } else if (!"PKCS#8".equals(key.getFormat())) {
68             throw new InvalidKeyException("Unknown key format " + keyFormat);
69         }
70 
71         final byte[] encoded = key.getEncoded();
72         if (encoded == null) {
73             throw new InvalidKeyException("Key encoding is null");
74         }
75 
76         return new OpenSSLKey(NativeCrypto.EVP_parse_private_key(key.getEncoded()));
77     }
78 
79     /**
80      * Parse a private key in PEM encoding from the provided input stream.
81      *
82      * @throws InvalidKeyException if parsing fails
83      */
fromPrivateKeyPemInputStream(InputStream is)84     static OpenSSLKey fromPrivateKeyPemInputStream(InputStream is)
85             throws InvalidKeyException {
86         OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
87         try {
88             long keyCtx = NativeCrypto.PEM_read_bio_PrivateKey(bis.getBioContext());
89             if (keyCtx == 0L) {
90                 return null;
91             }
92 
93             return new OpenSSLKey(keyCtx);
94         } catch (Exception e) {
95             throw new InvalidKeyException(e);
96         } finally {
97             bis.release();
98         }
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     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     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.EVP_parse_private_key(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     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.EVP_parse_public_key(key.getEncoded()));
226         } catch (Exception e) {
227             throw new InvalidKeyException(e);
228         }
229     }
230 
231     /**
232      * Parse a public key in PEM encoding from the provided input stream.
233      *
234      * @throws InvalidKeyException if parsing fails
235      */
fromPublicKeyPemInputStream(InputStream is)236     static OpenSSLKey fromPublicKeyPemInputStream(InputStream is)
237             throws InvalidKeyException {
238         OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
239         try {
240             long keyCtx = NativeCrypto.PEM_read_bio_PUBKEY(bis.getBioContext());
241             if (keyCtx == 0L) {
242                 return null;
243             }
244 
245             return new OpenSSLKey(keyCtx);
246         } catch (Exception e) {
247             throw new InvalidKeyException(e);
248         } finally {
249             bis.release();
250         }
251     }
252 
getPublicKey()253     PublicKey getPublicKey() throws NoSuchAlgorithmException {
254         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
255             case NativeConstants.EVP_PKEY_RSA:
256                 return new OpenSSLRSAPublicKey(this);
257             case NativeConstants.EVP_PKEY_EC:
258                 return new OpenSSLECPublicKey(this);
259             default:
260                 throw new NoSuchAlgorithmException("unknown PKEY type");
261         }
262     }
263 
getPublicKey(X509EncodedKeySpec keySpec, int type)264     static PublicKey getPublicKey(X509EncodedKeySpec keySpec, int type)
265             throws InvalidKeySpecException {
266         X509EncodedKeySpec x509KeySpec = keySpec;
267 
268         final OpenSSLKey key;
269         try {
270             key = new OpenSSLKey(NativeCrypto.EVP_parse_public_key(x509KeySpec.getEncoded()));
271         } catch (Exception e) {
272             throw new InvalidKeySpecException(e);
273         }
274 
275         if (NativeCrypto.EVP_PKEY_type(key.getNativeRef()) != type) {
276             throw new InvalidKeySpecException("Unexpected key type");
277         }
278 
279         try {
280             return key.getPublicKey();
281         } catch (NoSuchAlgorithmException e) {
282             throw new InvalidKeySpecException(e);
283         }
284     }
285 
getPrivateKey()286     PrivateKey getPrivateKey() throws NoSuchAlgorithmException {
287         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
288             case NativeConstants.EVP_PKEY_RSA:
289                 return new OpenSSLRSAPrivateKey(this);
290             case NativeConstants.EVP_PKEY_EC:
291                 return new OpenSSLECPrivateKey(this);
292             default:
293                 throw new NoSuchAlgorithmException("unknown PKEY type");
294         }
295     }
296 
getPrivateKey(PKCS8EncodedKeySpec keySpec, int type)297     static PrivateKey getPrivateKey(PKCS8EncodedKeySpec keySpec, int type)
298             throws InvalidKeySpecException {
299         PKCS8EncodedKeySpec pkcs8KeySpec = keySpec;
300 
301         final OpenSSLKey key;
302         try {
303             key = new OpenSSLKey(NativeCrypto.EVP_parse_private_key(pkcs8KeySpec.getEncoded()));
304         } catch (Exception e) {
305             throw new InvalidKeySpecException(e);
306         }
307 
308         if (NativeCrypto.EVP_PKEY_type(key.getNativeRef()) != type) {
309             throw new InvalidKeySpecException("Unexpected key type");
310         }
311 
312         try {
313             return key.getPrivateKey();
314         } catch (NoSuchAlgorithmException e) {
315             throw new InvalidKeySpecException(e);
316         }
317     }
318 
319     @Override
equals(Object o)320     public boolean equals(Object o) {
321         if (o == this) {
322             return true;
323         }
324 
325         if (!(o instanceof OpenSSLKey)) {
326             return false;
327         }
328 
329         OpenSSLKey other = (OpenSSLKey) o;
330         if (ctx.equals(other.getNativeRef())) {
331             return true;
332         }
333 
334         return NativeCrypto.EVP_PKEY_cmp(ctx, other.getNativeRef()) == 1;
335     }
336 
337     @Override
hashCode()338     public int hashCode() {
339         return ctx.hashCode();
340     }
341 }
342