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