View Javadoc

1   // ========================================================================
2   // Copyright 2000-2005 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at 
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14  
15  package org.mortbay.jetty.security;
16  
17  import java.io.ByteArrayInputStream;
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.net.InetAddress;
22  import java.net.ServerSocket;
23  import java.net.Socket;
24  import java.net.SocketAddress;
25  import java.security.KeyStore;
26  import java.security.SecureRandom;
27  import java.security.Security;
28  import java.security.cert.X509Certificate;
29  import java.util.ArrayList;
30  import java.util.Arrays;
31  import java.util.Iterator;
32  import java.util.List;
33  
34  import javax.net.ssl.HandshakeCompletedEvent;
35  import javax.net.ssl.HandshakeCompletedListener;
36  import javax.net.ssl.KeyManager;
37  import javax.net.ssl.KeyManagerFactory;
38  import javax.net.ssl.SSLContext;
39  import javax.net.ssl.SSLException;
40  import javax.net.ssl.SSLPeerUnverifiedException;
41  import javax.net.ssl.SSLServerSocket;
42  import javax.net.ssl.SSLServerSocketFactory;
43  import javax.net.ssl.SSLSession;
44  import javax.net.ssl.SSLSocket;
45  import javax.net.ssl.TrustManager;
46  import javax.net.ssl.TrustManagerFactory;
47  
48  import org.mortbay.io.EndPoint;
49  import org.mortbay.io.bio.SocketEndPoint;
50  import org.mortbay.jetty.HttpSchemes;
51  import org.mortbay.jetty.Request;
52  import org.mortbay.jetty.bio.SocketConnector;
53  import org.mortbay.log.Log;
54  import org.mortbay.resource.Resource;
55  
56  /* ------------------------------------------------------------ */
57  /**
58   * JSSE Socket Listener.
59   * 
60   * This specialization of HttpListener is an abstract listener that can be used as the basis for a
61   * specific JSSE listener.
62   * 
63   * This is heavily based on the work from Court Demas, which in turn is based on the work from Forge
64   * Research.
65   * 
66   * @author Greg Wilkins (gregw@mortbay.com)
67   * @author Court Demas (court@kiwiconsulting.com)
68   * @author Forge Research Pty Ltd ACN 003 491 576
69   * @author Jan Hlavat�
70   */
71  public class SslSocketConnector extends SocketConnector
72  {
73      /**
74       * The name of the SSLSession attribute that will contain any cached information.
75       */
76      static final String CACHED_INFO_ATTR = CachedInfo.class.getName();
77  
78      /** Default value for the keystore location path. */
79      public static final String DEFAULT_KEYSTORE = System.getProperty("user.home") + File.separator
80              + ".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      /**
89       * Return the chain of X509 certificates used to negotiate the SSL Session.
90       * <p>
91       * Note: in order to do this we must convert a javax.security.cert.X509Certificate[], as used by
92       * JSSE to a java.security.cert.X509Certificate[],as required by the Servlet specs.
93       * 
94       * @param sslSession the javax.net.ssl.SSLSession to use as the source of the cert chain.
95       * @return the chain of java.security.cert.X509Certificates used to negotiate the SSL
96       *         connection. <br>
97       *         Will be null if the chain is missing or empty.
98       */
99      private static X509Certificate[] getCertChain(SSLSession sslSession)
100     {
101         try
102         {
103             javax.security.cert.X509Certificate javaxCerts[] = sslSession.getPeerCertificateChain();
104             if (javaxCerts == null || javaxCerts.length == 0)
105                 return null;
106 
107             int length = javaxCerts.length;
108             X509Certificate[] javaCerts = new X509Certificate[length];
109 
110             java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance("X.509");
111             for (int i = 0; i < length; i++)
112             {
113                 byte bytes[] = javaxCerts[i].getEncoded();
114                 ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
115                 javaCerts[i] = (X509Certificate) cf.generateCertificate(stream);
116             }
117 
118             return javaCerts;
119         }
120         catch (SSLPeerUnverifiedException pue)
121         {
122             return null;
123         }
124         catch (Exception e)
125         {
126             Log.warn(Log.EXCEPTION, e);
127             return null;
128         }
129     }
130 
131 
132     /** Default value for the cipher Suites. */
133     private String _excludeCipherSuites[] = null;
134     
135     /** Default value for the keystore location path. */
136     private String _keystore=DEFAULT_KEYSTORE ;
137     private String _keystoreType = "JKS"; // type of the key store
138     
139     /** Set to true if we require client certificate authentication. */
140     private boolean _needClientAuth = false;
141     private transient Password _password;
142     private transient Password _keyPassword;
143     private transient Password _trustPassword;
144     private String _protocol= "TLS";
145     private String _provider;
146     private String _secureRandomAlgorithm; // cert algorithm
147     private String _sslKeyManagerFactoryAlgorithm = (Security.getProperty("ssl.KeyManagerFactory.algorithm")==null?"SunX509":Security.getProperty("ssl.KeyManagerFactory.algorithm")); // cert algorithm
148     private String _sslTrustManagerFactoryAlgorithm = (Security.getProperty("ssl.TrustManagerFactory.algorithm")==null?"SunX509":Security.getProperty("ssl.TrustManagerFactory.algorithm")); // cert algorithm
149     
150     private String _truststore;
151     private String _truststoreType = "JKS"; // type of the key store
152 
153     /** Set to true if we would like client certificate authentication. */
154     private boolean _wantClientAuth = false;
155     private int _handshakeTimeout = 0; //0 means use maxIdleTime
156 
157     private boolean _allowRenegotiate =false;
158 
159     /* ------------------------------------------------------------ */
160     /**
161      * Constructor.
162      */
163     public SslSocketConnector()
164     {
165         super();
166     }
167 
168 
169     /* ------------------------------------------------------------ */
170     /**
171      * @return True if SSL re-negotiation is allowed (default false)
172      */
173     public boolean isAllowRenegotiate()
174     {
175         return _allowRenegotiate;
176     }
177 
178     /* ------------------------------------------------------------ */
179     /**
180      * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
181      * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
182      * does not have CVE-2009-3555 fixed, then re-negotiation should 
183      * not be allowed.
184      * @param allowRenegotiate true if re-negotiation is allowed (default false)
185      */
186     public void setAllowRenegotiate(boolean allowRenegotiate)
187     {
188         _allowRenegotiate = allowRenegotiate;
189     }
190 
191     /* ------------------------------------------------------------ */
192     public void accept(int acceptorID)
193         throws IOException, InterruptedException
194     {   
195         try
196         {
197             Socket socket = _serverSocket.accept();
198             configure(socket);
199 
200             Connection connection=new SslConnection(socket);
201             connection.dispatch();
202         }
203         catch(SSLException e)
204         {
205             Log.warn(e);
206             try
207             {
208                 stop();
209             }
210             catch(Exception e2)
211             {
212                 Log.warn(e2);
213                 throw new IllegalStateException(e2.getMessage());
214             }
215         }
216     }
217     
218     /* ------------------------------------------------------------ */
219     protected void configure(Socket socket)
220         throws IOException
221     {   
222         super.configure(socket);
223     }
224 
225     /* ------------------------------------------------------------ */
226     protected SSLServerSocketFactory createFactory() 
227         throws Exception
228     {
229         if (_truststore==null)
230         {
231             _truststore=_keystore;
232             _truststoreType=_keystoreType;
233         }
234 
235         KeyManager[] keyManagers = null;
236         InputStream keystoreInputStream = null;
237         if (_keystore != null)
238         	keystoreInputStream = Resource.newResource(_keystore).getInputStream();
239         KeyStore keyStore = KeyStore.getInstance(_keystoreType);
240         keyStore.load(keystoreInputStream, _password==null?null:_password.toString().toCharArray());
241 
242         KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(_sslKeyManagerFactoryAlgorithm);        
243         keyManagerFactory.init(keyStore,_keyPassword==null?null:_keyPassword.toString().toCharArray());
244         keyManagers = keyManagerFactory.getKeyManagers();
245 
246         TrustManager[] trustManagers = null;
247         InputStream truststoreInputStream = null;
248         if (_truststore != null)
249         	truststoreInputStream = Resource.newResource(_truststore).getInputStream();
250         KeyStore trustStore = KeyStore.getInstance(_truststoreType);
251         trustStore.load(truststoreInputStream,_trustPassword==null?null:_trustPassword.toString().toCharArray());
252         
253         TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_sslTrustManagerFactoryAlgorithm);
254         trustManagerFactory.init(trustStore);
255         trustManagers = trustManagerFactory.getTrustManagers();
256         
257 
258         SecureRandom secureRandom = _secureRandomAlgorithm==null?null:SecureRandom.getInstance(_secureRandomAlgorithm);
259 
260         SSLContext context = _provider==null?SSLContext.getInstance(_protocol):SSLContext.getInstance(_protocol, _provider);
261 
262         context.init(keyManagers, trustManagers, secureRandom);
263 
264         return context.getServerSocketFactory();
265     }
266 
267     /* ------------------------------------------------------------ */
268     /**
269      * Allow the Listener a chance to customise the request. before the server does its stuff. <br>
270      * This allows the required attributes to be set for SSL requests. <br>
271      * The requirements of the Servlet specs are:
272      * <ul>
273      * <li> an attribute named "javax.servlet.request.cipher_suite" of type String.</li>
274      * <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li>
275      * <li> an attribute named "javax.servlet.request.X509Certificate" of type
276      * java.security.cert.X509Certificate[]. This is an array of objects of type X509Certificate,
277      * the order of this array is defined as being in ascending order of trust. The first
278      * certificate in the chain is the one set by the client, the next is the one used to
279      * authenticate the first, and so on. </li>
280      * </ul>
281      * 
282      * @param endpoint The Socket the request arrived on. 
283      *        This should be a {@link SocketEndPoint} wrapping a {@link SSLSocket}.
284      * @param request HttpRequest to be customised.
285      */
286     public void customize(EndPoint endpoint, Request request)
287         throws IOException
288     {
289         super.customize(endpoint, request);
290         request.setScheme(HttpSchemes.HTTPS);
291         
292         SocketEndPoint socket_end_point = (SocketEndPoint)endpoint;
293         SSLSocket sslSocket = (SSLSocket)socket_end_point.getTransport();
294         
295         try
296         {
297             SSLSession sslSession = sslSocket.getSession();
298             String cipherSuite = sslSession.getCipherSuite();
299             Integer keySize;
300             X509Certificate[] certs;
301 
302             CachedInfo cachedInfo = (CachedInfo) sslSession.getValue(CACHED_INFO_ATTR);
303             if (cachedInfo != null)
304             {
305                 keySize = cachedInfo.getKeySize();
306                 certs = cachedInfo.getCerts();
307             }
308             else
309             {
310                 keySize = new Integer(ServletSSL.deduceKeyLength(cipherSuite));
311                 certs = getCertChain(sslSession);
312                 cachedInfo = new CachedInfo(keySize, certs);
313                 sslSession.putValue(CACHED_INFO_ATTR, cachedInfo);
314             }
315 
316             if (certs != null)
317                 request.setAttribute("javax.servlet.request.X509Certificate", certs);
318             else if (_needClientAuth) // Sanity check
319                 throw new IllegalStateException("no client auth");
320 
321             request.setAttribute("javax.servlet.request.cipher_suite", cipherSuite);
322             request.setAttribute("javax.servlet.request.key_size", keySize);
323         }
324         catch (Exception e)
325         {
326             Log.warn(Log.EXCEPTION, e);
327         }
328     }
329 
330     /* ------------------------------------------------------------ */    
331     public String[] getExcludeCipherSuites() {
332         return _excludeCipherSuites;
333     }
334 
335     /* ------------------------------------------------------------ */
336     public String getKeystore()
337     {
338         return _keystore;
339     }
340 
341     /* ------------------------------------------------------------ */
342     public String getKeystoreType() 
343     {
344         return (_keystoreType);
345     }
346 
347     /* ------------------------------------------------------------ */
348     public boolean getNeedClientAuth()
349     {
350         return _needClientAuth;
351     }
352 
353     /* ------------------------------------------------------------ */
354     public String getProtocol() 
355     {
356         return _protocol;
357     }
358 
359     /* ------------------------------------------------------------ */
360     public String getProvider() {
361 	return _provider;
362     }
363 
364     /* ------------------------------------------------------------ */
365     public String getSecureRandomAlgorithm() 
366     {
367         return (this._secureRandomAlgorithm);
368     }
369 
370     /* ------------------------------------------------------------ */
371     public String getSslKeyManagerFactoryAlgorithm() 
372     {
373         return (this._sslKeyManagerFactoryAlgorithm);
374     }
375 
376     /* ------------------------------------------------------------ */
377     public String getSslTrustManagerFactoryAlgorithm() 
378     {
379         return (this._sslTrustManagerFactoryAlgorithm);
380     }
381 
382     /* ------------------------------------------------------------ */
383     public String getTruststore()
384     {
385         return _truststore;
386     }
387 
388     /* ------------------------------------------------------------ */
389     public String getTruststoreType()
390     {
391         return _truststoreType;
392     }
393 
394     /* ------------------------------------------------------------ */
395     public boolean getWantClientAuth()
396     {
397         return _wantClientAuth;
398     }
399 
400     /* ------------------------------------------------------------ */
401     /**
402      * By default, we're confidential, given we speak SSL. But, if we've been told about an
403      * confidential port, and said port is not our port, then we're not. This allows separation of
404      * listeners providing INTEGRAL versus CONFIDENTIAL constraints, such as one SSL listener
405      * configured to require client certs providing CONFIDENTIAL, whereas another SSL listener not
406      * requiring client certs providing mere INTEGRAL constraints.
407      */
408     public boolean isConfidential(Request request)
409     {
410         final int confidentialPort = getConfidentialPort();
411         return confidentialPort == 0 || confidentialPort == request.getServerPort();
412     }
413     
414     /* ------------------------------------------------------------ */
415     /**
416      * By default, we're integral, given we speak SSL. But, if we've been told about an integral
417      * port, and said port is not our port, then we're not. This allows separation of listeners
418      * providing INTEGRAL versus CONFIDENTIAL constraints, such as one SSL listener configured to
419      * require client certs providing CONFIDENTIAL, whereas another SSL listener not requiring
420      * client certs providing mere INTEGRAL constraints.
421      */
422     public boolean isIntegral(Request request)
423     {
424         final int integralPort = getIntegralPort();
425         return integralPort == 0 || integralPort == request.getServerPort();
426     }
427     
428     /* ------------------------------------------------------------ */
429     /**
430      * @param addr The {@link SocketAddress address} that this server should listen on 
431      * @param backlog See {@link ServerSocket#bind(java.net.SocketAddress, int)}
432      * @return A new {@link ServerSocket socket object} bound to the supplied address with all other
433      * settings as per the current configuration of this connector. 
434      * @see #setWantClientAuth
435      * @see #setNeedClientAuth
436      * @see #setCipherSuites
437      * @exception IOException
438      */
439 
440     /* ------------------------------------------------------------ */
441     protected ServerSocket newServerSocket(String host, int port,int backlog) throws IOException
442     {
443         SSLServerSocketFactory factory = null;
444         SSLServerSocket socket = null;
445 
446         try
447         {
448             factory = createFactory();
449 
450             socket = (SSLServerSocket) (host==null?
451                             factory.createServerSocket(port,backlog):
452                             factory.createServerSocket(port,backlog,InetAddress.getByName(host)));
453 
454             if (_wantClientAuth)
455                 socket.setWantClientAuth(_wantClientAuth);
456             if (_needClientAuth)
457                 socket.setNeedClientAuth(_needClientAuth);
458 
459             if (_excludeCipherSuites != null && _excludeCipherSuites.length >0) 
460             {
461                 List excludedCSList = Arrays.asList(_excludeCipherSuites);
462                 String[] enabledCipherSuites = socket.getEnabledCipherSuites();
463             	List enabledCSList = new ArrayList(Arrays.asList(enabledCipherSuites));
464             	Iterator exIter = excludedCSList.iterator();
465 
466                 while (exIter.hasNext())
467             	{
468             	    String cipherName = (String)exIter.next();
469                     if (enabledCSList.contains(cipherName))
470                     {
471                         enabledCSList.remove(cipherName);
472                     }
473             	}
474                 enabledCipherSuites = (String[])enabledCSList.toArray(new String[enabledCSList.size()]);
475 
476                 socket.setEnabledCipherSuites(enabledCipherSuites);
477             }
478             
479         }
480         catch (IOException e)
481         {
482             throw e;
483         }
484         catch (Exception e)
485         {
486             Log.warn(e.toString());
487             Log.debug(e);
488             throw new IOException("!JsseListener: " + e);
489         }
490         return socket;
491     }
492 
493     /* ------------------------------------------------------------ */
494     /** 
495      * @author Tony Jiang
496      */
497     public void setExcludeCipherSuites(String[] cipherSuites) {
498         this._excludeCipherSuites = cipherSuites;
499     }
500 
501     /* ------------------------------------------------------------ */
502     public void setKeyPassword(String password)
503     {
504         _keyPassword = Password.getPassword(KEYPASSWORD_PROPERTY,password,null);
505     }
506 
507     /* ------------------------------------------------------------ */
508     /**
509      * @param keystore The resource path to the keystore, or null for built in keystores.
510      */
511     public void setKeystore(String keystore)
512     {
513         _keystore = keystore;
514     }
515 
516     /* ------------------------------------------------------------ */
517     public void setKeystoreType(String keystoreType) 
518     {
519         _keystoreType = keystoreType;
520     }
521 
522     /* ------------------------------------------------------------ */
523     /**
524      * Set the value of the needClientAuth property
525      * 
526      * @param needClientAuth true iff we require client certificate authentication.
527      */
528     public void setNeedClientAuth(boolean needClientAuth)
529     {
530         _needClientAuth = needClientAuth;
531     }
532     
533     /* ------------------------------------------------------------ */
534     public void setPassword(String password)
535     {
536         _password = Password.getPassword(PASSWORD_PROPERTY,password,null);
537     }
538     
539     /* ------------------------------------------------------------ */
540     public void setTrustPassword(String password)
541     {
542         _trustPassword = Password.getPassword(PASSWORD_PROPERTY,password,null);
543     }
544 
545     /* ------------------------------------------------------------ */
546     public void setProtocol(String protocol) 
547     {
548         _protocol = protocol;
549     }
550 
551     /* ------------------------------------------------------------ */
552     public void setProvider(String _provider) {
553 	this._provider = _provider;
554     }
555 
556     /* ------------------------------------------------------------ */
557     public void setSecureRandomAlgorithm(String algorithm) 
558     {
559         this._secureRandomAlgorithm = algorithm;
560     }
561 
562     /* ------------------------------------------------------------ */
563     public void setSslKeyManagerFactoryAlgorithm(String algorithm) 
564     {
565         this._sslKeyManagerFactoryAlgorithm = algorithm;
566     }
567     
568     /* ------------------------------------------------------------ */
569     public void setSslTrustManagerFactoryAlgorithm(String algorithm) 
570     {
571         this._sslTrustManagerFactoryAlgorithm = algorithm;
572     }
573 
574 
575     public void setTruststore(String truststore)
576     {
577         _truststore = truststore;
578     }
579     
580 
581     public void setTruststoreType(String truststoreType)
582     {
583         _truststoreType = truststoreType;
584     }
585 
586     /* ------------------------------------------------------------ */
587     /**
588      * Set the value of the _wantClientAuth property. This property is used when
589      * {@link #newServerSocket(SocketAddress, int) opening server sockets}.
590      * 
591      * @param wantClientAuth true iff we want client certificate authentication.
592      * @see SSLServerSocket#setWantClientAuth
593      */
594     public void setWantClientAuth(boolean wantClientAuth)
595     {
596         _wantClientAuth = wantClientAuth;
597     }
598 
599     /**
600      * Set the time in milliseconds for so_timeout during ssl handshaking
601      * @param msec a non-zero value will be used to set so_timeout during
602      * ssl handshakes. A zero value means the maxIdleTime is used instead.
603      */
604     public void setHandshakeTimeout (int msec)
605     {
606         _handshakeTimeout = msec;
607     }
608     
609     
610     public int getHandshakeTimeout ()
611     {
612         return _handshakeTimeout;
613     }
614     /**
615      * Simple bundle of information that is cached in the SSLSession. Stores the effective keySize
616      * and the client certificate chain.
617      */
618     private class CachedInfo
619     {
620         private X509Certificate[] _certs;
621         private Integer _keySize;
622 
623         CachedInfo(Integer keySize, X509Certificate[] certs)
624         {
625             this._keySize = keySize;
626             this._certs = certs;
627         }
628 
629         X509Certificate[] getCerts()
630         {
631             return _certs;
632         }
633 
634         Integer getKeySize()
635         {
636             return _keySize;
637         }
638     }
639     
640     
641     public class SslConnection extends Connection
642     {
643         public SslConnection(Socket socket) throws IOException
644         {
645             super(socket);
646         }
647         
648         public void run()
649         {
650             try
651             {
652                 int handshakeTimeout = getHandshakeTimeout();
653                 int oldTimeout = _socket.getSoTimeout();
654                 if (handshakeTimeout > 0)            
655                     _socket.setSoTimeout(handshakeTimeout);
656 
657                 final SSLSocket ssl=(SSLSocket)_socket;
658                 ssl.addHandshakeCompletedListener(new HandshakeCompletedListener()
659                 {
660                     boolean handshook=false;
661                     public void handshakeCompleted(HandshakeCompletedEvent event)
662                     {
663                         if (handshook)
664                         {
665                             if (!_allowRenegotiate)
666                             {
667                                 Log.warn("SSL renegotiate denied: "+ssl);
668                                 try{ssl.close();}catch(IOException e){Log.warn(e);}
669                             }
670                         }
671                         else
672                             handshook=true;
673                     }
674                 });
675                 ssl.startHandshake();
676 
677                 if (handshakeTimeout>0)
678                     _socket.setSoTimeout(oldTimeout);
679 
680                 super.run();
681             }
682             catch (SSLException e)
683             {
684                 Log.warn(e); 
685                 try{close();}
686                 catch(IOException e2){Log.ignore(e2);}
687             }
688             catch (IOException e)
689             {
690                 Log.debug(e);
691                 try{close();}
692                 catch(IOException e2){Log.ignore(e2);}
693             } 
694         }
695     }
696 
697 }