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.spec.InvalidKeySpecException; 24 import java.security.spec.PKCS8EncodedKeySpec; 25 import java.security.spec.X509EncodedKeySpec; 26 import javax.crypto.SecretKey; 27 28 public class OpenSSLKey { 29 private final long ctx; 30 31 private final OpenSSLEngine engine; 32 33 private final String alias; 34 35 public OpenSSLKey(long ctx) { 36 this.ctx = ctx; 37 engine = null; 38 alias = null; 39 } 40 41 public OpenSSLKey(long ctx, OpenSSLEngine engine, String alias) { 42 this.ctx = ctx; 43 this.engine = engine; 44 this.alias = alias; 45 } 46 47 /** 48 * Returns the raw pointer to the EVP_PKEY context for use in JNI calls. The 49 * life cycle of this native pointer is managed by the {@code OpenSSLKey} 50 * instance and must not be destroyed or freed by users of this API. 51 */ 52 public long getPkeyContext() { 53 return ctx; 54 } 55 56 OpenSSLEngine getEngine() { 57 return engine; 58 } 59 60 boolean isEngineBased() { 61 return engine != null; 62 } 63 64 public String getAlias() { 65 return alias; 66 } 67 68 public static OpenSSLKey fromPrivateKey(PrivateKey key) throws InvalidKeyException { 69 if (key instanceof OpenSSLKeyHolder) { 70 return ((OpenSSLKeyHolder) key).getOpenSSLKey(); 71 } 72 73 if ("PKCS#8".equals(key.getFormat())) { 74 return new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(key.getEncoded())); 75 } else { 76 throw new InvalidKeyException("Unknown key format " + key.getFormat()); 77 } 78 } 79 80 public PublicKey getPublicKey() throws NoSuchAlgorithmException { 81 switch (NativeCrypto.EVP_PKEY_type(ctx)) { 82 case NativeCrypto.EVP_PKEY_RSA: 83 return new OpenSSLRSAPublicKey(this); 84 case NativeCrypto.EVP_PKEY_DSA: 85 return new OpenSSLDSAPublicKey(this); 86 case NativeCrypto.EVP_PKEY_EC: 87 return new OpenSSLECPublicKey(this); 88 default: 89 throw new NoSuchAlgorithmException("unknown PKEY type"); 90 } 91 } 92 93 static PublicKey getPublicKey(X509EncodedKeySpec keySpec, int type) 94 throws InvalidKeySpecException { 95 X509EncodedKeySpec x509KeySpec = (X509EncodedKeySpec) keySpec; 96 97 final OpenSSLKey key; 98 try { 99 key = new OpenSSLKey(NativeCrypto.d2i_PUBKEY(x509KeySpec.getEncoded())); 100 } catch (Exception e) { 101 throw new InvalidKeySpecException(e); 102 } 103 104 if (NativeCrypto.EVP_PKEY_type(key.getPkeyContext()) != type) { 105 throw new InvalidKeySpecException("Unexpected key type"); 106 } 107 108 try { 109 return key.getPublicKey(); 110 } catch (NoSuchAlgorithmException e) { 111 throw new InvalidKeySpecException(e); 112 } 113 } 114 115 public PrivateKey getPrivateKey() throws NoSuchAlgorithmException { 116 switch (NativeCrypto.EVP_PKEY_type(ctx)) { 117 case NativeCrypto.EVP_PKEY_RSA: 118 return new OpenSSLRSAPrivateKey(this); 119 case NativeCrypto.EVP_PKEY_DSA: 120 return new OpenSSLDSAPrivateKey(this); 121 case NativeCrypto.EVP_PKEY_EC: 122 return new OpenSSLECPrivateKey(this); 123 default: 124 throw new NoSuchAlgorithmException("unknown PKEY type"); 125 } 126 } 127 128 static PrivateKey getPrivateKey(PKCS8EncodedKeySpec keySpec, int type) 129 throws InvalidKeySpecException { 130 PKCS8EncodedKeySpec pkcs8KeySpec = (PKCS8EncodedKeySpec) keySpec; 131 132 final OpenSSLKey key; 133 try { 134 key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(pkcs8KeySpec.getEncoded())); 135 } catch (Exception e) { 136 throw new InvalidKeySpecException(e); 137 } 138 139 if (NativeCrypto.EVP_PKEY_type(key.getPkeyContext()) != type) { 140 throw new InvalidKeySpecException("Unexpected key type"); 141 } 142 143 try { 144 return key.getPrivateKey(); 145 } catch (NoSuchAlgorithmException e) { 146 throw new InvalidKeySpecException(e); 147 } 148 } 149 150 public SecretKey getSecretKey(String algorithm) throws NoSuchAlgorithmException { 151 switch (NativeCrypto.EVP_PKEY_type(ctx)) { 152 case NativeCrypto.EVP_PKEY_HMAC: 153 case NativeCrypto.EVP_PKEY_CMAC: 154 return new OpenSSLSecretKey(algorithm, this); 155 default: 156 throw new NoSuchAlgorithmException("unknown PKEY type"); 157 } 158 } 159 160 @Override 161 protected void finalize() throws Throwable { 162 try { 163 if (ctx != 0) { 164 NativeCrypto.EVP_PKEY_free(ctx); 165 } 166 } finally { 167 super.finalize(); 168 } 169 } 170 171 @Override 172 public boolean equals(Object o) { 173 if (o == this) { 174 return true; 175 } 176 177 if (!(o instanceof OpenSSLKey)) { 178 return false; 179 } 180 181 OpenSSLKey other = (OpenSSLKey) o; 182 if (ctx == other.getPkeyContext()) { 183 return true; 184 } 185 186 /* 187 * ENGINE-based keys must be checked in a special way. 188 */ 189 if (engine == null) { 190 if (other.getEngine() != null) { 191 return false; 192 } 193 } else if (!engine.equals(other.getEngine())) { 194 return false; 195 } else { 196 if (alias != null) { 197 return alias.equals(other.getAlias()); 198 } else if (other.getAlias() != null) { 199 return false; 200 } 201 } 202 203 return NativeCrypto.EVP_PKEY_cmp(ctx, other.getPkeyContext()) == 1; 204 } 205 206 @Override 207 public int hashCode() { 208 int hash = 1; 209 hash = hash * 17 + (int) ctx; 210 hash = hash * 31 + (int) (engine == null ? 0 : engine.getEngineContext()); 211 return hash; 212 } 213 } 214