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.InvalidAlgorithmParameterException;
24import java.security.InvalidKeyException;
25import java.security.PrivateKey;
26import java.security.PublicKey;
27import java.security.interfaces.ECKey;
28import java.security.interfaces.ECPrivateKey;
29import java.security.spec.ECParameterSpec;
30import java.security.spec.ECPrivateKeySpec;
31import java.security.spec.InvalidKeySpecException;
32import java.util.Arrays;
33
34/**
35 * An implementation of a {@link PrivateKey} for EC keys based on BoringSSL.
36 *
37 * @hide
38 */
39@Internal
40public final class OpenSSLECPrivateKey implements ECPrivateKey, OpenSSLKeyHolder {
41    private static final long serialVersionUID = -4036633595001083922L;
42
43    private static final String ALGORITHM = "EC";
44
45    protected transient OpenSSLKey key;
46
47    protected transient OpenSSLECGroupContext group;
48
49    public OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key) {
50        this.group = group;
51        this.key = key;
52    }
53
54    public OpenSSLECPrivateKey(OpenSSLKey key) {
55        this.group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
56                NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
57        this.key = key;
58    }
59
60    public OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec) throws InvalidKeySpecException {
61        try {
62            group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams());
63            final BigInteger privKey = ecKeySpec.getS();
64            key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(), null,
65                    privKey.toByteArray()));
66        } catch (Exception e) {
67            throw new InvalidKeySpecException(e);
68        }
69    }
70
71    public static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
72        OpenSSLECGroupContext group;
73        try {
74            group = OpenSSLECGroupContext.getInstance(ecPrivateKey.getParams());
75        } catch (InvalidAlgorithmParameterException e) {
76            throw new InvalidKeyException("Unknown group parameters", e);
77        }
78        return wrapPlatformKey(ecPrivateKey, group);
79    }
80
81    /**
82     * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations
83     * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the
84     * provider which accepts the key.
85     */
86    static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey,
87            PublicKey publicKey) throws InvalidKeyException {
88        ECParameterSpec params = null;
89        if (privateKey instanceof ECKey) {
90            params = ((ECKey) privateKey).getParams();
91        } else if (publicKey instanceof ECKey) {
92            params = ((ECKey) publicKey).getParams();
93        }
94        if (params == null) {
95            throw new InvalidKeyException("EC parameters not available. Private: " + privateKey
96                    + ", public: " + publicKey);
97        }
98        return wrapJCAPrivateKeyForTLSStackOnly(privateKey, params);
99    }
100
101    /**
102     * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations
103     * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the
104     * provider which accepts the key.
105     */
106    static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey,
107            ECParameterSpec params) throws InvalidKeyException {
108        if (params == null) {
109            if (privateKey instanceof ECKey) {
110                params = ((ECKey) privateKey).getParams();
111            }
112        }
113        if (params == null) {
114            throw new InvalidKeyException("EC parameters not available: " + privateKey);
115        }
116
117        OpenSSLECGroupContext group;
118        try {
119            group = OpenSSLECGroupContext.getInstance(params);
120        } catch (InvalidAlgorithmParameterException e) {
121            throw new InvalidKeyException("Invalid EC parameters: " + params);
122        }
123
124        return new OpenSSLKey(
125                NativeCrypto.getECPrivateKeyWrapper(privateKey, group.getNativeRef()), true);
126    }
127
128    private static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey,
129            OpenSSLECGroupContext group) throws InvalidKeyException {
130        return new OpenSSLKey(NativeCrypto.getECPrivateKeyWrapper(ecPrivateKey,
131                group.getNativeRef()), true);
132    }
133
134    public static OpenSSLKey getInstance(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
135        try {
136            OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecPrivateKey
137                    .getParams());
138
139            /**
140             * If the key is not encodable (PKCS11-like key), then wrap it and
141             * use JNI upcalls to satisfy requests.
142             */
143            if (ecPrivateKey.getFormat() == null) {
144                return wrapPlatformKey(ecPrivateKey, group);
145            }
146
147            final BigInteger privKey = ecPrivateKey.getS();
148            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(), null,
149                    privKey.toByteArray()));
150        } catch (Exception e) {
151            throw new InvalidKeyException(e);
152        }
153    }
154
155    @Override
156    public String getAlgorithm() {
157        return ALGORITHM;
158    }
159
160    @Override
161    public String getFormat() {
162        return "PKCS#8";
163    }
164
165    @Override
166    public byte[] getEncoded() {
167        return NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getNativeRef());
168    }
169
170    @Override
171    public ECParameterSpec getParams() {
172        return group.getECParameterSpec();
173    }
174
175    @Override
176    public BigInteger getS() {
177        return getPrivateKey();
178    }
179
180    private BigInteger getPrivateKey() {
181        return new BigInteger(NativeCrypto.EC_KEY_get_private_key(key.getNativeRef()));
182    }
183
184    @Override
185    public OpenSSLKey getOpenSSLKey() {
186        return key;
187    }
188
189    @Override
190    public boolean equals(Object o) {
191        if (o == this) {
192            return true;
193        }
194
195        if (o instanceof OpenSSLECPrivateKey) {
196            OpenSSLECPrivateKey other = (OpenSSLECPrivateKey) o;
197            return key.equals(other.key);
198        }
199
200        if (!(o instanceof ECPrivateKey)) {
201            return false;
202        }
203
204        final ECPrivateKey other = (ECPrivateKey) o;
205        if (!getPrivateKey().equals(other.getS())) {
206            return false;
207        }
208
209        final ECParameterSpec spec = getParams();
210        final ECParameterSpec otherSpec = other.getParams();
211
212        return spec.getCurve().equals(otherSpec.getCurve())
213                && spec.getGenerator().equals(otherSpec.getGenerator())
214                && spec.getOrder().equals(otherSpec.getOrder())
215                && spec.getCofactor() == otherSpec.getCofactor();
216    }
217
218    @Override
219    public int hashCode() {
220        return Arrays.hashCode(NativeCrypto.i2d_PKCS8_PRIV_KEY_INFO(key.getNativeRef()));
221    }
222
223    @Override
224    public String toString() {
225        StringBuilder sb = new StringBuilder("OpenSSLECPrivateKey{");
226        sb.append("params={");
227        sb.append(NativeCrypto.EVP_PKEY_print_params(key.getNativeRef()));
228        sb.append("}}");
229        return sb.toString();
230    }
231
232    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
233        stream.defaultReadObject();
234
235        byte[] encoded = (byte[]) stream.readObject();
236
237        key = new OpenSSLKey(NativeCrypto.d2i_PKCS8_PRIV_KEY_INFO(encoded));
238        group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
239                NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
240    }
241
242    private void writeObject(ObjectOutputStream stream) throws IOException {
243        stream.defaultWriteObject();
244        stream.writeObject(getEncoded());
245    }
246}
247