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