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