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