Home | History | Annotate | Download | only in jsse
      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.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.DSAParams;
     26 import java.security.interfaces.DSAPrivateKey;
     27 import java.security.spec.DSAPrivateKeySpec;
     28 import java.security.spec.InvalidKeySpecException;
     29 
     30 public class OpenSSLDSAPrivateKey implements DSAPrivateKey, OpenSSLKeyHolder {
     31     private static final long serialVersionUID = 6524734576187424628L;
     32 
     33     private transient OpenSSLKey key;
     34 
     35     private transient OpenSSLDSAParams params;
     36 
     37     OpenSSLDSAPrivateKey(OpenSSLKey key) {
     38         this.key = key;
     39     }
     40 
     41     @Override
     42     public OpenSSLKey getOpenSSLKey() {
     43         return key;
     44     }
     45 
     46     OpenSSLDSAPrivateKey(DSAPrivateKeySpec dsaKeySpec) throws InvalidKeySpecException {
     47         try {
     48             key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA(
     49                     dsaKeySpec.getP().toByteArray(),
     50                     dsaKeySpec.getQ().toByteArray(),
     51                     dsaKeySpec.getG().toByteArray(),
     52                     null,
     53                     dsaKeySpec.getX().toByteArray()));
     54         } catch (Exception e) {
     55             throw new InvalidKeySpecException(e);
     56         }
     57     }
     58 
     59     private void ensureReadParams() {
     60         if (params == null) {
     61             params = new OpenSSLDSAParams(key);
     62         }
     63     }
     64 
     65     static OpenSSLKey getInstance(DSAPrivateKey dsaPrivateKey) throws InvalidKeyException {
     66         try {
     67             DSAParams dsaParams = dsaPrivateKey.getParams();
     68             return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA(
     69                     dsaParams.getP().toByteArray(),
     70                     dsaParams.getQ().toByteArray(),
     71                     dsaParams.getG().toByteArray(),
     72                     null,
     73                     dsaPrivateKey.getX().toByteArray()));
     74         } catch (Exception e) {
     75             throw new InvalidKeyException(e);
     76         }
     77     }
     78 
     79     @Override
     80     public DSAParams getParams() {
     81         ensureReadParams();
     82         return params;
     83     }
     84 
     85     @Override
     86     public String getAlgorithm() {
     87         return "DSA";
     88     }
     89 
     90     @Override
     91     public String getFormat() {
     92         /*
     93          * If we're using an OpenSSL ENGINE, there's no guarantee we can export
     94          * the key. Returning {@code null} tells the caller that there's no
     95          * encoded format.
     96          */
     97         if (key.isEngineBased()) {
     98             return null;
     99         }
    100 
    101         return "PKCS#8";
    102     }
    103 
    104     @Override
    105     public byte[] getEncoded() {
    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 NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext());
    116     }
    117 
    118     @Override
    119     public BigInteger getX() {
    120         if (key.isEngineBased()) {
    121             throw new UnsupportedOperationException("private key value X cannot be extracted");
    122         }
    123 
    124         ensureReadParams();
    125         return params.getX();
    126     }
    127 
    128     @Override
    129     public boolean equals(Object o) {
    130         if (o == this) {
    131             return true;
    132         }
    133 
    134         if (o instanceof OpenSSLDSAPrivateKey) {
    135             OpenSSLDSAPrivateKey other = (OpenSSLDSAPrivateKey) o;
    136 
    137             /*
    138              * We can shortcut the true case, but it still may be equivalent but
    139              * different copies.
    140              */
    141             if (key.equals(other.getOpenSSLKey())) {
    142                 return true;
    143             }
    144         }
    145 
    146         if (!(o instanceof DSAPrivateKey)) {
    147             return false;
    148         }
    149 
    150         ensureReadParams();
    151 
    152         final BigInteger x = params.getX();
    153         if (x == null) {
    154             /*
    155              * If our X is null, we can't tell if these two private keys are
    156              * equivalent. This usually happens if this key is ENGINE-based. If
    157              * the other key was ENGINE-based, we should have caught it in the
    158              * OpenSSLDSAPrivateKey case.
    159              */
    160             return false;
    161         }
    162 
    163         final DSAPrivateKey other = (DSAPrivateKey) o;
    164         return x.equals(other.getX()) && params.equals(other.getParams());
    165     }
    166 
    167     @Override
    168     public int hashCode() {
    169         ensureReadParams();
    170 
    171         int hash = 1;
    172 
    173         final BigInteger x = getX();
    174         if (x != null) {
    175             hash = hash * 3 + x.hashCode();
    176         }
    177 
    178         hash = hash * 7 + params.hashCode();
    179 
    180         return hash;
    181     }
    182 
    183     @Override
    184     public String toString() {
    185         final StringBuilder sb = new StringBuilder("OpenSSLDSAPrivateKey{");
    186 
    187         if (key.isEngineBased()) {
    188             sb.append("key=");
    189             sb.append(key);
    190             sb.append('}');
    191             return sb.toString();
    192         }
    193 
    194         ensureReadParams();
    195         sb.append("X=");
    196         sb.append(params.getX().toString(16));
    197         sb.append(',');
    198         sb.append("params=");
    199         sb.append(params.toString());
    200         sb.append('}');
    201 
    202         return sb.toString();
    203     }
    204 
    205     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
    206         stream.defaultReadObject();
    207 
    208         final BigInteger g = (BigInteger) stream.readObject();
    209         final BigInteger p = (BigInteger) stream.readObject();
    210         final BigInteger q = (BigInteger) stream.readObject();
    211         final BigInteger x = (BigInteger) stream.readObject();
    212 
    213         key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DSA(
    214                 p.toByteArray(),
    215                 q.toByteArray(),
    216                 g.toByteArray(),
    217                 null,
    218                 x.toByteArray()));
    219     }
    220 
    221     private void writeObject(ObjectOutputStream stream) throws IOException {
    222         if (getOpenSSLKey().isEngineBased()) {
    223             throw new NotSerializableException("engine-based keys can not be serialized");
    224         }
    225 
    226         stream.defaultWriteObject();
    227 
    228         ensureReadParams();
    229         stream.writeObject(params.getG());
    230         stream.writeObject(params.getP());
    231         stream.writeObject(params.getQ());
    232         stream.writeObject(params.getX());
    233     }
    234 }
    235