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