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