View Javadoc

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