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.apache.harmony.xnet.provider.jsse; 18 19 import java.security.NoSuchAlgorithmException; 20 import java.security.PrivateKey; 21 import java.security.PublicKey; 22 import java.security.spec.InvalidKeySpecException; 23 import java.security.spec.PKCS8EncodedKeySpec; 24 import java.security.spec.X509EncodedKeySpec; 25 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 PublicKey getPublicKey() throws NoSuchAlgorithmException { 69 switch (NativeCrypto.EVP_PKEY_type(ctx)) { 70 case NativeCrypto.EVP_PKEY_RSA: 71 return new OpenSSLRSAPublicKey(this); 72 case NativeCrypto.EVP_PKEY_DSA: 73 return new OpenSSLDSAPublicKey(this); 74 case NativeCrypto.EVP_PKEY_EC: 75 return new OpenSSLECPublicKey(this); 76 default: 77 throw new NoSuchAlgorithmException("unknown PKEY type"); 78 } 79 } 80 81 static PublicKey getPublicKey(X509EncodedKeySpec keySpec, int type) 82 throws InvalidKeySpecException { 83 X509EncodedKeySpec x509KeySpec = (X509EncodedKeySpec) keySpec; 84 85 final OpenSSLKey key; 86 try { 87 key = new OpenSSLKey(NativeCrypto.d2i_PUBKEY(x509KeySpec.getEncoded())); 88 } catch (Exception e) { 89 throw new InvalidKeySpecException(e); 90 } 91 92 if (NativeCrypto.EVP_PKEY_type(key.getPkeyContext()) != type) { 93 throw new InvalidKeySpecException("Unexpected key type"); 94 } 95 96 try { 97 return key.getPublicKey(); 98 } catch (NoSuchAlgorithmException e) { 99 throw new InvalidKeySpecException(e); 100 } 101 } 102 103 public PrivateKey getPrivateKey() throws NoSuchAlgorithmException { 104 switch (NativeCrypto.EVP_PKEY_type(ctx)) { 105 case NativeCrypto.EVP_PKEY_RSA: 106 return new OpenSSLRSAPrivateKey(this); 107 case NativeCrypto.EVP_PKEY_DSA: 108 return new OpenSSLDSAPrivateKey(this); 109 case NativeCrypto.EVP_PKEY_EC: 110 return new OpenSSLECPrivateKey(this); 111 default: 112 throw new NoSuchAlgorithmException("unknown PKEY type"); 113 } 114 } 115 116 static PrivateKey getPrivateKey(PKCS8EncodedKeySpec keySpec, int type) 117 throws InvalidKeySpecException { 118 PKCS8EncodedKeySpec pkcs8KeySpec = (PKCS8EncodedKeySpec) keySpec; 119 120 final OpenSSLKey key; 121 try { 122 key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(pkcs8KeySpec.getEncoded())); 123 } catch (Exception e) { 124 throw new InvalidKeySpecException(e); 125 } 126 127 if (NativeCrypto.EVP_PKEY_type(key.getPkeyContext()) != type) { 128 throw new InvalidKeySpecException("Unexpected key type"); 129 } 130 131 try { 132 return key.getPrivateKey(); 133 } catch (NoSuchAlgorithmException e) { 134 throw new InvalidKeySpecException(e); 135 } 136 } 137 138 public SecretKey getSecretKey(String algorithm) throws NoSuchAlgorithmException { 139 switch (NativeCrypto.EVP_PKEY_type(ctx)) { 140 case NativeCrypto.EVP_PKEY_HMAC: 141 case NativeCrypto.EVP_PKEY_CMAC: 142 return new OpenSSLSecretKey(algorithm, this); 143 default: 144 throw new NoSuchAlgorithmException("unknown PKEY type"); 145 } 146 } 147 148 @Override 149 protected void finalize() throws Throwable { 150 try { 151 if (ctx != 0) { 152 NativeCrypto.EVP_PKEY_free(ctx); 153 } 154 } finally { 155 super.finalize(); 156 } 157 } 158 159 @Override 160 public boolean equals(Object o) { 161 if (o == this) { 162 return true; 163 } 164 165 if (!(o instanceof OpenSSLKey)) { 166 return false; 167 } 168 169 OpenSSLKey other = (OpenSSLKey) o; 170 if (ctx == other.getPkeyContext()) { 171 return true; 172 } 173 174 /* 175 * ENGINE-based keys must be checked in a special way. 176 */ 177 if (engine == null) { 178 if (other.getEngine() != null) { 179 return false; 180 } 181 } else if (!engine.equals(other.getEngine())) { 182 return false; 183 } else { 184 if (alias != null) { 185 return alias.equals(other.getAlias()); 186 } else if (other.getAlias() != null) { 187 return false; 188 } 189 } 190 191 return NativeCrypto.EVP_PKEY_cmp(ctx, other.getPkeyContext()) == 1; 192 } 193 194 @Override 195 public int hashCode() { 196 int hash = 1; 197 hash = hash * 17 + (int) ctx; 198 hash = hash * 31 + (int) (engine == null ? 0 : engine.getEngineContext()); 199 return hash; 200 } 201 } 202