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.InvalidAlgorithmParameterException;
     25 import java.security.InvalidKeyException;
     26 import java.security.interfaces.ECPrivateKey;
     27 import java.security.spec.ECParameterSpec;
     28 import java.security.spec.ECPrivateKeySpec;
     29 import java.security.spec.InvalidKeySpecException;
     30 import java.util.Arrays;
     31 
     32 public final class OpenSSLECPrivateKey implements ECPrivateKey, OpenSSLKeyHolder {
     33     private static final long serialVersionUID = -4036633595001083922L;
     34 
     35     private static final String ALGORITHM = "EC";
     36 
     37     protected transient OpenSSLKey key;
     38 
     39     protected transient OpenSSLECGroupContext group;
     40 
     41     public OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key) {
     42         this.group = group;
     43         this.key = key;
     44     }
     45 
     46     public OpenSSLECPrivateKey(OpenSSLKey key) {
     47         final long origGroup = NativeCrypto.EC_KEY_get0_group(key.getPkeyContext());
     48         this.group = new OpenSSLECGroupContext(NativeCrypto.EC_GROUP_dup(origGroup));
     49         this.key = key;
     50     }
     51 
     52     public OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec) throws InvalidKeySpecException {
     53         try {
     54             group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams());
     55             final BigInteger privKey = ecKeySpec.getS();
     56             key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getContext(), 0,
     57                     privKey.toByteArray()));
     58         } catch (Exception e) {
     59             throw new InvalidKeySpecException(e);
     60         }
     61     }
     62 
     63     public static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
     64         OpenSSLECGroupContext group;
     65         try {
     66             group = OpenSSLECGroupContext.getInstance(ecPrivateKey.getParams());
     67         } catch (InvalidAlgorithmParameterException e) {
     68             throw new InvalidKeyException("Unknown group parameters", e);
     69         }
     70         return wrapPlatformKey(ecPrivateKey, group);
     71     }
     72 
     73     private static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey, OpenSSLECGroupContext group)
     74             throws InvalidKeyException {
     75         return new OpenSSLKey(NativeCrypto.getECPrivateKeyWrapper(ecPrivateKey, group.getContext()));
     76     }
     77 
     78     public static OpenSSLKey getInstance(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
     79         try {
     80             OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecPrivateKey
     81                     .getParams());
     82 
     83             /**
     84              * If the key is not encodable (PKCS11-like key), then wrap it and
     85              * use JNI upcalls to satisfy requests.
     86              */
     87             if (ecPrivateKey.getFormat() == null) {
     88                 return wrapPlatformKey(ecPrivateKey, group);
     89             }
     90 
     91             final BigInteger privKey = ecPrivateKey.getS();
     92             return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getContext(), 0,
     93                     privKey.toByteArray()));
     94         } catch (Exception e) {
     95             throw new InvalidKeyException(e);
     96         }
     97     }
     98 
     99     @Override
    100     public String getAlgorithm() {
    101         return ALGORITHM;
    102     }
    103 
    104     @Override
    105     public String getFormat() {
    106         /*
    107          * If we're using an OpenSSL ENGINE, there's no guarantee we can export
    108          * the key. Returning {@code null} tells the caller that there's no
    109          * encoded format.
    110          */
    111         if (key.isEngineBased()) {
    112             return null;
    113         }
    114 
    115         return "PKCS#8";
    116     }
    117 
    118     @Override
    119     public byte[] getEncoded() {
    120         /*
    121          * If we're using an OpenSSL ENGINE, there's no guarantee we can export
    122          * the key. Returning {@code null} tells the caller that there's no
    123          * encoded format.
    124          */
    125         if (key.isEngineBased()) {
    126             return null;
    127         }
    128 
    129         return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext());
    130     }
    131 
    132     @Override
    133     public ECParameterSpec getParams() {
    134         return group.getECParameterSpec();
    135     }
    136 
    137     @Override
    138     public BigInteger getS() {
    139         if (key.isEngineBased()) {
    140             throw new UnsupportedOperationException("private key value S cannot be extracted");
    141         }
    142 
    143         return getPrivateKey();
    144     }
    145 
    146     private BigInteger getPrivateKey() {
    147         return new BigInteger(NativeCrypto.EC_KEY_get_private_key(key.getPkeyContext()));
    148     }
    149 
    150     @Override
    151     public OpenSSLKey getOpenSSLKey() {
    152         return key;
    153     }
    154 
    155     @Override
    156     public boolean equals(Object o) {
    157         if (o == this) {
    158             return true;
    159         }
    160 
    161         if (o instanceof OpenSSLECPrivateKey) {
    162             OpenSSLECPrivateKey other = (OpenSSLECPrivateKey) o;
    163             return key.equals(other.key);
    164         }
    165 
    166         if (!(o instanceof ECPrivateKey)) {
    167             return false;
    168         }
    169 
    170         final ECPrivateKey other = (ECPrivateKey) o;
    171         if (!getPrivateKey().equals(other.getS())) {
    172             return false;
    173         }
    174 
    175         final ECParameterSpec spec = getParams();
    176         final ECParameterSpec otherSpec = other.getParams();
    177 
    178         return spec.getCurve().equals(otherSpec.getCurve())
    179                 && spec.getGenerator().equals(otherSpec.getGenerator())
    180                 && spec.getOrder().equals(otherSpec.getOrder())
    181                 && spec.getCofactor() == otherSpec.getCofactor();
    182     }
    183 
    184     @Override
    185     public int hashCode() {
    186         return Arrays.hashCode(NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext()));
    187     }
    188 
    189     @Override
    190     public String toString() {
    191         return NativeCrypto.EVP_PKEY_print_private(key.getPkeyContext());
    192     }
    193 
    194     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
    195         stream.defaultReadObject();
    196 
    197         byte[] encoded = (byte[]) stream.readObject();
    198 
    199         key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(encoded));
    200 
    201         final long origGroup = NativeCrypto.EC_KEY_get0_group(key.getPkeyContext());
    202         group = new OpenSSLECGroupContext(NativeCrypto.EC_GROUP_dup(origGroup));
    203     }
    204 
    205     private void writeObject(ObjectOutputStream stream) throws IOException {
    206         if (key.isEngineBased()) {
    207             throw new NotSerializableException("engine-based keys can not be serialized");
    208         }
    209 
    210         stream.defaultWriteObject();
    211         stream.writeObject(getEncoded());
    212     }
    213 }
    214