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