1/*
2 * Copyright (C) 2014 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.math.BigInteger;
20import java.security.InvalidKeyException;
21import java.security.Key;
22import java.security.KeyFactorySpi;
23import java.security.PrivateKey;
24import java.security.PublicKey;
25import java.security.spec.InvalidKeySpecException;
26import java.security.spec.KeySpec;
27import java.security.spec.PKCS8EncodedKeySpec;
28import java.security.spec.X509EncodedKeySpec;
29
30import javax.crypto.interfaces.DHPrivateKey;
31import javax.crypto.interfaces.DHPublicKey;
32import javax.crypto.spec.DHParameterSpec;
33import javax.crypto.spec.DHPrivateKeySpec;
34import javax.crypto.spec.DHPublicKeySpec;
35
36public class OpenSSLDHKeyFactory extends KeyFactorySpi {
37
38    @Override
39    protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
40        if (keySpec == null) {
41            throw new InvalidKeySpecException("keySpec == null");
42        }
43
44        if (keySpec instanceof DHPublicKeySpec) {
45            return new OpenSSLDHPublicKey((DHPublicKeySpec) keySpec);
46        } else if (keySpec instanceof X509EncodedKeySpec) {
47            return OpenSSLKey.getPublicKey((X509EncodedKeySpec) keySpec, NativeCrypto.EVP_PKEY_DH);
48        }
49        throw new InvalidKeySpecException("Must use DHPublicKeySpec or X509EncodedKeySpec; was "
50                + keySpec.getClass().getName());
51    }
52
53    @Override
54    protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException {
55        if (keySpec == null) {
56            throw new InvalidKeySpecException("keySpec == null");
57        }
58
59        if (keySpec instanceof DHPrivateKeySpec) {
60            return new OpenSSLDHPrivateKey((DHPrivateKeySpec) keySpec);
61        } else if (keySpec instanceof PKCS8EncodedKeySpec) {
62            return OpenSSLKey.getPrivateKey((PKCS8EncodedKeySpec) keySpec,
63                    NativeCrypto.EVP_PKEY_DH);
64        }
65        throw new InvalidKeySpecException("Must use DHPrivateKeySpec or PKCS8EncodedKeySpec; was "
66                + keySpec.getClass().getName());
67    }
68
69    @Override
70    protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
71            throws InvalidKeySpecException {
72        if (key == null) {
73            throw new InvalidKeySpecException("key == null");
74        }
75
76        if (keySpec == null) {
77            throw new InvalidKeySpecException("keySpec == null");
78        }
79
80        if (!"DH".equals(key.getAlgorithm())) {
81            throw new InvalidKeySpecException("Key must be a DH key");
82        }
83
84        if (key instanceof DHPublicKey && DHPublicKeySpec.class.isAssignableFrom(keySpec)) {
85            DHPublicKey dhKey = (DHPublicKey) key;
86            DHParameterSpec params = dhKey.getParams();
87            return (T) new DHPublicKeySpec(dhKey.getY(), params.getP(), params.getG());
88        } else if (key instanceof PublicKey && DHPublicKeySpec.class.isAssignableFrom(keySpec)) {
89            final byte[] encoded = key.getEncoded();
90            if (!"X.509".equals(key.getFormat()) || encoded == null) {
91                throw new InvalidKeySpecException("Not a valid X.509 encoding");
92            }
93            DHPublicKey dhKey = (DHPublicKey) engineGeneratePublic(new X509EncodedKeySpec(encoded));
94            DHParameterSpec params = dhKey.getParams();
95            return (T) new DHPublicKeySpec(dhKey.getY(), params.getP(), params.getG());
96        } else if (key instanceof DHPrivateKey && DHPrivateKeySpec.class.isAssignableFrom(keySpec)) {
97            DHPrivateKey dhKey = (DHPrivateKey) key;
98            DHParameterSpec params = dhKey.getParams();
99            return (T) new DHPrivateKeySpec(dhKey.getX(), params.getP(), params.getG());
100        } else if (key instanceof PrivateKey && DHPrivateKeySpec.class.isAssignableFrom(keySpec)) {
101            final byte[] encoded = key.getEncoded();
102            if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
103                throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
104            }
105            DHPrivateKey dhKey = (DHPrivateKey) engineGeneratePrivate(new PKCS8EncodedKeySpec(
106                    encoded));
107            DHParameterSpec params = dhKey.getParams();
108            return (T) new DHPrivateKeySpec(dhKey.getX(), params.getP(), params.getG());
109        } else if (key instanceof PrivateKey
110                && PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
111            final byte[] encoded = key.getEncoded();
112            if (!"PKCS#8".equals(key.getFormat())) {
113                throw new InvalidKeySpecException("Encoding type must be PKCS#8; was "
114                        + key.getFormat());
115            } else if (encoded == null) {
116                throw new InvalidKeySpecException("Key is not encodable");
117            }
118            return (T) new PKCS8EncodedKeySpec(encoded);
119        } else if (key instanceof PublicKey && X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
120            final byte[] encoded = key.getEncoded();
121            if (!"X.509".equals(key.getFormat())) {
122                throw new InvalidKeySpecException("Encoding type must be X.509; was "
123                        + key.getFormat());
124            } else if (encoded == null) {
125                throw new InvalidKeySpecException("Key is not encodable");
126            }
127            return (T) new X509EncodedKeySpec(encoded);
128        } else {
129            throw new InvalidKeySpecException("Unsupported key type and key spec combination; key="
130                    + key.getClass().getName() + ", keySpec=" + keySpec.getName());
131        }
132    }
133
134    @Override
135    protected Key engineTranslateKey(Key key) throws InvalidKeyException {
136        if (key == null) {
137            throw new InvalidKeyException("key == null");
138        }
139        if ((key instanceof OpenSSLDHPublicKey) || (key instanceof OpenSSLDHPrivateKey)) {
140            return key;
141        } else if (key instanceof DHPublicKey) {
142            DHPublicKey dhKey = (DHPublicKey) key;
143
144            BigInteger y = dhKey.getY();
145
146            DHParameterSpec params = dhKey.getParams();
147            BigInteger p = params.getP();
148            BigInteger g = params.getG();
149
150            try {
151                return engineGeneratePublic(new DHPublicKeySpec(y, p, g));
152            } catch (InvalidKeySpecException e) {
153                throw new InvalidKeyException(e);
154            }
155        } else if (key instanceof DHPrivateKey) {
156            DHPrivateKey dhKey = (DHPrivateKey) key;
157
158            BigInteger x = dhKey.getX();
159
160            DHParameterSpec params = dhKey.getParams();
161            BigInteger p = params.getP();
162            BigInteger g = params.getG();
163
164            try {
165                return engineGeneratePrivate(new DHPrivateKeySpec(x, p, g));
166            } catch (InvalidKeySpecException e) {
167                throw new InvalidKeyException(e);
168            }
169        } else if ((key instanceof PrivateKey) && ("PKCS#8".equals(key.getFormat()))) {
170            byte[] encoded = key.getEncoded();
171            if (encoded == null) {
172                throw new InvalidKeyException("Key does not support encoding");
173            }
174            try {
175                return engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded));
176            } catch (InvalidKeySpecException e) {
177                throw new InvalidKeyException(e);
178            }
179        } else if ((key instanceof PublicKey) && ("X.509".equals(key.getFormat()))) {
180            byte[] encoded = key.getEncoded();
181            if (encoded == null) {
182                throw new InvalidKeyException("Key does not support encoding");
183            }
184            try {
185                return engineGeneratePublic(new X509EncodedKeySpec(encoded));
186            } catch (InvalidKeySpecException e) {
187                throw new InvalidKeyException(e);
188            }
189        } else {
190            throw new InvalidKeyException("Key must be DH public or private key; was "
191                    + key.getClass().getName());
192        }
193    }
194}
195