OpenSSLDHPublicKey.java revision 7b27ca77c328e510a165712a497c20b67c68e8a3
1/*
2 * Copyright (C) 2014 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.conscrypt;
18
19import java.io.IOException;
20import java.io.ObjectInputStream;
21import java.io.ObjectOutputStream;
22import java.math.BigInteger;
23import java.security.InvalidKeyException;
24import java.security.spec.InvalidKeySpecException;
25import javax.crypto.interfaces.DHPublicKey;
26import javax.crypto.spec.DHParameterSpec;
27import javax.crypto.spec.DHPublicKeySpec;
28
29public class OpenSSLDHPublicKey implements DHPublicKey, OpenSSLKeyHolder {
30    private static final long serialVersionUID = 6123717708079837723L;
31
32    private transient OpenSSLKey key;
33
34    /** base prime */
35    private transient byte[] p;
36
37    /** generator */
38    private transient byte[] g;
39
40    /** public key */
41    private transient byte[] y;
42
43    private transient final Object mParamsLock = new Object();
44
45    private transient boolean readParams;
46
47    OpenSSLDHPublicKey(OpenSSLKey key) {
48        this.key = key;
49    }
50
51    @Override
52    public OpenSSLKey getOpenSSLKey() {
53        return key;
54    }
55
56    OpenSSLDHPublicKey(DHPublicKeySpec dsaKeySpec) throws InvalidKeySpecException {
57        try {
58            key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH(
59                    dsaKeySpec.getP().toByteArray(),
60                    dsaKeySpec.getG().toByteArray(),
61                    dsaKeySpec.getY().toByteArray(),
62                    null));
63        } catch (Exception e) {
64            throw new InvalidKeySpecException(e);
65        }
66    }
67
68    private void ensureReadParams() {
69        synchronized (mParamsLock) {
70            if (readParams) {
71                return;
72            }
73
74            byte[][] params = NativeCrypto.get_DH_params(key.getPkeyContext());
75
76            p = params[0];
77            g = params[1];
78            y = params[2];
79
80            readParams = true;
81        }
82    }
83
84    static OpenSSLKey getInstance(DHPublicKey DHPublicKey) throws InvalidKeyException {
85        try {
86            final DHParameterSpec dhParams = DHPublicKey.getParams();
87            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH(
88                    dhParams.getP().toByteArray(),
89                    dhParams.getG().toByteArray(),
90                    DHPublicKey.getY().toByteArray(),
91                    null));
92        } catch (Exception e) {
93            throw new InvalidKeyException(e);
94        }
95    }
96
97    @Override
98    public DHParameterSpec getParams() {
99        ensureReadParams();
100        return new DHParameterSpec(new BigInteger(p), new BigInteger(g));
101    }
102
103    @Override
104    public String getAlgorithm() {
105        return "DSA";
106    }
107
108    @Override
109    public String getFormat() {
110        return "X.509";
111    }
112
113    @Override
114    public byte[] getEncoded() {
115        return NativeCrypto.i2d_PUBKEY(key.getPkeyContext());
116    }
117
118    @Override
119    public BigInteger getY() {
120        ensureReadParams();
121        return new BigInteger(y);
122    }
123
124    @Override
125    public boolean equals(Object o) {
126        if (o == this) {
127            return true;
128        }
129
130        if (o instanceof OpenSSLDHPublicKey) {
131            OpenSSLDHPublicKey other = (OpenSSLDHPublicKey) o;
132
133            /*
134             * We can shortcut the true case, but it still may be equivalent but
135             * different copies.
136             */
137            if (key.equals(other.getOpenSSLKey())) {
138                return true;
139            }
140        }
141
142        if (!(o instanceof DHPublicKey)) {
143            return false;
144        }
145
146        ensureReadParams();
147
148        final DHPublicKey other = (DHPublicKey) o;
149        if (!y.equals(other.getY())) {
150            return false;
151        }
152
153        DHParameterSpec spec = other.getParams();
154        return g.equals(spec.getG()) && p.equals(spec.getP());
155    }
156
157    @Override
158    public int hashCode() {
159        ensureReadParams();
160        int hash = 1;
161        hash = hash * 3 + y.hashCode();
162        hash = hash * 7 + p.hashCode();
163        hash = hash * 13 + g.hashCode();
164        return hash;
165    }
166
167
168    @Override
169    public String toString() {
170        ensureReadParams();
171
172        final StringBuilder sb = new StringBuilder("OpenSSLDHPublicKey{");
173        sb.append("Y=");
174        sb.append(new BigInteger(y).toString(16));
175        sb.append(',');
176        sb.append("P=");
177        sb.append(new BigInteger(p).toString(16));
178        sb.append(',');
179        sb.append("G=");
180        sb.append(new BigInteger(g).toString(16));
181        sb.append('}');
182
183        return sb.toString();
184    }
185
186    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
187        stream.defaultReadObject();
188
189        final BigInteger g = (BigInteger) stream.readObject();
190        final BigInteger p = (BigInteger) stream.readObject();
191        final BigInteger y = (BigInteger) stream.readObject();
192
193        key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_DH(
194                p.toByteArray(),
195                g.toByteArray(),
196                y.toByteArray(),
197                null));
198    }
199
200    private void writeObject(ObjectOutputStream stream) throws IOException {
201        stream.defaultWriteObject();
202
203        ensureReadParams();
204        stream.writeObject(new BigInteger(g));
205        stream.writeObject(new BigInteger(p));
206        stream.writeObject(new BigInteger(y));
207    }
208}
209