1 /*
2 * Copyright 2015 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16
17 package io.netty.handler.ssl;
18
19 import io.netty.handler.ssl.util.KeyManagerFactoryWrapper;
20 import io.netty.handler.ssl.util.TrustManagerFactoryWrapper;
21 import io.netty.util.internal.UnstableApi;
22
23 import javax.net.ssl.KeyManager;
24 import javax.net.ssl.KeyManagerFactory;
25 import javax.net.ssl.SSLEngine;
26 import javax.net.ssl.SSLException;
27 import javax.net.ssl.TrustManager;
28 import javax.net.ssl.TrustManagerFactory;
29 import java.io.File;
30 import java.io.InputStream;
31 import java.security.KeyStore;
32 import java.security.PrivateKey;
33 import java.security.Provider;
34 import java.security.SecureRandom;
35 import java.security.cert.X509Certificate;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40
41 import static io.netty.util.internal.EmptyArrays.EMPTY_STRINGS;
42 import static io.netty.util.internal.EmptyArrays.EMPTY_X509_CERTIFICATES;
43 import static io.netty.util.internal.ObjectUtil.checkNotNull;
44 import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE;
45 import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
46
47 /**
48 * Builder for configuring a new SslContext for creation.
49 */
50 public final class SslContextBuilder {
51 @SuppressWarnings("rawtypes")
52 private static final Map.Entry[] EMPTY_ENTRIES = new Map.Entry[0];
53
54 /**
55 * Creates a builder for new client-side {@link SslContext}.
56 */
57 public static SslContextBuilder forClient() {
58 return new SslContextBuilder(false);
59 }
60
61 /**
62 * Creates a builder for new server-side {@link SslContext}.
63 *
64 * @param keyCertChainFile an X.509 certificate chain file in PEM format
65 * @param keyFile a PKCS#8 private key file in PEM format
66 * @see #keyManager(File, File)
67 */
68 public static SslContextBuilder forServer(File keyCertChainFile, File keyFile) {
69 return new SslContextBuilder(true).keyManager(keyCertChainFile, keyFile);
70 }
71
72 /**
73 * Creates a builder for new server-side {@link SslContext}.
74 *
75 * @param keyCertChainInputStream an input stream for an X.509 certificate chain in PEM format. The caller is
76 * responsible for calling {@link InputStream#close()} after {@link #build()}
77 * has been called.
78 * @param keyInputStream an input stream for a PKCS#8 private key in PEM format. The caller is
79 * responsible for calling {@link InputStream#close()} after {@link #build()}
80 * has been called.
81 *
82 * @see #keyManager(InputStream, InputStream)
83 */
84 public static SslContextBuilder forServer(InputStream keyCertChainInputStream, InputStream keyInputStream) {
85 return new SslContextBuilder(true).keyManager(keyCertChainInputStream, keyInputStream);
86 }
87
88 /**
89 * Creates a builder for new server-side {@link SslContext}.
90 *
91 * @param key a PKCS#8 private key
92 * @param keyCertChain the X.509 certificate chain
93 * @see #keyManager(PrivateKey, X509Certificate[])
94 */
95 public static SslContextBuilder forServer(PrivateKey key, X509Certificate... keyCertChain) {
96 return new SslContextBuilder(true).keyManager(key, keyCertChain);
97 }
98
99 /**
100 * Creates a builder for new server-side {@link SslContext}.
101 *
102 * @param key a PKCS#8 private key
103 * @param keyCertChain the X.509 certificate chain
104 * @see #keyManager(PrivateKey, X509Certificate[])
105 */
106 public static SslContextBuilder forServer(PrivateKey key, Iterable<? extends X509Certificate> keyCertChain) {
107 return forServer(key, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
108 }
109
110 /**
111 * Creates a builder for new server-side {@link SslContext}.
112 *
113 * @param keyCertChainFile an X.509 certificate chain file in PEM format
114 * @param keyFile a PKCS#8 private key file in PEM format
115 * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not
116 * password-protected
117 * @see #keyManager(File, File, String)
118 */
119 public static SslContextBuilder forServer(
120 File keyCertChainFile, File keyFile, String keyPassword) {
121 return new SslContextBuilder(true).keyManager(keyCertChainFile, keyFile, keyPassword);
122 }
123
124 /**
125 * Creates a builder for new server-side {@link SslContext}.
126 *
127 * @param keyCertChainInputStream an input stream for an X.509 certificate chain in PEM format. The caller is
128 * responsible for calling {@link InputStream#close()} after {@link #build()}
129 * has been called.
130 * @param keyInputStream an input stream for a PKCS#8 private key in PEM format. The caller is
131 * responsible for calling {@link InputStream#close()} after {@link #build()}
132 * has been called.
133 * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not
134 * password-protected
135 * @see #keyManager(InputStream, InputStream, String)
136 */
137 public static SslContextBuilder forServer(
138 InputStream keyCertChainInputStream, InputStream keyInputStream, String keyPassword) {
139 return new SslContextBuilder(true).keyManager(keyCertChainInputStream, keyInputStream, keyPassword);
140 }
141
142 /**
143 * Creates a builder for new server-side {@link SslContext}.
144 *
145 * @param key a PKCS#8 private key
146 * @param keyCertChain the X.509 certificate chain
147 * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not
148 * password-protected
149 * @see #keyManager(File, File, String)
150 */
151 public static SslContextBuilder forServer(
152 PrivateKey key, String keyPassword, X509Certificate... keyCertChain) {
153 return new SslContextBuilder(true).keyManager(key, keyPassword, keyCertChain);
154 }
155
156 /**
157 * Creates a builder for new server-side {@link SslContext}.
158 *
159 * @param key a PKCS#8 private key
160 * @param keyCertChain the X.509 certificate chain
161 * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not
162 * password-protected
163 * @see #keyManager(File, File, String)
164 */
165 public static SslContextBuilder forServer(
166 PrivateKey key, String keyPassword, Iterable<? extends X509Certificate> keyCertChain) {
167 return forServer(key, keyPassword, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
168 }
169
170 /**
171 * Creates a builder for new server-side {@link SslContext}.
172 *
173 * If you use {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} consider using
174 * {@link OpenSslX509KeyManagerFactory} or {@link OpenSslCachingX509KeyManagerFactory}.
175 *
176 * @param keyManagerFactory non-{@code null} factory for server's private key
177 * @see #keyManager(KeyManagerFactory)
178 */
179 public static SslContextBuilder forServer(KeyManagerFactory keyManagerFactory) {
180 return new SslContextBuilder(true).keyManager(keyManagerFactory);
181 }
182
183 /**
184 * Creates a builder for new server-side {@link SslContext} with {@link KeyManager}.
185 *
186 * @param keyManager non-{@code null} KeyManager for server's private key
187 */
188 public static SslContextBuilder forServer(KeyManager keyManager) {
189 return new SslContextBuilder(true).keyManager(keyManager);
190 }
191
192 private final boolean forServer;
193 private SslProvider provider;
194 private Provider sslContextProvider;
195 private X509Certificate[] trustCertCollection;
196 private TrustManagerFactory trustManagerFactory;
197 private X509Certificate[] keyCertChain;
198 private PrivateKey key;
199 private String keyPassword;
200 private KeyManagerFactory keyManagerFactory;
201 private Iterable<String> ciphers;
202 private CipherSuiteFilter cipherFilter = IdentityCipherSuiteFilter.INSTANCE;
203 private ApplicationProtocolConfig apn;
204 private long sessionCacheSize;
205 private long sessionTimeout;
206 private ClientAuth clientAuth = ClientAuth.NONE;
207 private String[] protocols;
208 private boolean startTls;
209 private boolean enableOcsp;
210 private SecureRandom secureRandom;
211 private String keyStoreType = KeyStore.getDefaultType();
212 private String endpointIdentificationAlgorithm;
213 private final Map<SslContextOption<?>, Object> options = new HashMap<SslContextOption<?>, Object>();
214
215 private SslContextBuilder(boolean forServer) {
216 this.forServer = forServer;
217 }
218
219 /**
220 * Configure a {@link SslContextOption}.
221 */
222 public <T> SslContextBuilder option(SslContextOption<T> option, T value) {
223 if (value == null) {
224 options.remove(option);
225 } else {
226 options.put(option, value);
227 }
228 return this;
229 }
230
231 /**
232 * The {@link SslContext} implementation to use. {@code null} uses the default one.
233 */
234 public SslContextBuilder sslProvider(SslProvider provider) {
235 this.provider = provider;
236 return this;
237 }
238
239 /**
240 * Sets the {@link KeyStore} type that should be used. {@code null} uses the default one.
241 */
242 public SslContextBuilder keyStoreType(String keyStoreType) {
243 this.keyStoreType = keyStoreType;
244 return this;
245 }
246
247 /**
248 * The SSLContext {@link Provider} to use. {@code null} uses the default one. This is only
249 * used with {@link SslProvider#JDK}.
250 */
251 public SslContextBuilder sslContextProvider(Provider sslContextProvider) {
252 this.sslContextProvider = sslContextProvider;
253 return this;
254 }
255
256 /**
257 * Trusted certificates for verifying the remote endpoint's certificate. The file should
258 * contain an X.509 certificate collection in PEM format. {@code null} uses the system default.
259 */
260 public SslContextBuilder trustManager(File trustCertCollectionFile) {
261 try {
262 return trustManager(SslContext.toX509Certificates(trustCertCollectionFile));
263 } catch (Exception e) {
264 throw new IllegalArgumentException("File does not contain valid certificates: "
265 + trustCertCollectionFile, e);
266 }
267 }
268
269 /**
270 * Trusted certificates for verifying the remote endpoint's certificate. The input stream should
271 * contain an X.509 certificate collection in PEM format. {@code null} uses the system default.
272 *
273 * The caller is responsible for calling {@link InputStream#close()} after {@link #build()} has been called.
274 */
275 public SslContextBuilder trustManager(InputStream trustCertCollectionInputStream) {
276 try {
277 return trustManager(SslContext.toX509Certificates(trustCertCollectionInputStream));
278 } catch (Exception e) {
279 throw new IllegalArgumentException("Input stream does not contain valid certificates.", e);
280 }
281 }
282
283 /**
284 * Trusted certificates for verifying the remote endpoint's certificate, {@code null} uses the system default.
285 */
286 public SslContextBuilder trustManager(X509Certificate... trustCertCollection) {
287 this.trustCertCollection = trustCertCollection != null ? trustCertCollection.clone() : null;
288 trustManagerFactory = null;
289 return this;
290 }
291
292 /**
293 * Trusted certificates for verifying the remote endpoint's certificate, {@code null} uses the system default.
294 */
295 public SslContextBuilder trustManager(Iterable<? extends X509Certificate> trustCertCollection) {
296 return trustManager(toArray(trustCertCollection, EMPTY_X509_CERTIFICATES));
297 }
298
299 /**
300 * Trusted manager for verifying the remote endpoint's certificate. {@code null} uses the system default.
301 */
302 public SslContextBuilder trustManager(TrustManagerFactory trustManagerFactory) {
303 trustCertCollection = null;
304 this.trustManagerFactory = trustManagerFactory;
305 return this;
306 }
307
308 /**
309 * A single trusted manager for verifying the remote endpoint's certificate.
310 * This is helpful when custom implementation of {@link TrustManager} is needed.
311 * Internally, a simple wrapper of {@link TrustManagerFactory} that only produces this
312 * specified {@link TrustManager} will be created, thus all the requirements specified in
313 * {@link #trustManager(TrustManagerFactory trustManagerFactory)} also apply here.
314 */
315 public SslContextBuilder trustManager(TrustManager trustManager) {
316 if (trustManager != null) {
317 this.trustManagerFactory = new TrustManagerFactoryWrapper(trustManager);
318 } else {
319 this.trustManagerFactory = null;
320 }
321 trustCertCollection = null;
322 return this;
323 }
324
325 /**
326 * Identifying certificate for this host. {@code keyCertChainFile} and {@code keyFile} may
327 * be {@code null} for client contexts, which disables mutual authentication.
328 *
329 * @param keyCertChainFile an X.509 certificate chain file in PEM format
330 * @param keyFile a PKCS#8 private key file in PEM format
331 */
332 public SslContextBuilder keyManager(File keyCertChainFile, File keyFile) {
333 return keyManager(keyCertChainFile, keyFile, null);
334 }
335
336 /**
337 * Identifying certificate for this host. {@code keyCertChainInputStream} and {@code keyInputStream} may
338 * be {@code null} for client contexts, which disables mutual authentication.
339 *
340 * @param keyCertChainInputStream an input stream for an X.509 certificate chain in PEM format. The caller is
341 * responsible for calling {@link InputStream#close()} after {@link #build()}
342 * has been called.
343 * @param keyInputStream an input stream for a PKCS#8 private key in PEM format. The caller is
344 * responsible for calling {@link InputStream#close()} after {@link #build()}
345 * has been called.
346 */
347 public SslContextBuilder keyManager(InputStream keyCertChainInputStream, InputStream keyInputStream) {
348 return keyManager(keyCertChainInputStream, keyInputStream, null);
349 }
350
351 /**
352 * Identifying certificate for this host. {@code keyCertChain} and {@code key} may
353 * be {@code null} for client contexts, which disables mutual authentication.
354 *
355 * @param key a PKCS#8 private key
356 * @param keyCertChain an X.509 certificate chain
357 */
358 public SslContextBuilder keyManager(PrivateKey key, X509Certificate... keyCertChain) {
359 return keyManager(key, null, keyCertChain);
360 }
361
362 /**
363 * Identifying certificate for this host. {@code keyCertChain} and {@code key} may
364 * be {@code null} for client contexts, which disables mutual authentication.
365 *
366 * @param key a PKCS#8 private key
367 * @param keyCertChain an X.509 certificate chain
368 */
369 public SslContextBuilder keyManager(PrivateKey key, Iterable<? extends X509Certificate> keyCertChain) {
370 return keyManager(key, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
371 }
372
373 /**
374 * Identifying certificate for this host. {@code keyCertChainFile} and {@code keyFile} may
375 * be {@code null} for client contexts, which disables mutual authentication.
376 *
377 * @param keyCertChainFile an X.509 certificate chain file in PEM format
378 * @param keyFile a PKCS#8 private key file in PEM format
379 * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not
380 * password-protected
381 */
382 public SslContextBuilder keyManager(File keyCertChainFile, File keyFile, String keyPassword) {
383 X509Certificate[] keyCertChain;
384 PrivateKey key;
385 try {
386 keyCertChain = SslContext.toX509Certificates(keyCertChainFile);
387 } catch (Exception e) {
388 throw new IllegalArgumentException("File does not contain valid certificates: " + keyCertChainFile, e);
389 }
390 try {
391 key = SslContext.toPrivateKey(keyFile, keyPassword);
392 } catch (Exception e) {
393 throw new IllegalArgumentException("File does not contain valid private key: " + keyFile, e);
394 }
395 return keyManager(key, keyPassword, keyCertChain);
396 }
397
398 /**
399 * Identifying certificate for this host. {@code keyCertChainInputStream} and {@code keyInputStream} may
400 * be {@code null} for client contexts, which disables mutual authentication.
401 *
402 * @param keyCertChainInputStream an input stream for an X.509 certificate chain in PEM format. The caller is
403 * responsible for calling {@link InputStream#close()} after {@link #build()}
404 * has been called.
405 * @param keyInputStream an input stream for a PKCS#8 private key in PEM format. The caller is
406 * responsible for calling {@link InputStream#close()} after {@link #build()}
407 * has been called.
408 * @param keyPassword the password of the {@code keyInputStream}, or {@code null} if it's not
409 * password-protected
410 */
411 public SslContextBuilder keyManager(InputStream keyCertChainInputStream, InputStream keyInputStream,
412 String keyPassword) {
413 X509Certificate[] keyCertChain;
414 PrivateKey key;
415 try {
416 keyCertChain = SslContext.toX509Certificates(keyCertChainInputStream);
417 } catch (Exception e) {
418 throw new IllegalArgumentException("Input stream not contain valid certificates.", e);
419 }
420 try {
421 key = SslContext.toPrivateKey(keyInputStream, keyPassword);
422 } catch (Exception e) {
423 throw new IllegalArgumentException("Input stream does not contain valid private key.", e);
424 }
425 return keyManager(key, keyPassword, keyCertChain);
426 }
427
428 /**
429 * Identifying certificate for this host. {@code keyCertChain} and {@code key} may
430 * be {@code null} for client contexts, which disables mutual authentication.
431 *
432 * @param key a PKCS#8 private key file
433 * @param keyPassword the password of the {@code key}, or {@code null} if it's not
434 * password-protected
435 * @param keyCertChain an X.509 certificate chain
436 */
437 public SslContextBuilder keyManager(PrivateKey key, String keyPassword, X509Certificate... keyCertChain) {
438 if (forServer) {
439 checkNonEmpty(keyCertChain, "keyCertChain");
440 checkNotNull(key, "key required for servers");
441 }
442 if (keyCertChain == null || keyCertChain.length == 0) {
443 this.keyCertChain = null;
444 } else {
445 for (X509Certificate cert: keyCertChain) {
446 checkNotNullWithIAE(cert, "cert");
447 }
448 this.keyCertChain = keyCertChain.clone();
449 }
450 this.key = key;
451 this.keyPassword = keyPassword;
452 keyManagerFactory = null;
453 return this;
454 }
455
456 /**
457 * Identifying certificate for this host. {@code keyCertChain} and {@code key} may
458 * be {@code null} for client contexts, which disables mutual authentication.
459 *
460 * @param key a PKCS#8 private key file
461 * @param keyPassword the password of the {@code key}, or {@code null} if it's not
462 * password-protected
463 * @param keyCertChain an X.509 certificate chain
464 */
465 public SslContextBuilder keyManager(PrivateKey key, String keyPassword,
466 Iterable<? extends X509Certificate> keyCertChain) {
467 return keyManager(key, keyPassword, toArray(keyCertChain, EMPTY_X509_CERTIFICATES));
468 }
469
470 /**
471 * Identifying manager for this host. {@code keyManagerFactory} may be {@code null} for
472 * client contexts, which disables mutual authentication. Using a {@link KeyManagerFactory}
473 * is only supported for {@link SslProvider#JDK} or {@link SslProvider#OPENSSL} / {@link SslProvider#OPENSSL_REFCNT}
474 * if the used openssl version is 1.0.1+. You can check if your openssl version supports using a
475 * {@link KeyManagerFactory} by calling {@link OpenSsl#supportsKeyManagerFactory()}. If this is not the case
476 * you must use {@link #keyManager(File, File)} or {@link #keyManager(File, File, String)}.
477 *
478 * If you use {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} consider using
479 * {@link OpenSslX509KeyManagerFactory} or {@link OpenSslCachingX509KeyManagerFactory}.
480 */
481 public SslContextBuilder keyManager(KeyManagerFactory keyManagerFactory) {
482 if (forServer) {
483 checkNotNull(keyManagerFactory, "keyManagerFactory required for servers");
484 }
485 keyCertChain = null;
486 key = null;
487 keyPassword = null;
488 this.keyManagerFactory = keyManagerFactory;
489 return this;
490 }
491
492 /**
493 * A single key manager managing the identity information of this host.
494 * This is helpful when custom implementation of {@link KeyManager} is needed.
495 * Internally, a wrapper of {@link KeyManagerFactory} that only produces this specified
496 * {@link KeyManager} will be created, thus all the requirements specified in
497 * {@link #keyManager(KeyManagerFactory keyManagerFactory)} also apply here.
498 */
499 public SslContextBuilder keyManager(KeyManager keyManager) {
500 if (forServer) {
501 checkNotNull(keyManager, "keyManager required for servers");
502 }
503 if (keyManager != null) {
504 this.keyManagerFactory = new KeyManagerFactoryWrapper(keyManager);
505 } else {
506 this.keyManagerFactory = null;
507 }
508 keyCertChain = null;
509 key = null;
510 keyPassword = null;
511 return this;
512 }
513
514 /**
515 * The cipher suites to enable, in the order of preference. {@code null} to use default
516 * cipher suites.
517 */
518 public SslContextBuilder ciphers(Iterable<String> ciphers) {
519 return ciphers(ciphers, IdentityCipherSuiteFilter.INSTANCE);
520 }
521
522 /**
523 * The cipher suites to enable, in the order of preference. {@code cipherFilter} will be
524 * applied to the ciphers before use. If {@code ciphers} is {@code null}, then the default
525 * cipher suites will be used.
526 */
527 public SslContextBuilder ciphers(Iterable<String> ciphers, CipherSuiteFilter cipherFilter) {
528 this.cipherFilter = checkNotNull(cipherFilter, "cipherFilter");
529 this.ciphers = ciphers;
530 return this;
531 }
532
533 /**
534 * Application protocol negotiation configuration. {@code null} disables support.
535 */
536 public SslContextBuilder applicationProtocolConfig(ApplicationProtocolConfig apn) {
537 this.apn = apn;
538 return this;
539 }
540
541 /**
542 * Set the size of the cache used for storing SSL session objects. {@code 0} to use the
543 * default value.
544 */
545 public SslContextBuilder sessionCacheSize(long sessionCacheSize) {
546 this.sessionCacheSize = sessionCacheSize;
547 return this;
548 }
549
550 /**
551 * Set the timeout for the cached SSL session objects, in seconds. {@code 0} to use the
552 * default value.
553 */
554 public SslContextBuilder sessionTimeout(long sessionTimeout) {
555 this.sessionTimeout = sessionTimeout;
556 return this;
557 }
558
559 /**
560 * Sets the client authentication mode.
561 */
562 public SslContextBuilder clientAuth(ClientAuth clientAuth) {
563 this.clientAuth = checkNotNull(clientAuth, "clientAuth");
564 return this;
565 }
566
567 /**
568 * The TLS protocol versions to enable.
569 * @param protocols The protocols to enable, or {@code null} to enable the default protocols.
570 * @see SSLEngine#setEnabledCipherSuites(String[])
571 */
572 public SslContextBuilder protocols(String... protocols) {
573 this.protocols = protocols == null ? null : protocols.clone();
574 return this;
575 }
576
577 /**
578 * The TLS protocol versions to enable.
579 * @param protocols The protocols to enable, or {@code null} to enable the default protocols.
580 * @see SSLEngine#setEnabledCipherSuites(String[])
581 */
582 public SslContextBuilder protocols(Iterable<String> protocols) {
583 return protocols(toArray(protocols, EMPTY_STRINGS));
584 }
585
586 /**
587 * {@code true} if the first write request shouldn't be encrypted.
588 */
589 public SslContextBuilder startTls(boolean startTls) {
590 this.startTls = startTls;
591 return this;
592 }
593
594 /**
595 * Enables OCSP stapling. Please note that not all {@link SslProvider} implementations support OCSP
596 * stapling and an exception will be thrown upon {@link #build()}.
597 *
598 * @see OpenSsl#isOcspSupported()
599 */
600 @UnstableApi
601 public SslContextBuilder enableOcsp(boolean enableOcsp) {
602 this.enableOcsp = enableOcsp;
603 return this;
604 }
605
606 /**
607 * Specify a non-default source of randomness for the {@link JdkSslContext}
608 * <p>
609 * In general, the best practice is to leave this unspecified, or to assign a new random source using the
610 * default {@code new SecureRandom()} constructor.
611 * Only assign this something when you have a good reason to.
612 *
613 * @param secureRandom the source of randomness for {@link JdkSslContext}
614 *
615 */
616 public SslContextBuilder secureRandom(SecureRandom secureRandom) {
617 this.secureRandom = secureRandom;
618 return this;
619 }
620
621 /**
622 * Specify the endpoint identification algorithm (aka. hostname verification algorithm) that clients will use as
623 * part of authenticating servers.
624 * <p>
625 * See <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#jssenames">
626 * Java Security Standard Names</a> for a list of supported algorithms.
627 *
628 * @param algorithm either {@code "HTTPS"}, {@code "LDAPS"}, or {@code null} (disables hostname verification).
629 * @see javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)
630 */
631 public SslContextBuilder endpointIdentificationAlgorithm(String algorithm) {
632 endpointIdentificationAlgorithm = algorithm;
633 return this;
634 }
635
636 /**
637 * Create new {@code SslContext} instance with configured settings.
638 * <p>If {@link #sslProvider(SslProvider)} is set to {@link SslProvider#OPENSSL_REFCNT} then the caller is
639 * responsible for releasing this object, or else native memory may leak.
640 */
641 public SslContext build() throws SSLException {
642 if (forServer) {
643 return SslContext.newServerContextInternal(provider, sslContextProvider, trustCertCollection,
644 trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
645 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls,
646 enableOcsp, secureRandom, keyStoreType, toArray(options.entrySet(), EMPTY_ENTRIES));
647 } else {
648 return SslContext.newClientContextInternal(provider, sslContextProvider, trustCertCollection,
649 trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
650 ciphers, cipherFilter, apn, protocols, sessionCacheSize,
651 sessionTimeout, enableOcsp, secureRandom, keyStoreType, endpointIdentificationAlgorithm,
652 toArray(options.entrySet(), EMPTY_ENTRIES));
653 }
654 }
655
656 private static <T> T[] toArray(Iterable<? extends T> iterable, T[] prototype) {
657 if (iterable == null) {
658 return null;
659 }
660 final List<T> list = new ArrayList<T>();
661 for (T element : iterable) {
662 list.add(element);
663 }
664 return list.toArray(prototype);
665 }
666 }