Home | History | Annotate | Download | only in conscrypt
      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