View Javadoc
1   /*
2    * Copyright 2014 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    *   http://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.buffer.ByteBufAllocator;
20  import io.netty.channel.ChannelInitializer;
21  import io.netty.channel.ChannelPipeline;
22  
23  import javax.net.ssl.KeyManager;
24  import javax.net.ssl.KeyManagerFactory;
25  import javax.crypto.Cipher;
26  import javax.crypto.EncryptedPrivateKeyInfo;
27  import javax.crypto.NoSuchPaddingException;
28  import javax.crypto.SecretKey;
29  import javax.crypto.SecretKeyFactory;
30  import javax.crypto.spec.PBEKeySpec;
31  import javax.net.ssl.SSLContext;
32  import javax.net.ssl.SSLEngine;
33  import javax.net.ssl.SSLException;
34  import javax.net.ssl.SSLSessionContext;
35  import javax.net.ssl.TrustManager;
36  import javax.net.ssl.TrustManagerFactory;
37  import java.io.File;
38  import java.io.IOException;
39  import java.security.InvalidAlgorithmParameterException;
40  import java.security.InvalidKeyException;
41  import java.security.NoSuchAlgorithmException;
42  import java.security.cert.CertificateException;
43  import java.security.cert.CertificateFactory;
44  import java.security.spec.InvalidKeySpecException;
45  import java.security.spec.PKCS8EncodedKeySpec;
46  import java.util.List;
47  
48  /**
49   * A secure socket protocol implementation which acts as a factory for {@link SSLEngine} and {@link SslHandler}.
50   * Internally, it is implemented via JDK's {@link SSLContext} or OpenSSL's {@code SSL_CTX}.
51   *
52   * <h3>Making your server support SSL/TLS</h3>
53   * <pre>
54   * // In your {@link ChannelInitializer}:
55   * {@link ChannelPipeline} p = channel.pipeline();
56   * {@link SslContext} sslCtx = {@link #newServerContext(File, File) SslContext.newServerContext(...)};
57   * p.addLast("ssl", {@link #newEngine(ByteBufAllocator) sslCtx.newEngine(channel.alloc())});
58   * ...
59   * </pre>
60   *
61   * <h3>Making your client support SSL/TLS</h3>
62   * <pre>
63   * // In your {@link ChannelInitializer}:
64   * {@link ChannelPipeline} p = channel.pipeline();
65   * {@link SslContext} sslCtx = {@link #newClientContext(File) SslContext.newClientContext(...)};
66   * p.addLast("ssl", {@link #newEngine(ByteBufAllocator, String, int) sslCtx.newEngine(channel.alloc(), host, port)});
67   * ...
68   * </pre>
69   */
70  public abstract class SslContext {
71      static final CertificateFactory X509_CERT_FACTORY;
72      static {
73          try {
74              X509_CERT_FACTORY = CertificateFactory.getInstance("X.509");
75          } catch (CertificateException e) {
76              throw new IllegalStateException("unable to instance X.509 CertificateFactory", e);
77          }
78      }
79  
80      /**
81       * Returns the default server-side implementation provider currently in use.
82       *
83       * @return {@link SslProvider#OPENSSL} if OpenSSL is available. {@link SslProvider#JDK} otherwise.
84       */
85      public static SslProvider defaultServerProvider() {
86          return defaultProvider();
87      }
88  
89      /**
90       * Returns the default client-side implementation provider currently in use.
91       *
92       * @return {@link SslProvider#OPENSSL} if OpenSSL is available. {@link SslProvider#JDK} otherwise.
93       */
94      public static SslProvider defaultClientProvider() {
95          return defaultProvider();
96      }
97  
98      private static SslProvider defaultProvider() {
99          if (OpenSsl.isAvailable()) {
100             return SslProvider.OPENSSL;
101         } else {
102             return SslProvider.JDK;
103         }
104     }
105 
106     /**
107      * Creates a new server-side {@link SslContext}.
108      *
109      * @param certChainFile an X.509 certificate chain file in PEM format
110      * @param keyFile a PKCS#8 private key file in PEM format
111      * @return a new server-side {@link SslContext}
112      */
113     public static SslContext newServerContext(File certChainFile, File keyFile) throws SSLException {
114         return newServerContext(certChainFile, keyFile, null);
115     }
116 
117     /**
118      * Creates a new server-side {@link SslContext}.
119      *
120      * @param certChainFile an X.509 certificate chain file in PEM format
121      * @param keyFile a PKCS#8 private key file in PEM format
122      * @param keyPassword the password of the {@code keyFile}.
123      *                    {@code null} if it's not password-protected.
124      * @return a new server-side {@link SslContext}
125      */
126     public static SslContext newServerContext(
127             File certChainFile, File keyFile, String keyPassword) throws SSLException {
128         return newServerContext(null, certChainFile, keyFile, keyPassword);
129     }
130 
131     /**
132      * Creates a new server-side {@link SslContext}.
133      *
134      * @param certChainFile an X.509 certificate chain file in PEM format
135      * @param keyFile a PKCS#8 private key file in PEM format
136      * @param keyPassword the password of the {@code keyFile}.
137      *                    {@code null} if it's not password-protected.
138      * @param ciphers the cipher suites to enable, in the order of preference.
139      *                {@code null} to use the default cipher suites.
140      * @param cipherFilter a filter to apply over the supplied list of ciphers
141      * @param apn Provides a means to configure parameters related to application protocol negotiation.
142      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
143      *                         {@code 0} to use the default value.
144      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
145      *                       {@code 0} to use the default value.
146      * @return a new server-side {@link SslContext}
147      */
148     public static SslContext newServerContext(
149             File certChainFile, File keyFile, String keyPassword,
150             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
151             long sessionCacheSize, long sessionTimeout) throws SSLException {
152         return newServerContext(
153                 null, certChainFile, keyFile, keyPassword,
154                 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
155     }
156 
157     /**
158      * Creates a new server-side {@link SslContext}.
159      *
160      * @param provider the {@link SslContext} implementation to use.
161      *                 {@code null} to use the current default one.
162      * @param certChainFile an X.509 certificate chain file in PEM format
163      * @param keyFile a PKCS#8 private key file in PEM format
164      * @return a new server-side {@link SslContext}
165      */
166     public static SslContext newServerContext(
167             SslProvider provider, File certChainFile, File keyFile) throws SSLException {
168         return newServerContext(provider, certChainFile, keyFile, null);
169     }
170 
171     /**
172      * Creates a new server-side {@link SslContext}.
173      *
174      * @param provider the {@link SslContext} implementation to use.
175      *                 {@code null} to use the current default one.
176      * @param certChainFile an X.509 certificate chain file in PEM format
177      * @param keyFile a PKCS#8 private key file in PEM format
178      * @param keyPassword the password of the {@code keyFile}.
179      *                    {@code null} if it's not password-protected.
180      * @return a new server-side {@link SslContext}
181      */
182     public static SslContext newServerContext(
183             SslProvider provider, File certChainFile, File keyFile, String keyPassword) throws SSLException {
184         return newServerContext(provider, certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE,
185                 null, 0, 0);
186     }
187 
188     /**
189      * Creates a new server-side {@link SslContext}.
190      *
191      * @param provider the {@link SslContext} implementation to use.
192      *                 {@code null} to use the current default one.
193      * @param certChainFile an X.509 certificate chain file in PEM format
194      * @param keyFile a PKCS#8 private key file in PEM format
195      * @param keyPassword the password of the {@code keyFile}.
196      *                    {@code null} if it's not password-protected.
197      * @param ciphers the cipher suites to enable, in the order of preference.
198      *                {@code null} to use the default cipher suites.
199      * @param cipherFilter a filter to apply over the supplied list of ciphers
200      *                Only required if {@code provider} is {@link SslProvider#JDK}
201      * @param apn Provides a means to configure parameters related to application protocol negotiation.
202      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
203      *                         {@code 0} to use the default value.
204      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
205      *                       {@code 0} to use the default value.
206      * @return a new server-side {@link SslContext}
207      */
208     public static SslContext newServerContext(SslProvider provider,
209             File certChainFile, File keyFile, String keyPassword,
210             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
211             long sessionCacheSize, long sessionTimeout) throws SSLException {
212         return newServerContext(provider, null, null, certChainFile, keyFile, keyPassword, null,
213                 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
214     }
215 
216     /**
217      * Creates a new server-side {@link SslContext}.
218      * @param provider the {@link SslContext} implementation to use.
219      *                 {@code null} to use the current default one.
220      * @param trustCertChainFile an X.509 certificate chain file in PEM format.
221      *                      This provides the certificate chains used for mutual authentication.
222      *                      {@code null} to use the system default
223      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
224      *                            that verifies the certificates sent from clients.
225      *                            {@code null} to use the default or the results of parsing {@code trustCertChainFile}.
226      *                            This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
227      * @param keyCertChainFile an X.509 certificate chain file in PEM format
228      * @param keyFile a PKCS#8 private key file in PEM format
229      * @param keyPassword the password of the {@code keyFile}.
230      *                    {@code null} if it's not password-protected.
231      * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s
232      *                          that is used to encrypt data being sent to clients.
233      *                          {@code null} to use the default or the results of parsing
234      *                          {@code keyCertChainFile} and {@code keyFile}.
235      *                          This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
236      * @param ciphers the cipher suites to enable, in the order of preference.
237      *                {@code null} to use the default cipher suites.
238      * @param cipherFilter a filter to apply over the supplied list of ciphers
239      *                Only required if {@code provider} is {@link SslProvider#JDK}
240      * @param apn Provides a means to configure parameters related to application protocol negotiation.
241      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
242      *                         {@code 0} to use the default value.
243      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
244      *                       {@code 0} to use the default value.
245      * @return a new server-side {@link SslContext}
246      */
247     public static SslContext newServerContext(SslProvider provider,
248             File trustCertChainFile, TrustManagerFactory trustManagerFactory,
249             File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
250             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
251             long sessionCacheSize, long sessionTimeout) throws SSLException {
252 
253         if (provider == null) {
254             provider = defaultServerProvider();
255         }
256 
257         switch (provider) {
258         case JDK:
259             return new JdkSslServerContext(
260                     trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword,
261                     keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
262         case OPENSSL:
263             return new OpenSslServerContext(
264                     keyCertChainFile, keyFile, keyPassword, trustManagerFactory,
265                     ciphers, apn, sessionCacheSize, sessionTimeout);
266         default:
267             throw new Error(provider.toString());
268         }
269     }
270 
271     /**
272      * Creates a new client-side {@link SslContext}.
273      *
274      * @return a new client-side {@link SslContext}
275      */
276     public static SslContext newClientContext() throws SSLException {
277         return newClientContext(null, null, null);
278     }
279 
280     /**
281      * Creates a new client-side {@link SslContext}.
282      *
283      * @param certChainFile an X.509 certificate chain file in PEM format
284      *
285      * @return a new client-side {@link SslContext}
286      */
287     public static SslContext newClientContext(File certChainFile) throws SSLException {
288         return newClientContext(null, certChainFile);
289     }
290 
291     /**
292      * Creates a new client-side {@link SslContext}.
293      *
294      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
295      *                            that verifies the certificates sent from servers.
296      *                            {@code null} to use the default.
297      *
298      * @return a new client-side {@link SslContext}
299      */
300     public static SslContext newClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
301         return newClientContext(null, null, trustManagerFactory);
302     }
303 
304     /**
305      * Creates a new client-side {@link SslContext}.
306      *
307      * @param certChainFile an X.509 certificate chain file in PEM format.
308      *                      {@code null} to use the system default
309      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
310      *                            that verifies the certificates sent from servers.
311      *                            {@code null} to use the default.
312      *
313      * @return a new client-side {@link SslContext}
314      */
315     public static SslContext newClientContext(
316             File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
317         return newClientContext(null, certChainFile, trustManagerFactory);
318     }
319 
320     /**
321      * Creates a new client-side {@link SslContext}.
322      *
323      * @param certChainFile an X.509 certificate chain file in PEM format.
324      *                      {@code null} to use the system default
325      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
326      *                            that verifies the certificates sent from servers.
327      *                            {@code null} to use the default.
328      * @param ciphers the cipher suites to enable, in the order of preference.
329      *                {@code null} to use the default cipher suites.
330      * @param cipherFilter a filter to apply over the supplied list of ciphers
331      * @param apn Provides a means to configure parameters related to application protocol negotiation.
332      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
333      *                         {@code 0} to use the default value.
334      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
335      *                       {@code 0} to use the default value.
336      *
337      * @return a new client-side {@link SslContext}
338      */
339     public static SslContext newClientContext(
340             File certChainFile, TrustManagerFactory trustManagerFactory,
341             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
342             long sessionCacheSize, long sessionTimeout) throws SSLException {
343         return newClientContext(
344                 null, certChainFile, trustManagerFactory,
345                 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
346     }
347 
348     /**
349      * Creates a new client-side {@link SslContext}.
350      *
351      * @param provider the {@link SslContext} implementation to use.
352      *                 {@code null} to use the current default one.
353      *
354      * @return a new client-side {@link SslContext}
355      */
356     public static SslContext newClientContext(SslProvider provider) throws SSLException {
357         return newClientContext(provider, null, null);
358     }
359 
360     /**
361      * Creates a new client-side {@link SslContext}.
362      *
363      * @param provider the {@link SslContext} implementation to use.
364      *                 {@code null} to use the current default one.
365      * @param certChainFile an X.509 certificate chain file in PEM format.
366      *                      {@code null} to use the system default
367      *
368      * @return a new client-side {@link SslContext}
369      */
370     public static SslContext newClientContext(SslProvider provider, File certChainFile) throws SSLException {
371         return newClientContext(provider, certChainFile, null);
372     }
373 
374     /**
375      * Creates a new client-side {@link SslContext}.
376      *
377      * @param provider the {@link SslContext} implementation to use.
378      *                 {@code null} to use the current default one.
379      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
380      *                            that verifies the certificates sent from servers.
381      *                            {@code null} to use the default.
382      *
383      * @return a new client-side {@link SslContext}
384      */
385     public static SslContext newClientContext(
386             SslProvider provider, TrustManagerFactory trustManagerFactory) throws SSLException {
387         return newClientContext(provider, null, trustManagerFactory);
388     }
389 
390     /**
391      * Creates a new client-side {@link SslContext}.
392      *
393      * @param provider the {@link SslContext} implementation to use.
394      *                 {@code null} to use the current default one.
395      * @param certChainFile an X.509 certificate chain file in PEM format.
396      *                      {@code null} to use the system default
397      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
398      *                            that verifies the certificates sent from servers.
399      *                            {@code null} to use the default.
400      *
401      * @return a new client-side {@link SslContext}
402      */
403     public static SslContext newClientContext(
404             SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
405         return newClientContext(provider, certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE,
406                 null, 0, 0);
407     }
408 
409     /**
410      * Creates a new client-side {@link SslContext}.
411      *
412      * @param provider the {@link SslContext} implementation to use.
413      *                 {@code null} to use the current default one.
414      * @param certChainFile an X.509 certificate chain file in PEM format.
415      *                      {@code null} to use the system default
416      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
417      *                            that verifies the certificates sent from servers.
418      *                            {@code null} to use the default.
419      * @param ciphers the cipher suites to enable, in the order of preference.
420      *                {@code null} to use the default cipher suites.
421      * @param cipherFilter a filter to apply over the supplied list of ciphers
422      * @param apn Provides a means to configure parameters related to application protocol negotiation.
423      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
424      *                         {@code 0} to use the default value.
425      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
426      *                       {@code 0} to use the default value.
427      *
428      * @return a new client-side {@link SslContext}
429      */
430     public static SslContext newClientContext(SslProvider provider,
431             File certChainFile, TrustManagerFactory trustManagerFactory,
432             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
433             long sessionCacheSize, long sessionTimeout) throws SSLException {
434         return newClientContext(provider, certChainFile, trustManagerFactory, null, null, null, null,
435                 ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
436     }
437 
438     /**
439      * Creates a new client-side {@link SslContext}.
440      * @param provider the {@link SslContext} implementation to use.
441      *                 {@code null} to use the current default one.
442      * @param trustCertChainFile an X.509 certificate chain file in PEM format.
443      *                      {@code null} to use the system default
444      * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
445      *                            that verifies the certificates sent from servers.
446      *                            {@code null} to use the default or the results of parsing {@code trustCertChainFile}.
447      *                            This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
448      * @param keyCertChainFile an X.509 certificate chain file in PEM format.
449      *                      This provides the public key for mutual authentication.
450      *                      {@code null} to use the system default
451      * @param keyFile a PKCS#8 private key file in PEM format.
452      *                      This provides the private key for mutual authentication.
453      *                      {@code null} for no mutual authentication.
454      * @param keyPassword the password of the {@code keyFile}.
455      *                    {@code null} if it's not password-protected.
456      *                    Ignored if {@code keyFile} is {@code null}.
457      * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s
458      *                          that is used to encrypt data being sent to servers.
459      *                          {@code null} to use the default or the results of parsing
460      *                          {@code keyCertChainFile} and {@code keyFile}.
461      *                          This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
462      * @param ciphers the cipher suites to enable, in the order of preference.
463      *                {@code null} to use the default cipher suites.
464      * @param cipherFilter a filter to apply over the supplied list of ciphers
465      * @param apn Provides a means to configure parameters related to application protocol negotiation.
466      * @param sessionCacheSize the size of the cache used for storing SSL session objects.
467      *                         {@code 0} to use the default value.
468      * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
469      *                       {@code 0} to use the default value.
470      *
471      * @return a new client-side {@link SslContext}
472      */
473     public static SslContext newClientContext(SslProvider provider,
474             File trustCertChainFile, TrustManagerFactory trustManagerFactory,
475             File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
476             Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
477             long sessionCacheSize, long sessionTimeout) throws SSLException {
478         if (provider == null) {
479             provider = defaultClientProvider();
480         }
481         switch (provider) {
482             case JDK:
483                 return new JdkSslClientContext(
484                         trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword,
485                         keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
486             case OPENSSL:
487                 return new OpenSslClientContext(
488                         trustCertChainFile, trustManagerFactory, ciphers, apn, sessionCacheSize, sessionTimeout);
489         }
490         // Should never happen!!
491         throw new Error();
492     }
493 
494     SslContext() { }
495 
496     /**
497      * Returns {@code true} if and only if this context is for server-side.
498      */
499     public final boolean isServer() {
500         return !isClient();
501     }
502 
503     /**
504      * Returns the {@code true} if and only if this context is for client-side.
505      */
506     public abstract boolean isClient();
507 
508     /**
509      * Returns the list of enabled cipher suites, in the order of preference.
510      */
511     public abstract List<String> cipherSuites();
512 
513     /**
514      * Returns the size of the cache used for storing SSL session objects.
515      */
516     public abstract long sessionCacheSize();
517 
518     /**
519      * Returns the timeout for the cached SSL session objects, in seconds.
520      */
521     public abstract long sessionTimeout();
522 
523     /**
524      * Returns the object responsible for negotiating application layer protocols for the TLS NPN/ALPN extensions.
525      */
526     public abstract ApplicationProtocolNegotiator applicationProtocolNegotiator();
527 
528     /**
529      * Creates a new {@link SSLEngine}.
530      *
531      * @return a new {@link SSLEngine}
532      */
533     public abstract SSLEngine newEngine(ByteBufAllocator alloc);
534 
535     /**
536      * Creates a new {@link SSLEngine} using advisory peer information.
537      *
538      * @param peerHost the non-authoritative name of the host
539      * @param peerPort the non-authoritative port
540      *
541      * @return a new {@link SSLEngine}
542      */
543     public abstract SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort);
544 
545     /**
546      * Returns the {@link SSLSessionContext} object held by this context.
547      */
548     public abstract SSLSessionContext sessionContext();
549 
550     /**
551      * Creates a new {@link SslHandler}.
552      *
553      * @return a new {@link SslHandler}
554      */
555     public final SslHandler newHandler(ByteBufAllocator alloc) {
556         return newHandler(newEngine(alloc));
557     }
558 
559     /**
560      * Creates a new {@link SslHandler} with advisory peer information.
561      *
562      * @param peerHost the non-authoritative name of the host
563      * @param peerPort the non-authoritative port
564      *
565      * @return a new {@link SslHandler}
566      */
567     public final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort) {
568         return newHandler(newEngine(alloc, peerHost, peerPort));
569     }
570 
571     private static SslHandler newHandler(SSLEngine engine) {
572         return new SslHandler(engine);
573     }
574 
575     /**
576      * Generates a key specification for an (encrypted) private key.
577      *
578      * @param password characters, if {@code null} or empty an unencrypted key is assumed
579      * @param key bytes of the DER encoded private key
580      *
581      * @return a key specification
582      *
583      * @throws IOException if parsing {@code key} fails
584      * @throws NoSuchAlgorithmException if the algorithm used to encrypt {@code key} is unkown
585      * @throws NoSuchPaddingException if the padding scheme specified in the decryption algorithm is unkown
586      * @throws InvalidKeySpecException if the decryption key based on {@code password} cannot be generated
587      * @throws InvalidKeyException if the decryption key based on {@code password} cannot be used to decrypt
588      *                             {@code key}
589      * @throws InvalidAlgorithmParameterException if decryption algorithm parameters are somehow faulty
590      */
591     protected static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key)
592             throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
593             InvalidKeyException, InvalidAlgorithmParameterException {
594 
595         if (password == null || password.length == 0) {
596             return new PKCS8EncodedKeySpec(key);
597         }
598 
599         EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
600         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
601         PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
602         SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
603 
604         Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
605         cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
606 
607         return encryptedPrivateKeyInfo.getKeySpec(cipher);
608     }
609 }