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