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.conscrypt;
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.RSAPrivateCrtKey;
26import java.security.interfaces.RSAPrivateKey;
27import java.security.spec.InvalidKeySpecException;
28import java.security.spec.RSAPrivateCrtKeySpec;
29
30public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSAPrivateCrtKey {
31    private static final long serialVersionUID = 3785291944868707197L;
32
33    private BigInteger publicExponent;
34
35    private BigInteger primeP;
36
37    private BigInteger primeQ;
38
39    private BigInteger primeExponentP;
40
41    private BigInteger primeExponentQ;
42
43    private BigInteger crtCoefficient;
44
45    OpenSSLRSAPrivateCrtKey(OpenSSLKey key) {
46        super(key);
47    }
48
49    OpenSSLRSAPrivateCrtKey(OpenSSLKey key, byte[][] params) {
50        super(key, params);
51    }
52
53    public OpenSSLRSAPrivateCrtKey(RSAPrivateCrtKeySpec rsaKeySpec) throws InvalidKeySpecException {
54        super(init(rsaKeySpec));
55    }
56
57    private static OpenSSLKey init(RSAPrivateCrtKeySpec rsaKeySpec) throws InvalidKeySpecException {
58        BigInteger modulus = rsaKeySpec.getModulus();
59        BigInteger privateExponent = rsaKeySpec.getPrivateExponent();
60
61        if (modulus == null) {
62            throw new InvalidKeySpecException("modulus == null");
63        } else if (privateExponent == null) {
64            throw new InvalidKeySpecException("privateExponent == null");
65        }
66
67        try {
68            /*
69             * OpenSSL uses the public modulus to do RSA blinding. If
70             * the public modulus is not available, the call to
71             * EVP_PKEY_new_RSA will turn off blinding for this key
72             * instance.
73             */
74            final BigInteger publicExponent = rsaKeySpec.getPublicExponent();
75            final BigInteger primeP = rsaKeySpec.getPrimeP();
76            final BigInteger primeQ = rsaKeySpec.getPrimeQ();
77            final BigInteger primeExponentP = rsaKeySpec.getPrimeExponentP();
78            final BigInteger primeExponentQ = rsaKeySpec.getPrimeExponentQ();
79            final BigInteger crtCoefficient = rsaKeySpec.getCrtCoefficient();
80
81            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
82                    modulus.toByteArray(),
83                    publicExponent == null ? null : publicExponent.toByteArray(),
84                    privateExponent.toByteArray(),
85                    primeP == null ? null : primeP.toByteArray(),
86                    primeQ == null ? null : primeQ.toByteArray(),
87                    primeExponentP == null ? null : primeExponentP.toByteArray(),
88                    primeExponentQ == null ? null : primeExponentQ.toByteArray(),
89                    crtCoefficient == null ? null : crtCoefficient.toByteArray()));
90        } catch (Exception e) {
91            throw new InvalidKeySpecException(e);
92        }
93    }
94
95    static OpenSSLKey getInstance(RSAPrivateCrtKey rsaPrivateKey) throws InvalidKeyException {
96        BigInteger modulus = rsaPrivateKey.getModulus();
97        BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
98
99        if (modulus == null) {
100            throw new InvalidKeyException("modulus == null");
101        } else if (privateExponent == null) {
102            throw new InvalidKeyException("privateExponent == null");
103        }
104
105        try {
106            /*
107             * OpenSSL uses the public modulus to do RSA blinding. If
108             * the public modulus is not available, the call to
109             * EVP_PKEY_new_RSA will turn off blinding for this key
110             * instance.
111             */
112            final BigInteger publicExponent = rsaPrivateKey.getPublicExponent();
113            final BigInteger primeP = rsaPrivateKey.getPrimeP();
114            final BigInteger primeQ = rsaPrivateKey.getPrimeQ();
115            final BigInteger primeExponentP = rsaPrivateKey.getPrimeExponentP();
116            final BigInteger primeExponentQ = rsaPrivateKey.getPrimeExponentQ();
117            final BigInteger crtCoefficient = rsaPrivateKey.getCrtCoefficient();
118
119            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
120                    modulus.toByteArray(),
121                    publicExponent == null ? null : publicExponent.toByteArray(),
122                    privateExponent.toByteArray(),
123                    primeP == null ? null : primeP.toByteArray(),
124                    primeQ == null ? null : primeQ.toByteArray(),
125                    primeExponentP == null ? null : primeExponentP.toByteArray(),
126                    primeExponentQ == null ? null : primeExponentQ.toByteArray(),
127                    crtCoefficient == null ? null : crtCoefficient.toByteArray()));
128        } catch (Exception e) {
129            throw new InvalidKeyException(e);
130        }
131    }
132
133    @Override
134    synchronized void readParams(byte[][] params) {
135        super.readParams(params);
136        // params[0] read in super.readParams
137        if (params[1] != null) {
138            publicExponent = new BigInteger(params[1]);
139        }
140        // params[2] read in super.readParams
141        if (params[3] != null) {
142            primeP = new BigInteger(params[3]);
143        }
144        if (params[4] != null) {
145            primeQ = new BigInteger(params[4]);
146        }
147        if (params[5] != null) {
148            primeExponentP = new BigInteger(params[5]);
149        }
150        if (params[6] != null) {
151            primeExponentQ = new BigInteger(params[6]);
152        }
153        if (params[7] != null) {
154            crtCoefficient = new BigInteger(params[7]);
155        }
156    }
157
158    @Override
159    public BigInteger getPublicExponent() {
160        ensureReadParams();
161        return publicExponent;
162    }
163
164    @Override
165    public BigInteger getPrimeP() {
166        ensureReadParams();
167        return primeP;
168    }
169
170    @Override
171    public BigInteger getPrimeQ() {
172        ensureReadParams();
173        return primeQ;
174    }
175
176    @Override
177    public BigInteger getPrimeExponentP() {
178        ensureReadParams();
179        return primeExponentP;
180    }
181
182    @Override
183    public BigInteger getPrimeExponentQ() {
184        ensureReadParams();
185        return primeExponentQ;
186    }
187
188    @Override
189    public BigInteger getCrtCoefficient() {
190        ensureReadParams();
191        return crtCoefficient;
192    }
193
194    @Override
195    public boolean equals(Object o) {
196        if (o == this) {
197            return true;
198        }
199
200        if (o instanceof OpenSSLRSAPrivateKey) {
201            OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o;
202            return getOpenSSLKey().equals(other.getOpenSSLKey());
203        }
204
205        if (o instanceof RSAPrivateCrtKey) {
206            ensureReadParams();
207            RSAPrivateCrtKey other = (RSAPrivateCrtKey) o;
208
209            if (getOpenSSLKey().isEngineBased()) {
210                return getModulus().equals(other.getModulus())
211                        && publicExponent.equals(other.getPublicExponent());
212            } else {
213                return getModulus().equals(other.getModulus())
214                        && publicExponent.equals(other.getPublicExponent())
215                        && getPrivateExponent().equals(other.getPrivateExponent())
216                        && primeP.equals(other.getPrimeP()) && primeQ.equals(other.getPrimeQ())
217                        && primeExponentP.equals(other.getPrimeExponentP())
218                        && primeExponentQ.equals(other.getPrimeExponentQ())
219                        && crtCoefficient.equals(other.getCrtCoefficient());
220            }
221        } else if (o instanceof RSAPrivateKey) {
222            ensureReadParams();
223            RSAPrivateKey other = (RSAPrivateKey) o;
224
225            if (getOpenSSLKey().isEngineBased()) {
226                return getModulus().equals(other.getModulus());
227            } else {
228                return getModulus().equals(other.getModulus())
229                        && getPrivateExponent().equals(other.getPrivateExponent());
230            }
231        }
232
233        return false;
234    }
235
236    @Override
237    public final int hashCode() {
238        int hashCode = super.hashCode();
239        if (publicExponent != null) {
240            hashCode ^= publicExponent.hashCode();
241        }
242        return hashCode;
243    }
244
245    @Override
246    public String toString() {
247        final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateCrtKey{");
248
249        final boolean engineBased = getOpenSSLKey().isEngineBased();
250        if (engineBased) {
251            sb.append("key=");
252            sb.append(getOpenSSLKey());
253            sb.append('}');
254        }
255
256        ensureReadParams();
257        sb.append("modulus=");
258        sb.append(getModulus().toString(16));
259        sb.append(',');
260
261        if (publicExponent != null) {
262            sb.append("publicExponent=");
263            sb.append(publicExponent.toString(16));
264            sb.append(',');
265        }
266
267        if (!engineBased) {
268            sb.append("privateExponent=");
269            sb.append(getPrivateExponent().toString(16));
270            sb.append(',');
271        }
272
273        if (primeP != null) {
274            sb.append("primeP=");
275            sb.append(primeP.toString(16));
276            sb.append(',');
277        }
278
279        if (primeQ != null) {
280            sb.append("primeQ=");
281            sb.append(primeQ.toString(16));
282            sb.append(',');
283        }
284
285        if (primeExponentP != null) {
286            sb.append("primeExponentP=");
287            sb.append(primeExponentP.toString(16));
288            sb.append(',');
289        }
290
291        if (primeExponentQ != null) {
292            sb.append("primeExponentQ=");
293            sb.append(primeExponentQ.toString(16));
294            sb.append(',');
295        }
296
297        if (crtCoefficient != null) {
298            sb.append("crtCoefficient=");
299            sb.append(crtCoefficient.toString(16));
300            sb.append(',');
301        }
302
303        return sb.toString();
304    }
305
306    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
307        stream.defaultReadObject();
308
309        key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
310                modulus.toByteArray(),
311                publicExponent == null ? null : publicExponent.toByteArray(),
312                privateExponent.toByteArray(),
313                primeP == null ? null : primeP.toByteArray(),
314                primeQ == null ? null : primeQ.toByteArray(),
315                primeExponentP == null ? null : primeExponentP.toByteArray(),
316                primeExponentQ == null ? null : primeExponentQ.toByteArray(),
317                crtCoefficient == null ? null : crtCoefficient.toByteArray()));
318        fetchedParams = true;
319    }
320
321    private void writeObject(ObjectOutputStream stream) throws IOException {
322        if (getOpenSSLKey().isEngineBased()) {
323            throw new NotSerializableException("engine-based keys can not be serialized");
324        }
325
326        ensureReadParams();
327        stream.defaultWriteObject();
328    }
329}
330