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.math.BigInteger;
20import java.security.InvalidKeyException;
21import java.security.interfaces.RSAPrivateKey;
22import java.security.spec.InvalidKeySpecException;
23import java.security.spec.RSAPrivateKeySpec;
24
25public class OpenSSLRSAPrivateKey implements RSAPrivateKey {
26    private static final long serialVersionUID = 4872170254439578735L;
27
28    private final OpenSSLKey key;
29
30    private boolean fetchedParams;
31
32    private BigInteger modulus;
33
34    private BigInteger privateExponent;
35
36    OpenSSLRSAPrivateKey(OpenSSLKey key) {
37        this.key = key;
38    }
39
40    OpenSSLRSAPrivateKey(OpenSSLKey key, byte[][] params) {
41        this(key);
42        readParams(params);
43        fetchedParams = true;
44    }
45
46    final OpenSSLKey getOpenSSLKey() {
47        return key;
48    }
49
50    public OpenSSLRSAPrivateKey(RSAPrivateKeySpec rsaKeySpec) throws InvalidKeySpecException {
51        this(init(rsaKeySpec));
52    }
53
54    private static OpenSSLKey init(RSAPrivateKeySpec rsaKeySpec) throws InvalidKeySpecException {
55        final BigInteger modulus = rsaKeySpec.getModulus();
56        final BigInteger privateExponent = rsaKeySpec.getPrivateExponent();
57
58        if (modulus == null) {
59            throw new InvalidKeySpecException("modulus == null");
60        } else if (privateExponent == null) {
61            throw new InvalidKeySpecException("privateExponent == null");
62        }
63
64        try {
65            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
66                    modulus.toByteArray(),
67                    null,
68                    privateExponent.toByteArray(),
69                    null,
70                    null,
71                    null,
72                    null,
73                    null));
74        } catch (Exception e) {
75            throw new InvalidKeySpecException(e);
76        }
77    }
78
79    static OpenSSLRSAPrivateKey getInstance(OpenSSLKey key) {
80      byte[][] params = NativeCrypto.get_RSA_private_params(key.getPkeyContext());
81      if (params[1] != null) {
82          return new OpenSSLRSAPrivateCrtKey(key, params);
83      }
84      return new OpenSSLRSAPrivateKey(key, params);
85    }
86
87    static OpenSSLKey getInstance(RSAPrivateKey rsaPrivateKey) throws InvalidKeyException {
88        final BigInteger modulus = rsaPrivateKey.getModulus();
89        final BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
90
91        if (modulus == null) {
92            throw new InvalidKeyException("modulus == null");
93        } else if (privateExponent == null) {
94            throw new InvalidKeyException("privateExponent == null");
95        }
96
97        try {
98            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
99                    modulus.toByteArray(),
100                    null,
101                    privateExponent.toByteArray(),
102                    null,
103                    null,
104                    null,
105                    null,
106                    null));
107        } catch (Exception e) {
108            throw new InvalidKeyException(e);
109        }
110    }
111
112    synchronized final void ensureReadParams() {
113        if (fetchedParams) {
114            return;
115        }
116        readParams(NativeCrypto.get_RSA_private_params(key.getPkeyContext()));
117        fetchedParams = true;
118    }
119
120    void readParams(byte[][] params) {
121        if (params[0] == null) {
122            throw new NullPointerException("modulus == null");
123        } else if (params[2] == null && !key.isEngineBased()) {
124            throw new NullPointerException("privateExponent == null");
125        }
126
127        modulus = new BigInteger(params[0]);
128
129        // ENGINE-based keys are not guaranteed to have a private exponent.
130        if (params[2] != null) {
131            privateExponent = new BigInteger(params[2]);
132        }
133    }
134
135    @Override
136    public final BigInteger getPrivateExponent() {
137        ensureReadParams();
138        return privateExponent;
139    }
140
141    @Override
142    public final BigInteger getModulus() {
143        ensureReadParams();
144        return modulus;
145    }
146
147    @Override
148    public final byte[] getEncoded() {
149        /*
150         * If we're using an OpenSSL ENGINE, there's no guarantee we can export
151         * the key. Returning {@code null} tells the caller that there's no
152         * encoded format.
153         */
154        if (key.isEngineBased()) {
155            return null;
156        }
157
158        return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getPkeyContext());
159    }
160
161    public final String getFormat() {
162        /*
163         * If we're using an OpenSSL ENGINE, there's no guarantee we can export
164         * the key. Returning {@code null} tells the caller that there's no
165         * encoded format.
166         */
167        if (key.isEngineBased()) {
168            return null;
169        }
170
171        return "PKCS#8";
172    }
173
174    @Override
175    public final String getAlgorithm() {
176        return "RSA";
177    }
178
179    public int getPkeyContext() {
180        return key.getPkeyContext();
181    }
182
183    public String getPkeyAlias() {
184        return key.getAlias();
185    }
186
187    @Override
188    public boolean equals(Object o) {
189        if (o == this) {
190            return true;
191        }
192
193        if (o instanceof OpenSSLRSAPrivateKey) {
194            OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o;
195
196            /*
197             * We can shortcut the true case, but it still may be equivalent but
198             * different copies.
199             */
200            if (key.equals(other.getOpenSSLKey())) {
201                return true;
202            }
203
204            return NativeCrypto.EVP_PKEY_cmp(getPkeyContext(), other.getPkeyContext()) == 1;
205        }
206
207        if (o instanceof RSAPrivateKey) {
208            ensureReadParams();
209            RSAPrivateKey other = (RSAPrivateKey) o;
210
211            return modulus.equals(other.getModulus())
212                    && privateExponent.equals(other.getPrivateExponent());
213        }
214
215        return false;
216    }
217
218    @Override
219    public int hashCode() {
220        ensureReadParams();
221        int hash = 1;
222
223        hash = hash * 3 + modulus.hashCode();
224        if (privateExponent != null) {
225            hash = hash * 7 + privateExponent.hashCode();
226        }
227
228        return hash;
229    }
230
231    @Override
232    public String toString() {
233        final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateKey{");
234
235        final boolean engineBased = key.isEngineBased();
236        if (engineBased) {
237            sb.append("key=");
238            sb.append(key);
239            sb.append('}');
240        }
241
242        ensureReadParams();
243        sb.append("modulus=");
244        sb.append(modulus.toString(16));
245        sb.append(',');
246
247        if (!engineBased) {
248            sb.append("privateExponent=");
249            sb.append(privateExponent.toString(16));
250            sb.append(',');
251        }
252
253        return sb.toString();
254    }
255}
256