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