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