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.FileInputStream;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.net.Socket;
35 import java.net.URL;
36 import java.security.KeyManagementException;
37 import java.security.KeyStore;
38 import java.security.KeyStoreException;
39 import java.security.NoSuchAlgorithmException;
40 import java.security.Principal;
41 import java.security.PrivateKey;
42 import java.security.Provider;
43 import java.security.SecureRandom;
44 import java.security.Security;
45 import java.security.UnrecoverableKeyException;
46 import java.security.cert.CertificateException;
47 import java.security.cert.X509Certificate;
48 import java.util.Collection;
49 import java.util.Collections;
50 import java.util.HashMap;
51 import java.util.LinkedHashSet;
52 import java.util.Map;
53 import java.util.Set;
54
55 import javax.net.ssl.KeyManager;
56 import javax.net.ssl.KeyManagerFactory;
57 import javax.net.ssl.SSLContext;
58 import javax.net.ssl.SSLEngine;
59 import javax.net.ssl.SSLSocket;
60 import javax.net.ssl.TrustManager;
61 import javax.net.ssl.TrustManagerFactory;
62 import javax.net.ssl.X509ExtendedKeyManager;
63 import javax.net.ssl.X509TrustManager;
64
65 import org.apache.hc.core5.util.Args;
66
67
68
69
70
71
72
73
74
75
76
77
78
79 public class SSLContextBuilder {
80
81 static final String TLS = "TLS";
82
83 private String protocol;
84 private final Set<KeyManager> keyManagers;
85 private String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
86 private String keyStoreType = KeyStore.getDefaultType();
87 private final Set<TrustManager> trustManagers;
88 private String trustManagerFactoryAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
89 private SecureRandom secureRandom;
90 private Provider provider;
91
92
93
94
95 private static final KeyManager[] EMPTY_KEY_MANAGER_ARRAY = new KeyManager[0];
96
97
98
99
100 private static final TrustManager[] EMPTY_TRUST_MANAGER_ARRAY = new TrustManager[0];
101
102
103
104 public static SSLContextBuilder create() {
105 return new SSLContextBuilder();
106 }
107
108 public SSLContextBuilder() {
109 super();
110 this.keyManagers = new LinkedHashSet<>();
111 this.trustManagers = new LinkedHashSet<>();
112 }
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 public SSLContextBuilder setProtocol(final String protocol) {
129 this.protocol = protocol;
130 return this;
131 }
132
133 public SSLContextBuilder setProvider(final Provider provider) {
134 this.provider = provider;
135 return this;
136 }
137
138 public SSLContextBuilder setProvider(final String name) {
139 this.provider = Security.getProvider(name);
140 return this;
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158 public SSLContextBuilder setKeyStoreType(final String keyStoreType) {
159 this.keyStoreType = keyStoreType;
160 return this;
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178 public SSLContextBuilder setKeyManagerFactoryAlgorithm(final String keyManagerFactoryAlgorithm) {
179 this.keyManagerFactoryAlgorithm = keyManagerFactoryAlgorithm;
180 return this;
181 }
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198 public SSLContextBuilder setTrustManagerFactoryAlgorithm(final String trustManagerFactoryAlgorithm) {
199 this.trustManagerFactoryAlgorithm = trustManagerFactoryAlgorithm;
200 return this;
201 }
202
203 public SSLContextBuilder setSecureRandom(final SecureRandom secureRandom) {
204 this.secureRandom = secureRandom;
205 return this;
206 }
207
208 public SSLContextBuilder loadTrustMaterial(
209 final KeyStore truststore,
210 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
211 final TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
212 trustManagerFactoryAlgorithm == null ? TrustManagerFactory.getDefaultAlgorithm()
213 : trustManagerFactoryAlgorithm);
214 tmfactory.init(truststore);
215 final TrustManager[] tms = tmfactory.getTrustManagers();
216 if (tms != null) {
217 if (trustStrategy != null) {
218 for (int i = 0; i < tms.length; i++) {
219 final TrustManager tm = tms[i];
220 if (tm instanceof X509TrustManager) {
221 tms[i] = new TrustManagerDelegate(
222 (X509TrustManager) tm, trustStrategy);
223 }
224 }
225 }
226 Collections.addAll(this.trustManagers, tms);
227 }
228 return this;
229 }
230
231 public SSLContextBuilder loadTrustMaterial(
232 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException {
233 return loadTrustMaterial(null, trustStrategy);
234 }
235
236 public SSLContextBuilder loadTrustMaterial(
237 final File file,
238 final char[] storePassword,
239 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
240 Args.notNull(file, "Truststore file");
241 final KeyStore trustStore = KeyStore.getInstance(keyStoreType);
242 try (final FileInputStream inStream = new FileInputStream(file)) {
243 trustStore.load(inStream, storePassword);
244 }
245 return loadTrustMaterial(trustStore, trustStrategy);
246 }
247
248 public SSLContextBuilder loadTrustMaterial(
249 final File file,
250 final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
251 return loadTrustMaterial(file, storePassword, null);
252 }
253
254 public SSLContextBuilder loadTrustMaterial(
255 final File file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
256 return loadTrustMaterial(file, null);
257 }
258
259 public SSLContextBuilder loadTrustMaterial(
260 final URL url,
261 final char[] storePassword,
262 final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
263 Args.notNull(url, "Truststore URL");
264 final KeyStore trustStore = KeyStore.getInstance(keyStoreType);
265 try (final InputStream inStream = url.openStream()) {
266 trustStore.load(inStream, storePassword);
267 }
268 return loadTrustMaterial(trustStore, trustStrategy);
269 }
270
271 public SSLContextBuilder loadTrustMaterial(
272 final URL url,
273 final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
274 return loadTrustMaterial(url, storePassword, null);
275 }
276
277 public SSLContextBuilder loadKeyMaterial(
278 final KeyStore keystore,
279 final char[] keyPassword,
280 final PrivateKeyStrategy aliasStrategy)
281 throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
282 final KeyManagerFactory kmfactory = KeyManagerFactory
283 .getInstance(keyManagerFactoryAlgorithm == null ? KeyManagerFactory.getDefaultAlgorithm()
284 : keyManagerFactoryAlgorithm);
285 kmfactory.init(keystore, keyPassword);
286 final KeyManager[] kms = kmfactory.getKeyManagers();
287 if (kms != null) {
288 if (aliasStrategy != null) {
289 for (int i = 0; i < kms.length; i++) {
290 final KeyManager km = kms[i];
291 if (km instanceof X509ExtendedKeyManager) {
292 kms[i] = new KeyManagerDelegate((X509ExtendedKeyManager) km, aliasStrategy);
293 }
294 }
295 }
296 Collections.addAll(keyManagers, kms);
297 }
298 return this;
299 }
300
301 public SSLContextBuilder loadKeyMaterial(
302 final KeyStore keystore,
303 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
304 return loadKeyMaterial(keystore, keyPassword, null);
305 }
306
307 public SSLContextBuilder loadKeyMaterial(
308 final File file,
309 final char[] storePassword,
310 final char[] keyPassword,
311 final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
312 Args.notNull(file, "Keystore file");
313 final KeyStore identityStore = KeyStore.getInstance(keyStoreType);
314 try (final FileInputStream inStream = new FileInputStream(file)) {
315 identityStore.load(inStream, storePassword);
316 }
317 return loadKeyMaterial(identityStore, keyPassword, aliasStrategy);
318 }
319
320 public SSLContextBuilder loadKeyMaterial(
321 final File file,
322 final char[] storePassword,
323 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
324 return loadKeyMaterial(file, storePassword, keyPassword, null);
325 }
326
327 public SSLContextBuilder loadKeyMaterial(
328 final URL url,
329 final char[] storePassword,
330 final char[] keyPassword,
331 final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
332 Args.notNull(url, "Keystore URL");
333 final KeyStore identityStore = KeyStore.getInstance(keyStoreType);
334 try (final InputStream inStream = url.openStream()) {
335 identityStore.load(inStream, storePassword);
336 }
337 return loadKeyMaterial(identityStore, keyPassword, aliasStrategy);
338 }
339
340 public SSLContextBuilder loadKeyMaterial(
341 final URL url,
342 final char[] storePassword,
343 final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
344 return loadKeyMaterial(url, storePassword, keyPassword, null);
345 }
346
347 protected void initSSLContext(
348 final SSLContext sslContext,
349 final Collection<KeyManager> keyManagers,
350 final Collection<TrustManager> trustManagers,
351 final SecureRandom secureRandom) throws KeyManagementException {
352 sslContext.init(
353 !keyManagers.isEmpty() ? keyManagers.toArray(EMPTY_KEY_MANAGER_ARRAY) : null,
354 !trustManagers.isEmpty() ? trustManagers.toArray(EMPTY_TRUST_MANAGER_ARRAY) : null,
355 secureRandom);
356 }
357
358 public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException {
359 final SSLContext sslContext;
360 final String protocolStr = this.protocol != null ? this.protocol : TLS;
361 if (this.provider != null) {
362 sslContext = SSLContext.getInstance(protocolStr, this.provider);
363 } else {
364 sslContext = SSLContext.getInstance(protocolStr);
365 }
366 initSSLContext(sslContext, keyManagers, trustManagers, secureRandom);
367 return sslContext;
368 }
369
370 static class TrustManagerDelegate implements X509TrustManager {
371
372 private final X509TrustManager trustManager;
373 private final TrustStrategy trustStrategy;
374
375 TrustManagerDelegate(final X509TrustManager trustManager, final TrustStrategy trustStrategy) {
376 super();
377 this.trustManager = trustManager;
378 this.trustStrategy = trustStrategy;
379 }
380
381 @Override
382 public void checkClientTrusted(
383 final X509Certificate[] chain, final String authType) throws CertificateException {
384 this.trustManager.checkClientTrusted(chain, authType);
385 }
386
387 @Override
388 public void checkServerTrusted(
389 final X509Certificate[] chain, final String authType) throws CertificateException {
390 if (!this.trustStrategy.isTrusted(chain, authType)) {
391 this.trustManager.checkServerTrusted(chain, authType);
392 }
393 }
394
395 @Override
396 public X509Certificate[] getAcceptedIssuers() {
397 return this.trustManager.getAcceptedIssuers();
398 }
399
400 }
401
402 static class KeyManagerDelegate extends X509ExtendedKeyManager {
403
404 private final X509ExtendedKeyManager keyManager;
405 private final PrivateKeyStrategy aliasStrategy;
406
407 KeyManagerDelegate(final X509ExtendedKeyManager keyManager, final PrivateKeyStrategy aliasStrategy) {
408 super();
409 this.keyManager = keyManager;
410 this.aliasStrategy = aliasStrategy;
411 }
412
413 @Override
414 public String[] getClientAliases(
415 final String keyType, final Principal[] issuers) {
416 return this.keyManager.getClientAliases(keyType, issuers);
417 }
418
419 public Map<String, PrivateKeyDetails> getClientAliasMap(
420 final String[] keyTypes, final Principal[] issuers) {
421 final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
422 for (final String keyType: keyTypes) {
423 final String[] aliases = this.keyManager.getClientAliases(keyType, issuers);
424 if (aliases != null) {
425 for (final String alias: aliases) {
426 validAliases.put(alias,
427 new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias)));
428 }
429 }
430 }
431 return validAliases;
432 }
433
434 public Map<String, PrivateKeyDetails> getServerAliasMap(
435 final String keyType, final Principal[] issuers) {
436 final Map<String, PrivateKeyDetails> validAliases = new HashMap<>();
437 final String[] aliases = this.keyManager.getServerAliases(keyType, issuers);
438 if (aliases != null) {
439 for (final String alias: aliases) {
440 validAliases.put(alias,
441 new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias)));
442 }
443 }
444 return validAliases;
445 }
446
447 @Override
448 public String chooseClientAlias(
449 final String[] keyTypes, final Principal[] issuers, final Socket socket) {
450 final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
451 return this.aliasStrategy.chooseAlias(validAliases,
452 socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
453 }
454
455 @Override
456 public String[] getServerAliases(
457 final String keyType, final Principal[] issuers) {
458 return this.keyManager.getServerAliases(keyType, issuers);
459 }
460
461 @Override
462 public String chooseServerAlias(
463 final String keyType, final Principal[] issuers, final Socket socket) {
464 final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
465 return this.aliasStrategy.chooseAlias(validAliases,
466 socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null);
467 }
468
469 @Override
470 public X509Certificate[] getCertificateChain(final String alias) {
471 return this.keyManager.getCertificateChain(alias);
472 }
473
474 @Override
475 public PrivateKey getPrivateKey(final String alias) {
476 return this.keyManager.getPrivateKey(alias);
477 }
478
479 @Override
480 public String chooseEngineClientAlias(
481 final String[] keyTypes, final Principal[] issuers, final SSLEngine sslEngine) {
482 final Map<String, PrivateKeyDetails> validAliases = getClientAliasMap(keyTypes, issuers);
483 return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
484 }
485
486 @Override
487 public String chooseEngineServerAlias(
488 final String keyType, final Principal[] issuers, final SSLEngine sslEngine) {
489 final Map<String, PrivateKeyDetails> validAliases = getServerAliasMap(keyType, issuers);
490 return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters());
491 }
492
493 }
494
495 @Override
496 public String toString() {
497 return "[provider=" + provider + ", protocol=" + protocol + ", keyStoreType=" + keyStoreType
498 + ", keyManagerFactoryAlgorithm=" + keyManagerFactoryAlgorithm + ", keyManagers=" + keyManagers
499 + ", trustManagerFactoryAlgorithm=" + trustManagerFactoryAlgorithm + ", trustManagers=" + trustManagers
500 + ", secureRandom=" + secureRandom + "]";
501 }
502 }