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 public SSLContextBuilder setTrustStoreProvider(final Provider provider) {
154 this.tsProvider = provider;
155 return this;
156 }
157
158
159
160
161
162
163
164 public SSLContextBuilder setTrustStoreProvider(final String name) throws NoSuchProviderException {
165 this.tsProvider = requireNonNullProvider(name);
166 return this;
167 }
168
169
170
171
172
173
174
175 public SSLContextBuilder setKeyStoreProvider(final Provider provider) {
176 this.ksProvider = provider;
177 return this;
178 }
179
180
181
182
183
184
185
186 public SSLContextBuilder setKeyStoreProvider(final String name) throws NoSuchProviderException {
187 this.ksProvider = requireNonNullProvider(name);
188 return this;
189 }
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206 public SSLContextBuilder setKeyStoreType(final String keyStoreType) {
207 this.keyStoreType = keyStoreType;
208 return this;
209 }
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226 public SSLContextBuilder setKeyManagerFactoryAlgorithm(final String keyManagerFactoryAlgorithm) {
227 this.keyManagerFactoryAlgorithm = keyManagerFactoryAlgorithm;
228 return this;
229 }
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 public SSLContextBuilder setTrustManagerFactoryAlgorithm(final String trustManagerFactoryAlgorithm) {
247 this.trustManagerFactoryAlgorithm = trustManagerFactoryAlgorithm;
248 return this;
249 }
250
251 public SSLContextBuilder setSecureRandom(final SecureRandom secureRandom) {
252 this.secureRandom = secureRandom;
253 return this;
254 }
255
256 public SSLContextBuilder loadTrustMaterial(
257 final KeyStore trustStore,
258 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
259
260 final String alg = trustManagerFactoryAlgorithm == null ?
261 TrustManagerFactory.getDefaultAlgorithm() : trustManagerFactoryAlgorithm;
262
263 final TrustManagerFactory tmFactory = tsProvider == null ?
264 TrustManagerFactory.getInstance(alg) : TrustManagerFactory.getInstance(alg, tsProvider);
265
266 tmFactory.init(trustStore);
267 final TrustManager[] tms = tmFactory.getTrustManagers();
268 if (tms != null) {
269 if (trustStrategy != null) {
270 for (int i = 0; i < tms.length; i++) {
271 final TrustManager tm = tms[i];
272 if (tm instanceof X509TrustManager) {
273 tms[i] = new TrustManagerDelegate((X509TrustManager) tm, trustStrategy);
274 }
275 }
276 }
277 Collections.addAll(this.trustManagers, tms);
278 }
279 return this;
280 }
281
282
283
284
285 public SSLContextBuilder loadTrustMaterial(
286 final Path file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
287 return loadTrustMaterial(file, null);
288 }
289
290
291
292
293 public SSLContextBuilder loadTrustMaterial(
294 final Path file,
295 final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
296 return loadTrustMaterial(file, storePassword, null);
297 }
298
299
300
301
302 public SSLContextBuilder loadTrustMaterial(
303 final Path file,
304 final char[] storePassword,
305 final TrustStrategy trustStrategy,
306 final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
307 Args.notNull(file, "Truststore file");
308 return loadTrustMaterial(loadKeyStore(file, storePassword, openOptions), trustStrategy);
309 }
310
311 public SSLContextBuilder loadTrustMaterial(
312 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
313 return loadTrustMaterial(null, trustStrategy);
314 }
315
316 public SSLContextBuilder loadTrustMaterial(
317 final File file,
318 final char[] storePassword,
319 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
320 Args.notNull(file, "Truststore file");
321 return loadTrustMaterial(file.toPath(), storePassword, trustStrategy);
322 }
323
324 public SSLContextBuilder loadTrustMaterial(
325 final File file,
326 final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
327 return loadTrustMaterial(file, storePassword, null);
328 }
329
330 public SSLContextBuilder loadTrustMaterial(
331 final File file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
332 return loadTrustMaterial(file, null);
333 }
334
335 public SSLContextBuilder loadTrustMaterial(
336 final URL url,
337 final char[] storePassword,
338 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
339 Args.notNull(url, "Truststore URL");
340 return loadTrustMaterial(loadKeyStore(url, storePassword), trustStrategy);
341 }
342
343 public SSLContextBuilder loadTrustMaterial(
344 final URL url,
345 final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
346 return loadTrustMaterial(url, storePassword, null);
347 }
348
349 public SSLContextBuilder loadKeyMaterial(
350 final KeyStore keyStore,
351 final char[] keyPassword,
352 final PrivateKeyStrategy aliasStrategy)
353 throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
354
355 final String alg = keyManagerFactoryAlgorithm == null ?
356 KeyManagerFactory.getDefaultAlgorithm() : keyManagerFactoryAlgorithm;
357
358 final KeyManagerFactory kmFactory = ksProvider == null ?
359 KeyManagerFactory.getInstance(alg) : KeyManagerFactory.getInstance(alg, ksProvider);
360
361 kmFactory.init(keyStore, keyPassword);
362 final KeyManager[] kms = kmFactory.getKeyManagers();
363 if (kms != null) {
364 if (aliasStrategy != null) {
365 for (int i = 0; i < kms.length; i++) {
366 final KeyManager km = kms[i];
367 if (km instanceof X509ExtendedKeyManager) {
368 kms[i] = new KeyManagerDelegate((X509ExtendedKeyManager) km, aliasStrategy);
369 }
370 }
371 }
372 Collections.addAll(keyManagers, kms);
373 }
374 return this;
375 }
376
377
378
379
380 public SSLContextBuilder loadKeyMaterial(
381 final Path file,
382 final char[] storePassword,
383 final char[] keyPassword,
384 final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
385 return loadKeyMaterial(file, storePassword, keyPassword, null, openOptions);
386 }
387
388
389
390
391 public SSLContextBuilder loadKeyMaterial(
392 final Path file,
393 final char[] storePassword,
394 final char[] keyPassword,
395 final PrivateKeyStrategy aliasStrategy,
396 final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
397 Args.notNull(file, "Keystore file");
398 return loadKeyMaterial(loadKeyStore(file, storePassword, openOptions), keyPassword, aliasStrategy);
399 }
400
401 public SSLContextBuilder loadKeyMaterial(
402 final KeyStore keyStore,
403 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
404 return loadKeyMaterial(keyStore, keyPassword, null);
405 }
406
407 public SSLContextBuilder loadKeyMaterial(
408 final File file,
409 final char[] storePassword,
410 final char[] keyPassword,
411 final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
412 Args.notNull(file, "Keystore file");
413 return loadKeyMaterial(file.toPath(), storePassword, keyPassword, aliasStrategy);
414 }
415
416 public SSLContextBuilder loadKeyMaterial(
417 final File file,
418 final char[] storePassword,
419 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
420 return loadKeyMaterial(file, storePassword, keyPassword, null);
421 }
422
423 public SSLContextBuilder loadKeyMaterial(
424 final URL url,
425 final char[] storePassword,
426 final char[] keyPassword,
427 final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
428 Args.notNull(url, "Keystore URL");
429 return loadKeyMaterial(loadKeyStore(url, storePassword), keyPassword, aliasStrategy);
430 }
431
432 public SSLContextBuilder loadKeyMaterial(
433 final URL url,
434 final char[] storePassword,
435 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
436 return loadKeyMaterial(url, storePassword, keyPassword, null);
437 }
438
439 protected void initSSLContext(
440 final SSLContext sslContext,
441 final Collection<KeyManager> keyManagers,
442 final Collection<TrustManager> trustManagers,
443 final SecureRandom secureRandom) throws KeyManagementException {
444 sslContext.init(
445 !keyManagers.isEmpty() ? keyManagers.toArray(EMPTY_KEY_MANAGER_ARRAY) : null,
446 !trustManagers.isEmpty() ? trustManagers.toArray(EMPTY_TRUST_MANAGER_ARRAY) : null,
447 secureRandom);
448 }
449
450 private KeyStore loadKeyStore(final Path file, final char[] password, final OpenOption... openOptions)
451 throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
452 final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
453 try (final InputStream inputStream = Files.newInputStream(file, openOptions)) {
454 keyStore.load(inputStream, password);
455 }
456 return keyStore;
457 }
458
459 private KeyStore loadKeyStore(final URL url, final char[] password)
460 throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
461 final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
462 try (final InputStream inputStream = url.openStream()) {
463 keyStore.load(inputStream, password);
464 }
465 return keyStore;
466 }
467
468 public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException {
469 final SSLContext sslContext;
470 final String protocolStr = this.protocol != null ? this.protocol : TLS;
471 if (this.provider != null) {
472 sslContext = SSLContext.getInstance(protocolStr, this.provider);
473 } else {
474 sslContext = SSLContext.getInstance(protocolStr);
475 }
476 initSSLContext(sslContext, keyManagers, trustManagers, secureRandom);
477 return sslContext;
478 }
479
480 static class TrustManagerDelegate implements X509TrustManager {
481
482 private final X509TrustManager trustManager;
483 private final TrustStrategy trustStrategy;
484
485 TrustManagerDelegate(final X509TrustManager trustManager, final TrustStrategy trustStrategy) {
486 this.trustManager = trustManager;
487 this.trustStrategy = trustStrategy;
488 }
489
490 @Override
491 public void checkClientTrusted(
492 final X509Certificate[] chain, final String authType) throws CertificateException {
493 this.trustManager.checkClientTrusted(chain, authType);
494 }
495
496 @Override
497 public void checkServerTrusted(
498 final X509Certificate[] chain, final String authType) throws CertificateException {
499 if (!this.trustStrategy.isTrusted(chain, authType)) {
500 this.trustManager.checkServerTrusted(chain, authType);
501 }
502 }
503
504 @Override
505 public X509Certificate[] getAcceptedIssuers() {
506 return this.trustManager.getAcceptedIssuers();
507 }
508
509 }
510
511 static class KeyManagerDelegate extends X509ExtendedKeyManager {
512
513 private final X509ExtendedKeyManager keyManager;
514 private final PrivateKeyStrategy aliasStrategy;
515
516 KeyManagerDelegate(final X509ExtendedKeyManager keyManager, final PrivateKeyStrategy aliasStrategy) {
517 this.keyManager = keyManager;
518 this.aliasStrategy = aliasStrategy;
519 }
520
521 @Override
522 public String[] getClientAliases(
523 final String keyType, final Principal[] issuers) {
524 return this.keyManager.getClientAliases(keyType, issuers);
525 }
526
527 public Map<String, PrivateKeyDetails> getClientAliasMap(
528 final String[] keyTypes, final Principal[] issuers) {
529 final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
530 for (final String keyType: keyTypes) {
531 putPrivateKeyDetails(validAliases, keyType, this.keyManager.getClientAliases(keyType, issuers));
532 }
533 return validAliases;
534 }
535
536 public Map<String, PrivateKeyDetails> getServerAliasMap(
537 final String keyType, final Principal[] issuers) {
538 final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
539 putPrivateKeyDetails(validAliases, keyType, this.keyManager.getServerAliases(keyType, issuers));
540 return validAliases;
541 }
542
543 private void putPrivateKeyDetails(final Map<String, PrivateKeyDetails> validAliases, final String keyType,
544 final String[] aliases) {
545 if (aliases != null) {
546 for (final String alias: aliases) {
547 validAliases.put(alias, new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias)));
548 }
549 }
550 }
551
552 @Override
553 public String chooseClientAlias(
554 final String[] keyTypes, final Principal[] issuers, final Socket socket) {
555 final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
556 return this.aliasStrategy.chooseAlias(validAliases,
557 socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
558 }
559
560 @Override
561 public String[] getServerAliases(
562 final String keyType, final Principal[] issuers) {
563 return this.keyManager.getServerAliases(keyType, issuers);
564 }
565
566 @Override
567 public String chooseServerAlias(
568 final String keyType, final Principal[] issuers, final Socket socket) {
569 final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
570 return this.aliasStrategy.chooseAlias(validAliases,
571 socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
572 }
573
574 @Override
575 public X509Certificate[] getCertificateChain(final String alias) {
576 return this.keyManager.getCertificateChain(alias);
577 }
578
579 @Override
580 public PrivateKey getPrivateKey(final String alias) {
581 return this.keyManager.getPrivateKey(alias);
582 }
583
584 @Override
585 public String chooseEngineClientAlias(
586 final String[] keyTypes, final Principal[] issuers, final SSLEngine sslEngine) {
587 final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
588 return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
589 }
590
591 @Override
592 public String chooseEngineServerAlias(
593 final String keyType, final Principal[] issuers, final SSLEngine sslEngine) {
594 final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
595 return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
596 }
597
598 }
599
600 private Provider requireNonNullProvider(final String name) throws NoSuchProviderException {
601 final Provider provider = Security.getProvider(name);
602 if (provider == null) {
603 throw new NoSuchProviderException(name);
604 }
605 return provider;
606 }
607
608 @Override
609 public String toString() {
610 return "[provider=" + provider + ", protocol=" + protocol + ", keyStoreType=" + keyStoreType
611 + ", keyManagerFactoryAlgorithm=" + keyManagerFactoryAlgorithm + ", keyManagers=" + keyManagers
612 + ", trustManagerFactoryAlgorithm=" + trustManagerFactoryAlgorithm + ", trustManagers=" + trustManagers
613 + ", secureRandom=" + secureRandom + "]";
614 }
615
616 }