View Javadoc
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 }