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.io.IOException;
     20 import java.io.NotSerializableException;
     21 import java.io.ObjectInputStream;
     22 import java.io.ObjectOutputStream;
     23 import java.math.BigInteger;
     24 import java.security.InvalidKeyException;
     25 import java.security.interfaces.RSAPrivateKey;
     26 import java.security.spec.InvalidKeySpecException;
     27 import java.security.spec.RSAPrivateKeySpec;
     28 
     29 public class OpenSSLRSAPrivateKey implements RSAPrivateKey, OpenSSLKeyHolder {
     30     private static final long serialVersionUID = 4872170254439578735L;
     31 
     32     protected transient OpenSSLKey key;
     33 
     34     protected transient boolean fetchedParams;
     35 
     36     protected BigInteger modulus;
     37 
     38     protected BigInteger privateExponent;
     39 
     40     OpenSSLRSAPrivateKey(OpenSSLKey key) {
     41         this.key = key;
     42     }
     43 
     44     OpenSSLRSAPrivateKey(OpenSSLKey key, byte[][] params) {
     45         this(key);
     46         readParams(params);
     47         fetchedParams = true;
     48     }
     49 
     50     @Override
     51     public OpenSSLKey getOpenSSLKey() {
     52         return key;
     53     }
     54 
     55     public OpenSSLRSAPrivateKey(RSAPrivateKeySpec rsaKeySpec) throws InvalidKeySpecException {
     56         this(init(rsaKeySpec));
     57     }
     58 
     59     private static OpenSSLKey init(RSAPrivateKeySpec rsaKeySpec) throws InvalidKeySpecException {
     60         final BigInteger modulus = rsaKeySpec.getModulus();
     61         final BigInteger privateExponent = rsaKeySpec.getPrivateExponent();
     62 
     63         if (modulus == null) {
     64             throw new InvalidKeySpecException("modulus == null");
     65         } else if (privateExponent == null) {
     66             throw new InvalidKeySpecException("privateExponent == null");
     67         }
     68 
     69         try {
     70             return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
     71                     modulus.toByteArray(),
     72                     null,
     73                     privateExponent.toByteArray(),
     74                     null,
     75                     null,
     76                     null,
     77                     null,
     78                     null));
     79         } catch (Exception e) {
     80             throw new InvalidKeySpecException(e);
     81         }
     82     }
     83 
     84     static OpenSSLRSAPrivateKey getInstance(OpenSSLKey key) {
     85       byte[][] params = NativeCrypto.get_RSA_private_params(key.getPkeyContext());
     86       if (params[1] != null) {
     87           return new OpenSSLRSAPrivateCrtKey(key, params);
     88       }
     89       return new OpenSSLRSAPrivateKey(key, params);
     90     }
     91 
     92     protected static OpenSSLKey wrapPlatformKey(RSAPrivateKey rsaPrivateKey)
     93             throws InvalidKeyException {
     94         OpenSSLKey wrapper = Platform.wrapRsaKey(rsaPrivateKey);
     95         if (wrapper != null) {
     96             return wrapper;
     97         }
     98         return new OpenSSLKey(NativeCrypto.getRSAPrivateKeyWrapper(rsaPrivateKey, rsaPrivateKey
     99                 .getModulus().toByteArray()));
    100     }
    101 
    102     static OpenSSLKey getInstance(RSAPrivateKey rsaPrivateKey) throws InvalidKeyException {
    103         /**
    104          * If the key is not encodable (PKCS11-like key), then wrap it and use
    105          * JNI upcalls to satisfy requests.
    106          */
    107         if (rsaPrivateKey.getFormat() == null) {
    108             return wrapPlatformKey(rsaPrivateKey);
    109         }
    110 
    111         final BigInteger modulus = rsaPrivateKey.getModulus();
    112         final BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
    113 
    114         if (modulus == null) {
    115             throw new InvalidKeyException("modulus == null");
    116         } else if (privateExponent == null) {
    117             throw new InvalidKeyException("privateExponent == null");
    118         }
    119 
    120         try {
    121             return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
    122                     modulus.toByteArray(),
    123                     null,
    124                     privateExponent.toByteArray(),
    125                     null,
    126                     null,
    127                     null,
    128                     null,
    129                     null));
    130         } catch (Exception e) {
    131             throw new InvalidKeyException(e);
    132         }
    133     }
    134 
    135     synchronized final void ensureReadParams() {
    136         if (fetchedParams) {
    137             return;
    138         }
    139         readParams(NativeCrypto.get_RSA_private_params(key.getPkeyContext()));
    140         fetchedParams = true;
    141     }
    142 
    143     void readParams(byte[][] params) {
    144         if (params[0] == null) {
    145             throw new NullPointerException("modulus == null");
    146         } else if (params[2] == null && !key.isEngineBased()) {
    147             throw new NullPointerException("privateExponent == null");
    148         }
    149 
    150         modulus = new BigInteger(params[0]);
    151 
    152         // ENGINE-based keys are not guaranteed to have a private exponent.
    153         if (params[2] != null) {
    154             privateExponent = new BigInteger(params[2]);
    155         }
    156     }
    157 
    158     @Override
    159     public final BigInteger getPrivateExponent() {
    160         if (key.isEngineBased()) {
    161             throw new UnsupportedOperationException("private exponent cannot be extracted");
    162         }
    163 
    164         ensureReadParams();
    165         return privateExponent;
    166     }
    167 
    168     @Override
    169     public final BigInteger getModulus() {
    170         ensureReadParams();
    171         return modulus;
    172     }
    173 
    174     @Override
    175     public final byte[] getEncoded() {
    176         /*
    177          * If we're using an OpenSSL ENGINE, there's no guarantee we can export
    178          * the key. Returning {@code null} tells the caller that there's no
    179          * encoded format.
    180          */
    181         if (key.isEngineBased()) {
    182             return null;
    183         }
    184 
    185         return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext());
    186     }
    187 
    188     @Override
    189     public final String getFormat() {
    190         /*
    191          * If we're using an OpenSSL ENGINE, there's no guarantee we can export
    192          * the key. Returning {@code null} tells the caller that there's no
    193          * encoded format.
    194          */
    195         if (key.isEngineBased()) {
    196             return null;
    197         }
    198 
    199         return "PKCS#8";
    200     }
    201 
    202     @Override
    203     public final String getAlgorithm() {
    204         return "RSA";
    205     }
    206 
    207     @Override
    208     public boolean equals(Object o) {
    209         if (o == this) {
    210             return true;
    211         }
    212 
    213         if (o instanceof OpenSSLRSAPrivateKey) {
    214             OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o;
    215             return key.equals(other.getOpenSSLKey());
    216         }
    217 
    218         if (o instanceof RSAPrivateKey) {
    219             ensureReadParams();
    220             RSAPrivateKey other = (RSAPrivateKey) o;
    221 
    222             return modulus.equals(other.getModulus())
    223                     && privateExponent.equals(other.getPrivateExponent());
    224         }
    225 
    226         return false;
    227     }
    228 
    229     @Override
    230     public int hashCode() {
    231         ensureReadParams();
    232         int hash = 1;
    233 
    234         hash = hash * 3 + modulus.hashCode();
    235         if (privateExponent != null) {
    236             hash = hash * 7 + privateExponent.hashCode();
    237         }
    238 
    239         return hash;
    240     }
    241 
    242     @Override
    243     public String toString() {
    244         final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateKey{");
    245 
    246         final boolean engineBased = key.isEngineBased();
    247         if (engineBased) {
    248             sb.append("key=");
    249             sb.append(key);
    250             sb.append('}');
    251         }
    252 
    253         ensureReadParams();
    254         sb.append("modulus=");
    255         sb.append(modulus.toString(16));
    256         sb.append(',');
    257 
    258         if (!engineBased) {
    259             sb.append("privateExponent=");
    260             sb.append(privateExponent.toString(16));
    261             sb.append(',');
    262         }
    263 
    264         return sb.toString();
    265     }
    266 
    267     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
    268         stream.defaultReadObject();
    269 
    270         key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
    271                 modulus.toByteArray(),
    272                 null,
    273                 privateExponent.toByteArray(),
    274                 null,
    275                 null,
    276                 null,
    277                 null,
    278                 null));
    279         fetchedParams = true;
    280     }
    281 
    282     private void writeObject(ObjectOutputStream stream) throws IOException {
    283         if (getOpenSSLKey().isEngineBased()) {
    284             throw new NotSerializableException("engine-based keys can not be serialized");
    285         }
    286 
    287         ensureReadParams();
    288         stream.defaultWriteObject();
    289     }
    290 }
    291