View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
12  // ========================================================================
13  
14  package org.eclipse.jetty.server.ssl;
15  
16  import java.io.IOException;
17  import java.io.InputStream;
18  import java.nio.channels.SelectionKey;
19  import java.nio.channels.SocketChannel;
20  import java.security.KeyStore;
21  import java.security.SecureRandom;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.List;
25  import javax.net.ssl.KeyManager;
26  import javax.net.ssl.KeyManagerFactory;
27  import javax.net.ssl.SSLContext;
28  import javax.net.ssl.SSLEngine;
29  import javax.net.ssl.SSLSession;
30  import javax.net.ssl.SSLSocket;
31  import javax.net.ssl.TrustManager;
32  import javax.net.ssl.TrustManagerFactory;
33  
34  import org.eclipse.jetty.http.HttpParser;
35  import org.eclipse.jetty.http.HttpSchemes;
36  import org.eclipse.jetty.http.security.Password;
37  import org.eclipse.jetty.io.Buffer;
38  import org.eclipse.jetty.io.Buffers;
39  import org.eclipse.jetty.io.Connection;
40  import org.eclipse.jetty.io.EndPoint;
41  import org.eclipse.jetty.io.ThreadLocalBuffers;
42  import org.eclipse.jetty.io.bio.SocketEndPoint;
43  import org.eclipse.jetty.io.nio.DirectNIOBuffer;
44  import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
45  import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
46  import org.eclipse.jetty.io.nio.SelectorManager.SelectSet;
47  import org.eclipse.jetty.io.nio.SslSelectChannelEndPoint;
48  import org.eclipse.jetty.server.HttpConnection;
49  import org.eclipse.jetty.server.Request;
50  import org.eclipse.jetty.server.nio.SelectChannelConnector;
51  import org.eclipse.jetty.util.log.Log;
52  import org.eclipse.jetty.util.resource.Resource;
53  
54  /* ------------------------------------------------------------ */
55  /**
56   * SslSelectChannelConnector.
57   *
58   * @org.apache.xbean.XBean element="sslConnector" description="Creates an NIO ssl connector"
59   *
60   *
61   *
62   */
63  public class SslSelectChannelConnector extends SelectChannelConnector implements SslConnector
64  {
65      /** Default value for the excluded cipher Suites. */
66      private String _excludeCipherSuites[]=null;
67      /** Default value for the included cipher Suites. */
68      private String _includeCipherSuites[]=null;
69  
70      /** Default value for the keystore location path. */
71      private String _keystorePath=DEFAULT_KEYSTORE;
72      private String _keystoreType="JKS"; // type of the key store
73  
74      /** Set to true if we require client certificate authentication. */
75      private boolean _needClientAuth=false;
76      private boolean _wantClientAuth=false;
77      private boolean _allowRenegotiate=false;
78  
79      private transient Password _password;
80      private transient Password _keyPassword;
81      private transient Password _trustPassword;
82      private String _protocol="TLS";
83      private String _provider;
84      private String _secureRandomAlgorithm; // cert algorithm
85      private String _sslKeyManagerFactoryAlgorithm=DEFAULT_KEYSTORE_ALGORITHM;
86      private String _sslTrustManagerFactoryAlgorithm=DEFAULT_TRUSTSTORE_ALGORITHM;
87      private String _truststorePath;
88      private String _truststoreType="JKS"; // type of the key store
89      private SSLContext _context;
90      private Buffers _sslBuffers;
91  
92      /* ------------------------------------------------------------ */
93      public SslSelectChannelConnector()
94      {
95          setUseDirectBuffers(false);
96      }
97  
98      /* ------------------------------------------------------------ */
99      /**
100      * Allow the Listener a chance to customise the request. before the server
101      * does its stuff. <br>
102      * This allows the required attributes to be set for SSL requests. <br>
103      * The requirements of the Servlet specs are:
104      * <ul>
105      * <li> an attribute named "javax.servlet.request.ssl_session_id" of type
106      * String (since Servlet Spec 3.0).</li>
107      * <li> an attribute named "javax.servlet.request.cipher_suite" of type
108      * String.</li>
109      * <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li>
110      * <li> an attribute named "javax.servlet.request.X509Certificate" of type
111      * java.security.cert.X509Certificate[]. This is an array of objects of type
112      * X509Certificate, the order of this array is defined as being in ascending
113      * order of trust. The first certificate in the chain is the one set by the
114      * client, the next is the one used to authenticate the first, and so on.
115      * </li>
116      * </ul>
117      *
118      * @param endpoint
119      *                The Socket the request arrived on. This should be a
120      *                {@link SocketEndPoint} wrapping a {@link SSLSocket}.
121      * @param request
122      *                HttpRequest to be customised.
123      */
124     @Override
125     public void customize(EndPoint endpoint, Request request) throws IOException
126     {
127         request.setScheme(HttpSchemes.HTTPS);
128         super.customize(endpoint,request);
129 
130         SslSelectChannelEndPoint sslHttpChannelEndpoint=(SslSelectChannelEndPoint)endpoint;
131         SSLEngine sslEngine=sslHttpChannelEndpoint.getSSLEngine();
132         SSLSession sslSession=sslEngine.getSession();
133 
134         SslCertificates.customize(sslSession,endpoint,request);
135     }
136 
137     /* ------------------------------------------------------------ */
138     /**
139      * @return True if SSL re-negotiation is allowed (default false)
140      */
141     public boolean isAllowRenegotiate()
142     {
143         return _allowRenegotiate;
144     }
145 
146     /* ------------------------------------------------------------ */
147     /**
148      * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
149      * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
150      * does not have CVE-2009-3555 fixed, then re-negotiation should
151      * not be allowed.
152      * @param allowRenegotiate true if re-negotiation is allowed (default false)
153      */
154     public void setAllowRenegotiate(boolean allowRenegotiate)
155     {
156         _allowRenegotiate = allowRenegotiate;
157     }
158 
159     /* ------------------------------------------------------------ */
160     /**
161      * @see org.eclipse.jetty.server.ssl.SslConnector#getExcludeCipherSuites()
162      */
163     public String[] getExcludeCipherSuites()
164     {
165         return _excludeCipherSuites;
166     }
167 
168     /* ------------------------------------------------------------ */
169     /**
170      * @see org.eclipse.jetty.server.ssl.SslConnector#setExcludeCipherSuites(java.lang.String[])
171      */
172     public void setExcludeCipherSuites(String[] cipherSuites)
173     {
174         this._excludeCipherSuites=cipherSuites;
175     }
176 
177     /* ------------------------------------------------------------ */
178     /**
179      * @see org.eclipse.jetty.server.ssl.SslConnector#getExcludeCipherSuites()
180      */
181     public String[] getIncludeCipherSuites()
182     {
183         return _includeCipherSuites;
184     }
185 
186     /* ------------------------------------------------------------ */
187     /**
188      * @see org.eclipse.jetty.server.ssl.SslConnector#setExcludeCipherSuites(java.lang.String[])
189      */
190     public void setIncludeCipherSuites(String[] cipherSuites)
191     {
192         this._includeCipherSuites=cipherSuites;
193     }
194 
195     /* ------------------------------------------------------------ */
196     /**
197      * @see org.eclipse.jetty.server.ssl.SslConnector#setPassword(java.lang.String)
198      */
199     public void setPassword(String password)
200     {
201         _password=Password.getPassword(PASSWORD_PROPERTY,password,null);
202     }
203 
204     /* ------------------------------------------------------------ */
205     /**
206      * @see org.eclipse.jetty.server.ssl.SslConnector#setTrustPassword(java.lang.String)
207      */
208     public void setTrustPassword(String password)
209     {
210         _trustPassword=Password.getPassword(PASSWORD_PROPERTY,password,null);
211     }
212 
213     /* ------------------------------------------------------------ */
214     /**
215      * @see org.eclipse.jetty.server.ssl.SslConnector#setKeyPassword(java.lang.String)
216      */
217     public void setKeyPassword(String password)
218     {
219         _keyPassword=Password.getPassword(KEYPASSWORD_PROPERTY,password,null);
220     }
221 
222     /* ------------------------------------------------------------ */
223     /**
224      * @deprecated use {@link #getSslKeyManagerFactoryAlgorithm()} or
225      * {@link #getSslTrustManagerFactoryAlgorithm()}
226      */
227     @Deprecated
228     public String getAlgorithm()
229     {
230         return getSslKeyManagerFactoryAlgorithm();
231     }
232 
233     /* ------------------------------------------------------------ */
234     /**
235      * @deprecated use {@link #setSslKeyManagerFactoryAlgorithm(String)} or
236      * {@link #setSslTrustManagerFactoryAlgorithm(String)}
237      */
238     @Deprecated
239     public void setAlgorithm(String algorithm)
240     {
241         setSslKeyManagerFactoryAlgorithm(algorithm);
242         setSslTrustManagerFactoryAlgorithm(algorithm);
243     }
244 
245     /* ------------------------------------------------------------ */
246     /**
247      * @see org.eclipse.jetty.server.ssl.SslConnector#getProtocol()
248      */
249     public String getProtocol()
250     {
251         return _protocol;
252     }
253 
254     /* ------------------------------------------------------------ */
255     /**
256      * @see org.eclipse.jetty.server.ssl.SslConnector#setProtocol(java.lang.String)
257      */
258     public void setProtocol(String protocol)
259     {
260         _protocol=protocol;
261     }
262 
263     /* ------------------------------------------------------------ */
264     /**
265      * @see org.eclipse.jetty.server.ssl.SslConnector#setKeystore(java.lang.String)
266      */
267     public void setKeystore(String keystore)
268     {
269         _keystorePath=keystore;
270     }
271 
272     /* ------------------------------------------------------------ */
273     /**
274      * @see org.eclipse.jetty.server.ssl.SslConnector#getKeystore()
275      */
276     public String getKeystore()
277     {
278         return _keystorePath;
279     }
280 
281     /* ------------------------------------------------------------ */
282     /**
283      * @see org.eclipse.jetty.server.ssl.SslConnector#getKeystoreType()
284      */
285     public String getKeystoreType()
286     {
287         return (_keystoreType);
288     }
289 
290     /* ------------------------------------------------------------ */
291     /**
292      * @see org.eclipse.jetty.server.ssl.SslConnector#getNeedClientAuth()
293      */
294     public boolean getNeedClientAuth()
295     {
296         return _needClientAuth;
297     }
298 
299     /* ------------------------------------------------------------ */
300     /**
301      * @see org.eclipse.jetty.server.ssl.SslConnector#getWantClientAuth()
302      */
303     public boolean getWantClientAuth()
304     {
305         return _wantClientAuth;
306     }
307 
308     /* ------------------------------------------------------------ */
309     /**
310      * @see org.eclipse.jetty.server.ssl.SslConnector#setNeedClientAuth(boolean)
311      */
312     public void setNeedClientAuth(boolean needClientAuth)
313     {
314         _needClientAuth=needClientAuth;
315     }
316 
317     /* ------------------------------------------------------------ */
318     /**
319      * @see org.eclipse.jetty.server.ssl.SslConnector#setWantClientAuth(boolean)
320      */
321     public void setWantClientAuth(boolean wantClientAuth)
322     {
323         _wantClientAuth=wantClientAuth;
324     }
325 
326     /* ------------------------------------------------------------ */
327     /**
328      * @see org.eclipse.jetty.server.ssl.SslConnector#setKeystoreType(java.lang.String)
329      */
330     public void setKeystoreType(String keystoreType)
331     {
332         _keystoreType=keystoreType;
333     }
334 
335     /* ------------------------------------------------------------ */
336     /**
337      * @see org.eclipse.jetty.server.ssl.SslConnector#getProvider()
338      */
339     public String getProvider()
340     {
341         return _provider;
342     }
343 
344     /* ------------------------------------------------------------ */
345     /**
346      * @see org.eclipse.jetty.server.ssl.SslConnector#getSecureRandomAlgorithm()
347      */
348     public String getSecureRandomAlgorithm()
349     {
350         return (this._secureRandomAlgorithm);
351     }
352 
353     /* ------------------------------------------------------------ */
354     /**
355      * @see org.eclipse.jetty.server.ssl.SslConnector#getSslKeyManagerFactoryAlgorithm()
356      */
357     public String getSslKeyManagerFactoryAlgorithm()
358     {
359         return (this._sslKeyManagerFactoryAlgorithm);
360     }
361 
362     /* ------------------------------------------------------------ */
363     /**
364      * @see org.eclipse.jetty.server.ssl.SslConnector#getSslTrustManagerFactoryAlgorithm()
365      */
366     public String getSslTrustManagerFactoryAlgorithm()
367     {
368         return (this._sslTrustManagerFactoryAlgorithm);
369     }
370 
371     /* ------------------------------------------------------------ */
372     /**
373      * @see org.eclipse.jetty.server.ssl.SslConnector#getTruststore()
374      */
375     public String getTruststore()
376     {
377         return _truststorePath;
378     }
379 
380     /* ------------------------------------------------------------ */
381     /**
382      * @see org.eclipse.jetty.server.ssl.SslConnector#getTruststoreType()
383      */
384     public String getTruststoreType()
385     {
386         return _truststoreType;
387     }
388 
389     /* ------------------------------------------------------------ */
390     /**
391      * @see org.eclipse.jetty.server.ssl.SslConnector#setProvider(java.lang.String)
392      */
393     public void setProvider(String provider)
394     {
395         _provider=provider;
396     }
397 
398     /* ------------------------------------------------------------ */
399     /**
400      * @see org.eclipse.jetty.server.ssl.SslConnector#setSecureRandomAlgorithm(java.lang.String)
401      */
402     public void setSecureRandomAlgorithm(String algorithm)
403     {
404         this._secureRandomAlgorithm=algorithm;
405     }
406 
407     /* ------------------------------------------------------------ */
408     /**
409      * @see org.eclipse.jetty.server.ssl.SslConnector#setSslKeyManagerFactoryAlgorithm(java.lang.String)
410      */
411     public void setSslKeyManagerFactoryAlgorithm(String algorithm)
412     {
413         this._sslKeyManagerFactoryAlgorithm=algorithm;
414     }
415 
416     /* ------------------------------------------------------------ */
417     /**
418      * @see org.eclipse.jetty.server.ssl.SslConnector#setSslTrustManagerFactoryAlgorithm(java.lang.String)
419      */
420     public void setSslTrustManagerFactoryAlgorithm(String algorithm)
421     {
422         this._sslTrustManagerFactoryAlgorithm=algorithm;
423     }
424 
425     /* ------------------------------------------------------------ */
426     /**
427      * @see org.eclipse.jetty.server.ssl.SslConnector#setTruststore(java.lang.String)
428      */
429     public void setTruststore(String truststore)
430     {
431         _truststorePath=truststore;
432     }
433 
434     /* ------------------------------------------------------------ */
435     /**
436      * @see org.eclipse.jetty.server.ssl.SslConnector#setTruststoreType(java.lang.String)
437      */
438     public void setTruststoreType(String truststoreType)
439     {
440         _truststoreType=truststoreType;
441     }
442 
443     /* ------------------------------------------------------------ */
444     /**
445      * @see org.eclipse.jetty.server.ssl.SslConnector#setSslContext(javax.net.ssl.SSLContext)
446      */
447     public void setSslContext(SSLContext sslContext)
448     {
449         _context = sslContext;
450     }
451 
452     /* ------------------------------------------------------------ */
453     /**
454      * @see org.eclipse.jetty.server.ssl.SslConnector#setSslContext(javax.net.ssl.SSLContext)
455      */
456     public SSLContext getSslContext()
457     {
458         try
459         {
460             if (_context == null)
461                 _context=createSSLContext();
462         }
463         catch(Exception e)
464         {
465             throw new RuntimeException(e);
466         }
467 
468         return _context;
469     }
470 
471     /* ------------------------------------------------------------ */
472     /**
473      * By default, we're confidential, given we speak SSL. But, if we've been
474      * told about an confidential port, and said port is not our port, then
475      * we're not. This allows separation of listeners providing INTEGRAL versus
476      * CONFIDENTIAL constraints, such as one SSL listener configured to require
477      * client certs providing CONFIDENTIAL, whereas another SSL listener not
478      * requiring client certs providing mere INTEGRAL constraints.
479      */
480     @Override
481     public boolean isConfidential(Request request)
482     {
483         final int confidentialPort=getConfidentialPort();
484         return confidentialPort==0||confidentialPort==request.getServerPort();
485     }
486 
487     /* ------------------------------------------------------------ */
488     /**
489      * By default, we're integral, given we speak SSL. But, if we've been told
490      * about an integral port, and said port is not our port, then we're not.
491      * This allows separation of listeners providing INTEGRAL versus
492      * CONFIDENTIAL constraints, such as one SSL listener configured to require
493      * client certs providing CONFIDENTIAL, whereas another SSL listener not
494      * requiring client certs providing mere INTEGRAL constraints.
495      */
496     @Override
497     public boolean isIntegral(Request request)
498     {
499         final int integralPort=getIntegralPort();
500         return integralPort==0||integralPort==request.getServerPort();
501     }
502 
503     /* ------------------------------------------------------------------------------- */
504     @Override
505     protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
506     {
507         SslSelectChannelEndPoint endp = new SslSelectChannelEndPoint(_sslBuffers,channel,selectSet,key,createSSLEngine(), SslSelectChannelConnector.this._maxIdleTime);
508         endp.setAllowRenegotiate(_allowRenegotiate);
509         return endp;
510     }
511 
512     /* ------------------------------------------------------------------------------- */
513     @Override
514     protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint)
515     {
516         HttpConnection connection=(HttpConnection)super.newConnection(channel,endpoint);
517         ((HttpParser)connection.getParser()).setForceContentBuffer(true);
518         return connection;
519     }
520 
521     /* ------------------------------------------------------------ */
522     protected SSLEngine createSSLEngine() throws IOException
523     {
524         SSLEngine engine = null;
525         try
526         {
527             engine = _context.createSSLEngine();
528             engine.setUseClientMode(false);
529 
530             if (_wantClientAuth)
531                 engine.setWantClientAuth(_wantClientAuth);
532             if (_needClientAuth)
533                 engine.setNeedClientAuth(_needClientAuth);
534 
535             if ((_excludeCipherSuites != null && _excludeCipherSuites.length > 0) || (_includeCipherSuites != null && _includeCipherSuites.length > 0))
536             {
537                 List<String> includedCSList;
538                 if (_includeCipherSuites != null)
539                 {
540                     includedCSList = Arrays.asList(_includeCipherSuites);
541                 }
542                 else
543                 {
544                     includedCSList = new ArrayList<String>();
545                 }
546                 List<String> excludedCSList;
547                 if (_excludeCipherSuites != null)
548                 {
549                     excludedCSList = Arrays.asList(_excludeCipherSuites);
550                 }
551                 else
552                 {
553                     excludedCSList = new ArrayList<String>();
554                 }
555                 String[] enabledCipherSuites = engine.getEnabledCipherSuites();
556                 List<String> enabledCSList = new ArrayList<String>(Arrays.asList(enabledCipherSuites));
557 
558                 String[] supportedCipherSuites = engine.getSupportedCipherSuites();
559                 List<String> supportedCSList = Arrays.asList(supportedCipherSuites);
560 
561                 for (String cipherName : includedCSList)
562                 {
563                     if ((!enabledCSList.contains(cipherName)) && supportedCSList.contains(cipherName))
564                     {
565                         enabledCSList.add(cipherName);
566                     }
567                 }
568 
569                 for (String cipherName : excludedCSList)
570                 {
571                     if (enabledCSList.contains(cipherName))
572                     {
573                         enabledCSList.remove(cipherName);
574                     }
575                 }
576                 enabledCipherSuites = enabledCSList.toArray(new String[0]);
577 
578                 engine.setEnabledCipherSuites(enabledCipherSuites);
579             }
580         }
581         catch (Exception e)
582         {
583             Log.warn("Error creating sslEngine -- closing this connector",e);
584             close();
585             throw new IllegalStateException(e);
586         }
587         return engine;
588     }
589 
590     @Override
591     protected void doStart() throws Exception
592     {
593     	if (_context == null)
594            _context=createSSLContext();
595 
596         SSLEngine engine=createSSLEngine();
597         SSLSession ssl_session=engine.getSession();
598 
599         ThreadLocalBuffers buffers = new ThreadLocalBuffers()
600         {
601             @Override
602             protected Buffer newBuffer(int size)
603             {
604                 if (getUseDirectBuffers())
605                     return new DirectNIOBuffer(size);
606                 return new IndirectNIOBuffer(size);
607             }
608             @Override
609             protected Buffer newHeader(int size)
610             {
611                 if (getUseDirectBuffers())
612                     return new DirectNIOBuffer(size);
613                 return new IndirectNIOBuffer(size);
614             }
615             @Override
616             protected boolean isHeader(Buffer buffer)
617             {
618                 return true;
619             }
620         };
621         buffers.setBufferSize(ssl_session.getApplicationBufferSize());
622         buffers.setHeaderSize(ssl_session.getApplicationBufferSize());
623         _sslBuffers=buffers;
624 
625         if (getRequestHeaderSize()<ssl_session.getApplicationBufferSize())
626             setRequestHeaderSize(ssl_session.getApplicationBufferSize());
627         if (getRequestBufferSize()<ssl_session.getApplicationBufferSize())
628             setRequestBufferSize(ssl_session.getApplicationBufferSize());
629 
630         super.doStart();
631     }
632 
633     public Buffers getSslBuffers()
634     {
635         return _sslBuffers;
636     }
637 
638     /* ------------------------------------------------------------ */
639     protected SSLContext createSSLContext() throws Exception
640     {
641         KeyManager[] keyManagers=getKeyManagers();
642         TrustManager[] trustManagers=getTrustManagers();
643         SecureRandom secureRandom=_secureRandomAlgorithm==null?null:SecureRandom.getInstance(_secureRandomAlgorithm);
644         SSLContext context=_provider==null?SSLContext.getInstance(_protocol):SSLContext.getInstance(_protocol,_provider);
645         context.init(keyManagers,trustManagers,secureRandom);
646         return context;
647     }
648 
649     /* ------------------------------------------------------------ */
650     protected KeyManager[] getKeyManagers() throws Exception
651     {
652         KeyStore keyStore = getKeyStore(_keystorePath, _keystoreType, _password==null?null:_password.toString());
653         KeyManagerFactory keyManagerFactory=KeyManagerFactory.getInstance(_sslKeyManagerFactoryAlgorithm);
654         keyManagerFactory.init(keyStore,_keyPassword==null?(_password==null?null:_password.toString().toCharArray()):_keyPassword.toString().toCharArray());
655         return keyManagerFactory.getKeyManagers();
656     }
657 
658     /* ------------------------------------------------------------ */
659     protected TrustManager[] getTrustManagers() throws Exception
660     {
661         if (_truststorePath == null)
662         {
663             _truststorePath = _keystorePath;
664             _truststoreType = _keystoreType;
665             _trustPassword = _password;
666             _sslTrustManagerFactoryAlgorithm = _sslKeyManagerFactoryAlgorithm;
667         }
668 
669         KeyStore trustStore = getKeyStore(_truststorePath, _truststoreType, _trustPassword == null ? null : _trustPassword.toString());
670         TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_sslTrustManagerFactoryAlgorithm);
671         trustManagerFactory.init(trustStore);
672         return trustManagerFactory.getTrustManagers();
673     }
674 
675     /* ------------------------------------------------------------ */
676     protected KeyStore getKeyStore(String keystorePath, String keystoreType, String keystorePassword) throws Exception
677     {
678         if (keystorePath == null)
679             return null;
680 
681         InputStream keystoreInputStream = Resource.newResource(keystorePath).getInputStream();
682         try
683         {
684             KeyStore keystore = KeyStore.getInstance(keystoreType);
685             keystore.load(keystoreInputStream, keystorePassword == null ? null : keystorePassword.toCharArray());
686             return keystore;
687         }
688         finally
689         {
690             keystoreInputStream.close();
691         }
692     }
693 }