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