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