1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.ssl;
17
18 import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
19 import static io.netty.util.internal.ObjectUtil.checkNotNull;
20
21 import io.netty.buffer.ByteBufAllocator;
22 import io.netty.buffer.UnpooledByteBufAllocator;
23 import io.netty.internal.tcnative.SSL;
24 import io.netty.util.ReferenceCountUtil;
25 import io.netty.util.internal.ObjectUtil;
26
27 import javax.net.ssl.KeyManager;
28 import javax.net.ssl.KeyManagerFactory;
29 import javax.net.ssl.KeyManagerFactorySpi;
30 import javax.net.ssl.ManagerFactoryParameters;
31 import javax.net.ssl.X509KeyManager;
32 import java.io.File;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.security.InvalidAlgorithmParameterException;
37 import java.security.Key;
38 import java.security.KeyStore;
39 import java.security.KeyStoreException;
40 import java.security.KeyStoreSpi;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.PrivateKey;
43 import java.security.Provider;
44 import java.security.UnrecoverableKeyException;
45 import java.security.cert.Certificate;
46 import java.security.cert.CertificateException;
47 import java.security.cert.X509Certificate;
48 import java.util.Collections;
49 import java.util.Date;
50 import java.util.Enumeration;
51 import java.util.HashMap;
52 import java.util.Map;
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public final class OpenSslX509KeyManagerFactory extends KeyManagerFactory {
67
68 private final OpenSslKeyManagerFactorySpi spi;
69
70 public OpenSslX509KeyManagerFactory() {
71 this(newOpenSslKeyManagerFactorySpi(null));
72 }
73
74 public OpenSslX509KeyManagerFactory(Provider provider) {
75 this(newOpenSslKeyManagerFactorySpi(provider));
76 }
77
78 public OpenSslX509KeyManagerFactory(String algorithm, Provider provider) throws NoSuchAlgorithmException {
79 this(newOpenSslKeyManagerFactorySpi(algorithm, provider));
80 }
81
82 private OpenSslX509KeyManagerFactory(OpenSslKeyManagerFactorySpi spi) {
83 super(spi, spi.kmf.getProvider(), spi.kmf.getAlgorithm());
84 this.spi = spi;
85 }
86
87 private static OpenSslKeyManagerFactorySpi newOpenSslKeyManagerFactorySpi(Provider provider) {
88 try {
89 return newOpenSslKeyManagerFactorySpi(null, provider);
90 } catch (NoSuchAlgorithmException e) {
91
92 throw new IllegalStateException(e);
93 }
94 }
95
96 private static OpenSslKeyManagerFactorySpi newOpenSslKeyManagerFactorySpi(String algorithm, Provider provider)
97 throws NoSuchAlgorithmException {
98 if (algorithm == null) {
99 algorithm = KeyManagerFactory.getDefaultAlgorithm();
100 }
101 return new OpenSslKeyManagerFactorySpi(
102 provider == null ? KeyManagerFactory.getInstance(algorithm) :
103 KeyManagerFactory.getInstance(algorithm, provider));
104 }
105
106 OpenSslKeyMaterialProvider newProvider() {
107 return spi.newProvider();
108 }
109
110 private static final class OpenSslKeyManagerFactorySpi extends KeyManagerFactorySpi {
111 final KeyManagerFactory kmf;
112 private volatile ProviderFactory providerFactory;
113
114 OpenSslKeyManagerFactorySpi(KeyManagerFactory kmf) {
115 this.kmf = ObjectUtil.checkNotNull(kmf, "kmf");
116 }
117
118 @Override
119 protected synchronized void engineInit(KeyStore keyStore, char[] chars)
120 throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
121 if (providerFactory != null) {
122 throw new KeyStoreException("Already initialized");
123 }
124 if (!keyStore.aliases().hasMoreElements()) {
125 throw new KeyStoreException("No aliases found");
126 }
127
128 kmf.init(keyStore, chars);
129 providerFactory = new ProviderFactory(ReferenceCountedOpenSslContext.chooseX509KeyManager(
130 kmf.getKeyManagers()), password(chars), Collections.list(keyStore.aliases()));
131 }
132
133 private static String password(char[] password) {
134 if (password == null || password.length == 0) {
135 return null;
136 }
137 return new String(password);
138 }
139
140 @Override
141 protected void engineInit(ManagerFactoryParameters managerFactoryParameters)
142 throws InvalidAlgorithmParameterException {
143 throw new InvalidAlgorithmParameterException("Not supported");
144 }
145
146 @Override
147 protected KeyManager[] engineGetKeyManagers() {
148 ProviderFactory providerFactory = this.providerFactory;
149 if (providerFactory == null) {
150 throw new IllegalStateException("engineInit(...) not called yet");
151 }
152 return new KeyManager[] { providerFactory.keyManager };
153 }
154
155 OpenSslKeyMaterialProvider newProvider() {
156 ProviderFactory providerFactory = this.providerFactory;
157 if (providerFactory == null) {
158 throw new IllegalStateException("engineInit(...) not called yet");
159 }
160 return providerFactory.newProvider();
161 }
162
163 private static final class ProviderFactory {
164 private final X509KeyManager keyManager;
165 private final String password;
166 private final Iterable<String> aliases;
167
168 ProviderFactory(X509KeyManager keyManager, String password, Iterable<String> aliases) {
169 this.keyManager = keyManager;
170 this.password = password;
171 this.aliases = aliases;
172 }
173
174 OpenSslKeyMaterialProvider newProvider() {
175 return new OpenSslPopulatedKeyMaterialProvider(keyManager,
176 password, aliases);
177 }
178
179
180
181
182
183 private static final class OpenSslPopulatedKeyMaterialProvider extends OpenSslKeyMaterialProvider {
184 private final Map<String, Object> materialMap;
185
186 OpenSslPopulatedKeyMaterialProvider(
187 X509KeyManager keyManager, String password, Iterable<String> aliases) {
188 super(keyManager, password);
189 materialMap = new HashMap<String, Object>();
190 boolean initComplete = false;
191 try {
192 for (String alias: aliases) {
193 if (alias != null && !materialMap.containsKey(alias)) {
194 try {
195 materialMap.put(alias, super.chooseKeyMaterial(
196 UnpooledByteBufAllocator.DEFAULT, alias));
197 } catch (Exception e) {
198
199
200 materialMap.put(alias, e);
201 }
202 }
203 }
204 initComplete = true;
205 } finally {
206 if (!initComplete) {
207 destroy();
208 }
209 }
210 checkNonEmpty(materialMap, "materialMap");
211 }
212
213 @Override
214 OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception {
215 Object value = materialMap.get(alias);
216 if (value == null) {
217
218 return null;
219 }
220 if (value instanceof OpenSslKeyMaterial) {
221 return ((OpenSslKeyMaterial) value).retain();
222 }
223 throw (Exception) value;
224 }
225
226 @Override
227 void destroy() {
228 for (Object material: materialMap.values()) {
229 ReferenceCountUtil.release(material);
230 }
231 materialMap.clear();
232 }
233 }
234 }
235 }
236
237
238
239
240
241
242
243 public static OpenSslX509KeyManagerFactory newEngineBased(File certificateChain, String password)
244 throws CertificateException, IOException,
245 KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
246 return newEngineBased(SslContext.toX509Certificates(certificateChain), password);
247 }
248
249
250
251
252
253
254
255 public static OpenSslX509KeyManagerFactory newEngineBased(X509Certificate[] certificateChain, String password)
256 throws CertificateException, IOException,
257 KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
258 checkNotNull(certificateChain, "certificateChain");
259 KeyStore store = new OpenSslKeyStore(certificateChain.clone(), false);
260 store.load(null, null);
261 OpenSslX509KeyManagerFactory factory = new OpenSslX509KeyManagerFactory();
262 factory.init(store, password == null ? null : password.toCharArray());
263 return factory;
264 }
265
266
267
268
269 public static OpenSslX509KeyManagerFactory newKeyless(File chain)
270 throws CertificateException, IOException,
271 KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
272 return newKeyless(SslContext.toX509Certificates(chain));
273 }
274
275
276
277
278 public static OpenSslX509KeyManagerFactory newKeyless(InputStream chain)
279 throws CertificateException, IOException,
280 KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
281 return newKeyless(SslContext.toX509Certificates(chain));
282 }
283
284
285
286
287
288 public static OpenSslX509KeyManagerFactory newKeyless(X509Certificate... certificateChain)
289 throws CertificateException, IOException,
290 KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
291 checkNotNull(certificateChain, "certificateChain");
292 KeyStore store = new OpenSslKeyStore(certificateChain.clone(), true);
293 store.load(null, null);
294 OpenSslX509KeyManagerFactory factory = new OpenSslX509KeyManagerFactory();
295 factory.init(store, null);
296 return factory;
297 }
298
299 private static final class OpenSslKeyStore extends KeyStore {
300 private OpenSslKeyStore(final X509Certificate[] certificateChain, final boolean keyless) {
301 super(new KeyStoreSpi() {
302
303 private final Date creationDate = new Date();
304
305 @Override
306 public Key engineGetKey(String alias, char[] password) throws UnrecoverableKeyException {
307 if (engineContainsAlias(alias)) {
308 final long privateKeyAddress;
309 if (keyless) {
310 privateKeyAddress = 0;
311 } else {
312 try {
313 privateKeyAddress = SSL.loadPrivateKeyFromEngine(
314 alias, password == null ? null : new String(password));
315 } catch (Exception e) {
316 UnrecoverableKeyException keyException =
317 new UnrecoverableKeyException("Unable to load key from engine");
318 keyException.initCause(e);
319 throw keyException;
320 }
321 }
322 return new OpenSslPrivateKey(privateKeyAddress);
323 }
324 return null;
325 }
326
327 @Override
328 public Certificate[] engineGetCertificateChain(String alias) {
329 return engineContainsAlias(alias)? certificateChain.clone() : null;
330 }
331
332 @Override
333 public Certificate engineGetCertificate(String alias) {
334 return engineContainsAlias(alias)? certificateChain[0] : null;
335 }
336
337 @Override
338 public Date engineGetCreationDate(String alias) {
339 return engineContainsAlias(alias)? creationDate : null;
340 }
341
342 @Override
343 public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
344 throws KeyStoreException {
345 throw new KeyStoreException("Not supported");
346 }
347
348 @Override
349 public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException {
350 throw new KeyStoreException("Not supported");
351 }
352
353 @Override
354 public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
355 throw new KeyStoreException("Not supported");
356 }
357
358 @Override
359 public void engineDeleteEntry(String alias) throws KeyStoreException {
360 throw new KeyStoreException("Not supported");
361 }
362
363 @Override
364 public Enumeration<String> engineAliases() {
365 return Collections.enumeration(Collections.singleton(SslContext.ALIAS));
366 }
367
368 @Override
369 public boolean engineContainsAlias(String alias) {
370 return SslContext.ALIAS.equals(alias);
371 }
372
373 @Override
374 public int engineSize() {
375 return 1;
376 }
377
378 @Override
379 public boolean engineIsKeyEntry(String alias) {
380 return engineContainsAlias(alias);
381 }
382
383 @Override
384 public boolean engineIsCertificateEntry(String alias) {
385 return engineContainsAlias(alias);
386 }
387
388 @Override
389 public String engineGetCertificateAlias(Certificate cert) {
390 if (cert instanceof X509Certificate) {
391 for (X509Certificate x509Certificate : certificateChain) {
392 if (x509Certificate.equals(cert)) {
393 return SslContext.ALIAS;
394 }
395 }
396 }
397 return null;
398 }
399
400 @Override
401 public void engineStore(OutputStream stream, char[] password) {
402 throw new UnsupportedOperationException();
403 }
404
405 @Override
406 public void engineLoad(InputStream stream, char[] password) {
407 if (stream != null && password != null) {
408 throw new UnsupportedOperationException();
409 }
410 }
411 }, null, "native");
412
413 OpenSsl.ensureAvailability();
414 }
415 }
416 }