• 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.security.InvalidKeyException;
20 import java.security.NoSuchAlgorithmException;
21 import java.security.PrivateKey;
22 import java.security.PublicKey;
23 import java.security.interfaces.DSAPrivateKey;
24 import java.security.interfaces.ECPrivateKey;
25 import java.security.interfaces.RSAPrivateKey;
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 long ctx;
33 
34     private final OpenSSLEngine engine;
35 
36     private final String alias;
37 
OpenSSLKey(long ctx)38     public OpenSSLKey(long ctx) {
39         this.ctx = ctx;
40         engine = null;
41         alias = null;
42     }
43 
OpenSSLKey(long ctx, OpenSSLEngine engine, String alias)44     public OpenSSLKey(long ctx, OpenSSLEngine engine, String alias) {
45         this.ctx = ctx;
46         this.engine = engine;
47         this.alias = alias;
48     }
49 
50     /**
51      * Returns the raw pointer to the EVP_PKEY context for use in JNI calls. The
52      * life cycle of this native pointer is managed by the {@code OpenSSLKey}
53      * instance and must not be destroyed or freed by users of this API.
54      */
getPkeyContext()55     public long getPkeyContext() {
56         return ctx;
57     }
58 
getEngine()59     OpenSSLEngine getEngine() {
60         return engine;
61     }
62 
isEngineBased()63     boolean isEngineBased() {
64         return engine != null;
65     }
66 
getAlias()67     public String getAlias() {
68         return alias;
69     }
70 
fromPrivateKey(PrivateKey key)71     public static OpenSSLKey fromPrivateKey(PrivateKey key) throws InvalidKeyException {
72         if (key instanceof OpenSSLKeyHolder) {
73             return ((OpenSSLKeyHolder) key).getOpenSSLKey();
74         }
75 
76         final String keyFormat = key.getFormat();
77         if (keyFormat == null) {
78             return wrapPrivateKey(key);
79         } else if (!"PKCS#8".equals(key.getFormat())) {
80             throw new InvalidKeyException("Unknown key format " + keyFormat);
81         }
82 
83         final byte[] encoded = key.getEncoded();
84         if (encoded == null) {
85             throw new InvalidKeyException("Key encoding is null");
86         }
87 
88         return new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(key.getEncoded()));
89     }
90 
wrapPrivateKey(PrivateKey key)91     private static OpenSSLKey wrapPrivateKey(PrivateKey key) throws InvalidKeyException {
92         if (key instanceof RSAPrivateKey) {
93             return OpenSSLRSAPrivateKey.wrapPlatformKey((RSAPrivateKey) key);
94         } else if (key instanceof DSAPrivateKey) {
95             return OpenSSLDSAPrivateKey.wrapPlatformKey((DSAPrivateKey) key);
96         } else if (key instanceof ECPrivateKey) {
97             return OpenSSLECPrivateKey.wrapPlatformKey((ECPrivateKey) key);
98         } else {
99             throw new InvalidKeyException("Unknown key type: " + key.toString());
100         }
101     }
102 
fromPublicKey(PublicKey key)103     public static OpenSSLKey fromPublicKey(PublicKey key) throws InvalidKeyException {
104         if (key instanceof OpenSSLKeyHolder) {
105             return ((OpenSSLKeyHolder) key).getOpenSSLKey();
106         }
107 
108         if (!"X.509".equals(key.getFormat())) {
109             throw new InvalidKeyException("Unknown key format " + key.getFormat());
110         }
111 
112         final byte[] encoded = key.getEncoded();
113         if (encoded == null) {
114             throw new InvalidKeyException("Key encoding is null");
115         }
116 
117         return new OpenSSLKey(NativeCrypto.d2i_PUBKEY(key.getEncoded()));
118     }
119 
getPublicKey()120     public PublicKey getPublicKey() throws NoSuchAlgorithmException {
121         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
122             case NativeCrypto.EVP_PKEY_RSA:
123                 return new OpenSSLRSAPublicKey(this);
124             case NativeCrypto.EVP_PKEY_DH:
125                 return new OpenSSLDHPublicKey(this);
126             case NativeCrypto.EVP_PKEY_DSA:
127                 return new OpenSSLDSAPublicKey(this);
128             case NativeCrypto.EVP_PKEY_EC:
129                 return new OpenSSLECPublicKey(this);
130             default:
131                 throw new NoSuchAlgorithmException("unknown PKEY type");
132         }
133     }
134 
getPublicKey(X509EncodedKeySpec keySpec, int type)135     static PublicKey getPublicKey(X509EncodedKeySpec keySpec, int type)
136             throws InvalidKeySpecException {
137         X509EncodedKeySpec x509KeySpec = keySpec;
138 
139         final OpenSSLKey key;
140         try {
141             key = new OpenSSLKey(NativeCrypto.d2i_PUBKEY(x509KeySpec.getEncoded()));
142         } catch (Exception e) {
143             throw new InvalidKeySpecException(e);
144         }
145 
146         if (NativeCrypto.EVP_PKEY_type(key.getPkeyContext()) != type) {
147             throw new InvalidKeySpecException("Unexpected key type");
148         }
149 
150         try {
151             return key.getPublicKey();
152         } catch (NoSuchAlgorithmException e) {
153             throw new InvalidKeySpecException(e);
154         }
155     }
156 
getPrivateKey()157     public PrivateKey getPrivateKey() throws NoSuchAlgorithmException {
158         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
159             case NativeCrypto.EVP_PKEY_RSA:
160                 return new OpenSSLRSAPrivateKey(this);
161             case NativeCrypto.EVP_PKEY_DH:
162                 return new OpenSSLDHPrivateKey(this);
163             case NativeCrypto.EVP_PKEY_DSA:
164                 return new OpenSSLDSAPrivateKey(this);
165             case NativeCrypto.EVP_PKEY_EC:
166                 return new OpenSSLECPrivateKey(this);
167             default:
168                 throw new NoSuchAlgorithmException("unknown PKEY type");
169         }
170     }
171 
getPrivateKey(PKCS8EncodedKeySpec keySpec, int type)172     static PrivateKey getPrivateKey(PKCS8EncodedKeySpec keySpec, int type)
173             throws InvalidKeySpecException {
174         PKCS8EncodedKeySpec pkcs8KeySpec = keySpec;
175 
176         final OpenSSLKey key;
177         try {
178             key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(pkcs8KeySpec.getEncoded()));
179         } catch (Exception e) {
180             throw new InvalidKeySpecException(e);
181         }
182 
183         if (NativeCrypto.EVP_PKEY_type(key.getPkeyContext()) != type) {
184             throw new InvalidKeySpecException("Unexpected key type");
185         }
186 
187         try {
188             return key.getPrivateKey();
189         } catch (NoSuchAlgorithmException e) {
190             throw new InvalidKeySpecException(e);
191         }
192     }
193 
getSecretKey(String algorithm)194     public SecretKey getSecretKey(String algorithm) throws NoSuchAlgorithmException {
195         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
196             case NativeCrypto.EVP_PKEY_HMAC:
197             case NativeCrypto.EVP_PKEY_CMAC:
198                 return new OpenSSLSecretKey(algorithm, this);
199             default:
200                 throw new NoSuchAlgorithmException("unknown PKEY type");
201         }
202     }
203 
204     @Override
finalize()205     protected void finalize() throws Throwable {
206         try {
207             if (ctx != 0) {
208                 NativeCrypto.EVP_PKEY_free(ctx);
209             }
210         } finally {
211             super.finalize();
212         }
213     }
214 
215     @Override
equals(Object o)216     public boolean equals(Object o) {
217         if (o == this) {
218             return true;
219         }
220 
221         if (!(o instanceof OpenSSLKey)) {
222             return false;
223         }
224 
225         OpenSSLKey other = (OpenSSLKey) o;
226         if (ctx == other.getPkeyContext()) {
227             return true;
228         }
229 
230         /*
231          * ENGINE-based keys must be checked in a special way.
232          */
233         if (engine == null) {
234             if (other.getEngine() != null) {
235                 return false;
236             }
237         } else if (!engine.equals(other.getEngine())) {
238             return false;
239         } else {
240             if (alias != null) {
241                 return alias.equals(other.getAlias());
242             } else if (other.getAlias() != null) {
243                 return false;
244             }
245         }
246 
247         return NativeCrypto.EVP_PKEY_cmp(ctx, other.getPkeyContext()) == 1;
248     }
249 
250     @Override
hashCode()251     public int hashCode() {
252         int hash = 1;
253         hash = hash * 17 + (int) ctx;
254         hash = hash * 31 + (int) (engine == null ? 0 : engine.getEngineContext());
255         return hash;
256     }
257 }
258