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