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