1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.ssl;
29
30 import java.io.File;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.net.Socket;
34 import java.net.URL;
35 import java.nio.file.Files;
36 import java.nio.file.OpenOption;
37 import java.nio.file.Path;
38 import java.security.KeyManagementException;
39 import java.security.KeyStore;
40 import java.security.KeyStoreException;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.NoSuchProviderException;
43 import java.security.Principal;
44 import java.security.PrivateKey;
45 import java.security.Provider;
46 import java.security.SecureRandom;
47 import java.security.Security;
48 import java.security.UnrecoverableKeyException;
49 import java.security.cert.CertificateException;
50 import java.security.cert.X509Certificate;
51 import java.util.Collection;
52 import java.util.Collections;
53 import java.util.HashMap;
54 import java.util.LinkedHashSet;
55 import java.util.Map;
56 import java.util.Set;
57
58 import javax.net.ssl.KeyManager;
59 import javax.net.ssl.KeyManagerFactory;
60 import javax.net.ssl.SSLContext;
61 import javax.net.ssl.SSLEngine;
62 import javax.net.ssl.SSLSocket;
63 import javax.net.ssl.TrustManager;
64 import javax.net.ssl.TrustManagerFactory;
65 import javax.net.ssl.X509ExtendedKeyManager;
66 import javax.net.ssl.X509TrustManager;
67
68 import org.apache.hc.core5.util.Args;
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public class SSLContextBuilder {
83
84 static final String TLS = "TLS";
85
86 private String protocol;
87 private final Set<KeyManager> keyManagers;
88 private String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
89 private String keyStoreType = KeyStore.getDefaultType();
90 private final Set<TrustManager> trustManagers;
91 private String trustManagerFactoryAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
92 private SecureRandom secureRandom;
93 private Provider provider;
94 private Provider tsProvider;
95 private Provider ksProvider;
96
97
98
99
100 private static final KeyManager[] EMPTY_KEY_MANAGER_ARRAY = {};
101
102
103
104
105 private static final TrustManager[] EMPTY_TRUST_MANAGER_ARRAY = {};
106
107
108
109 public static SSLContextBuilder create() {
110 return new SSLContextBuilder();
111 }
112
113 public SSLContextBuilder() {
114 this.keyManagers = new LinkedHashSet<>();
115 this.trustManagers = new LinkedHashSet<>();
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 public SSLContextBuilder setProtocol(final String protocol) {
133 this.protocol = protocol;
134 return this;
135 }
136
137 public SSLContextBuilder setProvider(final Provider provider) {
138 this.provider = provider;
139 return this;
140 }
141
142 public SSLContextBuilder setProvider(final String name) {
143 this.provider = Security.getProvider(name);
144 return this;
145 }
146
147
148
149
150
151
152
153
154 public SSLContextBuilder setTrustStoreProvider(final Provider provider) {
155 this.tsProvider = provider;
156 return this;
157 }
158
159
160
161
162
163
164
165
166
167 public SSLContextBuilder setTrustStoreProvider(final String name) throws NoSuchProviderException {
168 this.tsProvider = requireNonNullProvider(name);
169 return this;
170 }
171
172
173
174
175
176
177
178
179 public SSLContextBuilder setKeyStoreProvider(final Provider provider) {
180 this.ksProvider = provider;
181 return this;
182 }
183
184
185
186
187
188
189
190
191
192 public SSLContextBuilder setKeyStoreProvider(final String name) throws NoSuchProviderException {
193 this.ksProvider = requireNonNullProvider(name);
194 return this;
195 }
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212 public SSLContextBuilder setKeyStoreType(final String keyStoreType) {
213 this.keyStoreType = keyStoreType;
214 return this;
215 }
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232 public SSLContextBuilder setKeyManagerFactoryAlgorithm(final String keyManagerFactoryAlgorithm) {
233 this.keyManagerFactoryAlgorithm = keyManagerFactoryAlgorithm;
234 return this;
235 }
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252 public SSLContextBuilder setTrustManagerFactoryAlgorithm(final String trustManagerFactoryAlgorithm) {
253 this.trustManagerFactoryAlgorithm = trustManagerFactoryAlgorithm;
254 return this;
255 }
256
257 public SSLContextBuilder setSecureRandom(final SecureRandom secureRandom) {
258 this.secureRandom = secureRandom;
259 return this;
260 }
261
262
263
264
265
266
267
268
269
270 public SSLContextBuilder loadTrustMaterial(
271 final KeyStore trustStore,
272 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
273
274 final String alg = trustManagerFactoryAlgorithm == null ?
275 TrustManagerFactory.getDefaultAlgorithm() : trustManagerFactoryAlgorithm;
276
277 final TrustManagerFactory tmFactory = tsProvider == null ?
278 TrustManagerFactory.getInstance(alg) : TrustManagerFactory.getInstance(alg, tsProvider);
279
280 tmFactory.init(trustStore);
281 final TrustManager[] tms = tmFactory.getTrustManagers();
282 if (tms != null) {
283 if (trustStrategy != null) {
284 for (int i = 0; i < tms.length; i++) {
285 final TrustManager tm = tms[i];
286 if (tm instanceof X509TrustManager) {
287 tms[i] = new TrustManagerDelegate((X509TrustManager) tm, trustStrategy);
288 }
289 }
290 }
291 Collections.addAll(this.trustManagers, tms);
292 }
293 return this;
294 }
295
296
297
298
299
300
301
302
303
304 public SSLContextBuilder loadTrustMaterial(
305 final Path file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
306 return loadTrustMaterial(file, null);
307 }
308
309
310
311
312
313
314
315
316
317 public SSLContextBuilder loadTrustMaterial(
318 final Path file,
319 final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
320 return loadTrustMaterial(file, storePassword, null);
321 }
322
323
324
325
326
327
328
329
330
331 public SSLContextBuilder loadTrustMaterial(
332 final Path file,
333 final char[] storePassword,
334 final TrustStrategy trustStrategy,
335 final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
336 Args.notNull(file, "Truststore file");
337 return loadTrustMaterial(loadKeyStore(file, storePassword, openOptions), trustStrategy);
338 }
339
340 public SSLContextBuilder loadTrustMaterial(
341 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
342 return loadTrustMaterial(null, trustStrategy);
343 }
344
345 public SSLContextBuilder loadTrustMaterial(
346 final File file,
347 final char[] storePassword,
348 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
349 Args.notNull(file, "Truststore file");
350 return loadTrustMaterial(file.toPath(), storePassword, trustStrategy);
351 }
352
353 public SSLContextBuilder loadTrustMaterial(
354 final File file,
355 final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
356 return loadTrustMaterial(file, storePassword, null);
357 }
358
359 public SSLContextBuilder loadTrustMaterial(
360 final File file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
361 return loadTrustMaterial(file, null);
362 }
363
364 public SSLContextBuilder loadTrustMaterial(
365 final URL url,
366 final char[] storePassword,
367 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
368 Args.notNull(url, "Truststore URL");
369 return loadTrustMaterial(loadKeyStore(url, storePassword), trustStrategy);
370 }
371
372 public SSLContextBuilder loadTrustMaterial(
373 final URL url,
374 final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
375 return loadTrustMaterial(url, storePassword, null);
376 }
377
378 public SSLContextBuilder loadKeyMaterial(
379 final KeyStore keyStore,
380 final char[] keyPassword,
381 final PrivateKeyStrategy aliasStrategy)
382 throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
383
384 final String alg = keyManagerFactoryAlgorithm == null ?
385 KeyManagerFactory.getDefaultAlgorithm() : keyManagerFactoryAlgorithm;
386
387 final KeyManagerFactory kmFactory = ksProvider == null ?
388 KeyManagerFactory.getInstance(alg) : KeyManagerFactory.getInstance(alg, ksProvider);
389
390 kmFactory.init(keyStore, keyPassword);
391 final KeyManager[] kms = kmFactory.getKeyManagers();
392 if (kms != null) {
393 if (aliasStrategy != null) {
394 for (int i = 0; i < kms.length; i++) {
395 final KeyManager km = kms[i];
396 if (km instanceof X509ExtendedKeyManager) {
397 kms[i] = new KeyManagerDelegate((X509ExtendedKeyManager) km, aliasStrategy);
398 }
399 }
400 }
401 Collections.addAll(keyManagers, kms);
402 }
403 return this;
404 }
405
406
407
408
409
410
411
412
413
414
415 public SSLContextBuilder loadKeyMaterial(
416 final Path file,
417 final char[] storePassword,
418 final char[] keyPassword,
419 final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
420 return loadKeyMaterial(file, storePassword, keyPassword, null, openOptions);
421 }
422
423
424
425
426
427
428
429
430
431
432 public SSLContextBuilder loadKeyMaterial(
433 final Path file,
434 final char[] storePassword,
435 final char[] keyPassword,
436 final PrivateKeyStrategy aliasStrategy,
437 final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
438 Args.notNull(file, "Keystore file");
439 return loadKeyMaterial(loadKeyStore(file, storePassword, openOptions), keyPassword, aliasStrategy);
440 }
441
442 public SSLContextBuilder loadKeyMaterial(
443 final KeyStore keyStore,
444 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
445 return loadKeyMaterial(keyStore, keyPassword, null);
446 }
447
448 public SSLContextBuilder loadKeyMaterial(
449 final File file,
450 final char[] storePassword,
451 final char[] keyPassword,
452 final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
453 Args.notNull(file, "Keystore file");
454 return loadKeyMaterial(file.toPath(), storePassword, keyPassword, aliasStrategy);
455 }
456
457 public SSLContextBuilder loadKeyMaterial(
458 final File file,
459 final char[] storePassword,
460 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
461 return loadKeyMaterial(file, storePassword, keyPassword, null);
462 }
463
464 public SSLContextBuilder loadKeyMaterial(
465 final URL url,
466 final char[] storePassword,
467 final char[] keyPassword,
468 final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
469 Args.notNull(url, "Keystore URL");
470 return loadKeyMaterial(loadKeyStore(url, storePassword), keyPassword, aliasStrategy);
471 }
472
473 public SSLContextBuilder loadKeyMaterial(
474 final URL url,
475 final char[] storePassword,
476 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
477 return loadKeyMaterial(url, storePassword, keyPassword, null);
478 }
479
480
481
482
483 protected void initSSLContext(
484 final SSLContext sslContext,
485 final Collection<KeyManager> keyManagers,
486 final Collection<TrustManager> trustManagers,
487 final SecureRandom secureRandom) throws KeyManagementException {
488 sslContext.init(
489 !keyManagers.isEmpty() ? keyManagers.toArray(EMPTY_KEY_MANAGER_ARRAY) : null,
490 !trustManagers.isEmpty() ? trustManagers.toArray(EMPTY_TRUST_MANAGER_ARRAY) : null,
491 secureRandom);
492 }
493
494
495
496
497
498
499
500 private KeyStore loadKeyStore(final Path file, final char[] password, final OpenOption... openOptions)
501 throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
502 final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
503 try (final InputStream inputStream = Files.newInputStream(file, openOptions)) {
504 keyStore.load(inputStream, password);
505 }
506 return keyStore;
507 }
508
509 private KeyStore loadKeyStore(final URL url, final char[] password)
510 throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
511 final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
512 try (final InputStream inputStream = url.openStream()) {
513 keyStore.load(inputStream, password);
514 }
515 return keyStore;
516 }
517
518
519
520
521
522
523
524
525 public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException {
526 final SSLContext sslContext;
527 final String protocolStr = this.protocol != null ? this.protocol : TLS;
528 if (this.provider != null) {
529 sslContext = SSLContext.getInstance(protocolStr, this.provider);
530 } else {
531 sslContext = SSLContext.getInstance(protocolStr);
532 }
533 initSSLContext(sslContext, keyManagers, trustManagers, secureRandom);
534 return sslContext;
535 }
536
537 static class TrustManagerDelegate implements X509TrustManager {
538
539 private final X509TrustManager trustManager;
540 private final TrustStrategy trustStrategy;
541
542 TrustManagerDelegate(final X509TrustManager trustManager, final TrustStrategy trustStrategy) {
543 this.trustManager = trustManager;
544 this.trustStrategy = trustStrategy;
545 }
546
547 @Override
548 public void checkClientTrusted(
549 final X509Certificate[] chain, final String authType) throws CertificateException {
550 this.trustManager.checkClientTrusted(chain, authType);
551 }
552
553 @Override
554 public void checkServerTrusted(
555 final X509Certificate[] chain, final String authType) throws CertificateException {
556 if (!this.trustStrategy.isTrusted(chain, authType)) {
557 this.trustManager.checkServerTrusted(chain, authType);
558 }
559 }
560
561 @Override
562 public X509Certificate[] getAcceptedIssuers() {
563 return this.trustManager.getAcceptedIssuers();
564 }
565
566 }
567
568 static class KeyManagerDelegate extends X509ExtendedKeyManager {
569
570 private final X509ExtendedKeyManager keyManager;
571 private final PrivateKeyStrategy aliasStrategy;
572
573 KeyManagerDelegate(final X509ExtendedKeyManager keyManager, final PrivateKeyStrategy aliasStrategy) {
574 this.keyManager = keyManager;
575 this.aliasStrategy = aliasStrategy;
576 }
577
578 @Override
579 public String[] getClientAliases(
580 final String keyType, final Principal[] issuers) {
581 return this.keyManager.getClientAliases(keyType, issuers);
582 }
583
584 public Map<String, PrivateKeyDetails> getClientAliasMap(
585 final String[] keyTypes, final Principal[] issuers) {
586 final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
587 for (final String keyType: keyTypes) {
588 putPrivateKeyDetails(validAliases, keyType, this.keyManager.getClientAliases(keyType, issuers));
589 }
590 return validAliases;
591 }
592
593 public Map<String, PrivateKeyDetails> getServerAliasMap(
594 final String keyType, final Principal[] issuers) {
595 final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
596 putPrivateKeyDetails(validAliases, keyType, this.keyManager.getServerAliases(keyType, issuers));
597 return validAliases;
598 }
599
600 private void putPrivateKeyDetails(final Map<String, PrivateKeyDetails> validAliases, final String keyType,
601 final String[] aliases) {
602 if (aliases != null) {
603 for (final String alias: aliases) {
604 validAliases.put(alias, new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias)));
605 }
606 }
607 }
608
609 @Override
610 public String chooseClientAlias(
611 final String[] keyTypes, final Principal[] issuers, final Socket socket) {
612 final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
613 return this.aliasStrategy.chooseAlias(validAliases,
614 socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
615 }
616
617 @Override
618 public String[] getServerAliases(
619 final String keyType, final Principal[] issuers) {
620 return this.keyManager.getServerAliases(keyType, issuers);
621 }
622
623 @Override
624 public String chooseServerAlias(
625 final String keyType, final Principal[] issuers, final Socket socket) {
626 final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
627 return this.aliasStrategy.chooseAlias(validAliases,
628 socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
629 }
630
631 @Override
632 public X509Certificate[] getCertificateChain(final String alias) {
633 return this.keyManager.getCertificateChain(alias);
634 }
635
636 @Override
637 public PrivateKey getPrivateKey(final String alias) {
638 return this.keyManager.getPrivateKey(alias);
639 }
640
641 @Override
642 public String chooseEngineClientAlias(
643 final String[] keyTypes, final Principal[] issuers, final SSLEngine sslEngine) {
644 final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
645 return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
646 }
647
648 @Override
649 public String chooseEngineServerAlias(
650 final String keyType, final Principal[] issuers, final SSLEngine sslEngine) {
651 final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
652 return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
653 }
654
655 }
656
657
658
659
660 private Provider requireNonNullProvider(final String name) throws NoSuchProviderException {
661 final Provider provider = Security.getProvider(name);
662 if (provider == null) {
663 throw new NoSuchProviderException(name);
664 }
665 return provider;
666 }
667
668 @Override
669 public String toString() {
670 return "[provider=" + provider + ", protocol=" + protocol + ", keyStoreType=" + keyStoreType
671 + ", keyManagerFactoryAlgorithm=" + keyManagerFactoryAlgorithm + ", keyManagers=" + keyManagers
672 + ", trustManagerFactoryAlgorithm=" + trustManagerFactoryAlgorithm + ", trustManagers=" + trustManagers
673 + ", secureRandom=" + secureRandom + "]";
674 }
675
676 }