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