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