KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mortbay > http > JsseListener


1 // ========================================================================
2
// $Id: JsseListener.java,v 1.19 2005/11/03 18:21:59 gregwilkins Exp $
3
// Copyright 2000-2004 Mort Bay Consulting Pty. Ltd.
4
// ------------------------------------------------------------------------
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
// ========================================================================
15

16 package org.mortbay.http;
17
18 import java.io.ByteArrayInputStream JavaDoc;
19 import java.io.File JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.net.ServerSocket JavaDoc;
22 import java.net.Socket JavaDoc;
23 import java.security.KeyStore JavaDoc;
24 import java.security.cert.X509Certificate JavaDoc;
25
26 import javax.net.ssl.SSLException;
27 import javax.net.ssl.SSLPeerUnverifiedException;
28 import javax.net.ssl.SSLServerSocket;
29 import javax.net.ssl.SSLServerSocketFactory;
30 import javax.net.ssl.SSLSession;
31 import javax.net.ssl.SSLSocket;
32
33 import org.apache.commons.logging.Log;
34 import org.mortbay.log.LogFactory;
35 import org.mortbay.jetty.servlet.ServletSSL;
36 import org.mortbay.util.InetAddrPort;
37 import org.mortbay.util.LogSupport;
38
39 /* ------------------------------------------------------------ */
40 /**
41  * JSSE Socket Listener.
42  *
43  * This specialization of HttpListener is an abstract listener that can be used as the basis for a
44  * specific JSSE listener.
45  *
46  * This is heavily based on the work from Court Demas, which in turn is based on the work from Forge
47  * Research.
48  *
49  * @deprecated use SslListener
50  *
51  * @version $Id: JsseListener.java,v 1.19 2005/11/03 18:21:59 gregwilkins Exp $
52  * @author Greg Wilkins (gregw@mortbay.com)
53  * @author Court Demas (court@kiwiconsulting.com)
54  * @author Forge Research Pty Ltd ACN 003 491 576
55  * @author Jan Hlavatý
56  */

57 public abstract class JsseListener extends SocketListener
58 {
59     private static Log log = LogFactory.getLog(JsseListener.class);
60
61     /** String name of keystore location path property. */
62     public static final String JavaDoc KEYSTORE_PROPERTY = "jetty.ssl.keystore";
63
64     /** String name of keystore type property */
65     public static final String JavaDoc KEYSTORE_TYPE_PROPERTY = "jetty.ssl.keystore.type";
66
67     /** Default keystore type */
68     public static final String JavaDoc DEFAULT_KEYSTORE_TYPE = System.getProperty(KEYSTORE_TYPE_PROPERTY, KeyStore.getDefaultType());
69
70     /** String name of keystore provider name property */
71     public static final String JavaDoc KEYSTORE_PROVIDER_NAME_PROPERTY = "jetty.ssl.keystore.provider.name";
72
73     /** String name of keystore provider class property */
74     public static final String JavaDoc KEYSTORE_PROVIDER_CLASS_PROPERTY = "jetty.ssl.keystore.provider.class";
75
76     /** Default value for keystore provider class. null = use default */
77     public static final String JavaDoc DEFAULT_KEYSTORE_PROVIDER_CLASS = System.getProperty(KEYSTORE_PROVIDER_CLASS_PROPERTY);
78
79     /** Default value for the keystore location path. */
80     public static final String JavaDoc DEFAULT_KEYSTORE = System.getProperty("user.home") + File.separator + ".keystore";
81
82     /** Default value for keystore provider name. null = use default */
83     public static final String JavaDoc DEFAULT_KEYSTORE_PROVIDER_NAME = System.getProperty(KEYSTORE_PROVIDER_NAME_PROPERTY);
84
85     /** String name of keystore password property. */
86     public static final String JavaDoc PASSWORD_PROPERTY = "jetty.ssl.password";
87
88     /** String name of key password property. */
89     public static final String JavaDoc KEYPASSWORD_PROPERTY = "jetty.ssl.keypassword";
90
91     /**
92      * The name of the SSLSession attribute that will contain any cached information.
93      */

94     static final String JavaDoc CACHED_INFO_ATTR = CachedInfo.class.getName();
95
96     /** Set to true if we require client certificate authentication. */
97     private boolean _needClientAuth = false;
98
99     /* ------------------------------------------------------------ */
100     /**
101      * Constructor.
102      */

103     public JsseListener()
104     {
105         super();
106         setDefaultScheme(HttpMessage.__SSL_SCHEME);
107     }
108
109     /* ------------------------------------------------------------ */
110     /**
111      * Constructor.
112      *
113      * @param p_address
114      */

115     public JsseListener(InetAddrPort p_address)
116     {
117         super(p_address);
118         if (p_address.getPort() == 0)
119         {
120             p_address.setPort(443);
121             setPort(443);
122         }
123         setDefaultScheme(HttpMessage.__SSL_SCHEME);
124     }
125
126     /* ------------------------------------------------------------ */
127     /**
128      * Set the value of the needClientAuth property
129      *
130      * @param needClientAuth true iff we require client certificate authentication.
131      */

132     public void setNeedClientAuth(boolean needClientAuth)
133     {
134         _needClientAuth = needClientAuth;
135     }
136
137     /* ------------------------------------------------------------ */
138     public boolean getNeedClientAuth()
139     {
140         return _needClientAuth;
141     }
142
143     /* ------------------------------------------------------------ */
144     /**
145      * By default, we're integral, given we speak SSL. But, if we've been told about an integral
146      * port, and said port is not our port, then we're not. This allows separation of listeners
147      * providing INTEGRAL versus CONFIDENTIAL constraints, such as one SSL listener configured to
148      * require client certs providing CONFIDENTIAL, whereas another SSL listener not requiring
149      * client certs providing mere INTEGRAL constraints.
150      */

151     public boolean isIntegral(HttpConnection connection)
152     {
153         final int integralPort = getIntegralPort();
154         return integralPort == 0 || integralPort == getPort();
155     }
156
157     /* ------------------------------------------------------------ */
158     /**
159      * By default, we're confidential, given we speak SSL. But, if we've been told about an
160      * confidential port, and said port is not our port, then we're not. This allows separation of
161      * listeners providing INTEGRAL versus CONFIDENTIAL constraints, such as one SSL listener
162      * configured to require client certs providing CONFIDENTIAL, whereas another SSL listener not
163      * requiring client certs providing mere INTEGRAL constraints.
164      */

165     public boolean isConfidential(HttpConnection connection)
166     {
167         final int confidentialPort = getConfidentialPort();
168         return confidentialPort == 0 || confidentialPort == getPort();
169     }
170
171     /* ------------------------------------------------------------ */
172     protected abstract SSLServerSocketFactory createFactory() throws Exception JavaDoc;
173
174     /* ------------------------------------------------------------ */
175     /**
176      * @param p_address
177      * @param p_acceptQueueSize
178      * @return
179      * @exception IOException
180      */

181     protected ServerSocket JavaDoc newServerSocket(InetAddrPort p_address, int p_acceptQueueSize) throws IOException JavaDoc
182     {
183         SSLServerSocketFactory factory = null;
184         SSLServerSocket socket = null;
185
186         try
187         {
188             factory = createFactory();
189
190             if (p_address == null)
191             {
192                 socket = (SSLServerSocket) factory.createServerSocket(0, p_acceptQueueSize);
193             }
194             else
195             {
196                 socket = (SSLServerSocket) factory.createServerSocket(p_address.getPort(), p_acceptQueueSize, p_address.getInetAddress());
197             }
198
199             socket.setNeedClientAuth(_needClientAuth);
200             log.info("JsseListener.needClientAuth=" + _needClientAuth);
201         }
202         catch (IOException JavaDoc e)
203         {
204             throw e;
205         }
206         catch (Exception JavaDoc e)
207         {
208             log.warn(LogSupport.EXCEPTION, e);
209             throw new IOException JavaDoc("Could not create JsseListener: " + e.toString());
210         }
211         return socket;
212     }
213
214     /* ------------------------------------------------------------ */
215     /**
216      * @param p_serverSocket
217      * @return
218      * @exception IOException
219      */

220     protected Socket JavaDoc accept(ServerSocket JavaDoc p_serverSocket) throws IOException JavaDoc
221     {
222         try
223         {
224             SSLSocket s = (SSLSocket) p_serverSocket.accept();
225             if (getMaxIdleTimeMs() > 0)
226                 s.setSoTimeout(getMaxIdleTimeMs());
227             s.startHandshake(); // block until SSL handshaking is done
228
return s;
229         }
230         catch (SSLException e)
231         {
232             log.warn(LogSupport.EXCEPTION, e);
233             throw new IOException JavaDoc(e.getMessage());
234         }
235     }
236
237     /* ------------------------------------------------------------ */
238     /**
239      * Allow the Listener a chance to customise the request. before the server does its stuff. <br>
240      * This allows the required attributes to be set for SSL requests. <br>
241      * The requirements of the Servlet specs are:
242      * <ul>
243      * <li> an attribute named "javax.servlet.request.cipher_suite" of type String.</li>
244      * <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li>
245      * <li> an attribute named "javax.servlet.request.X509Certificate" of type
246      * java.security.cert.X509Certificate[]. This is an array of objects of type X509Certificate,
247      * the order of this array is defined as being in ascending order of trust. The first
248      * certificate in the chain is the one set by the client, the next is the one used to
249      * authenticate the first, and so on. </li>
250      * </ul>
251      *
252      * @param socket The Socket the request arrived on. This should be a javax.net.ssl.SSLSocket.
253      * @param request HttpRequest to be customised.
254      */

255     protected void customizeRequest(Socket JavaDoc socket, HttpRequest request)
256     {
257         super.customizeRequest(socket, request);
258
259         if (!(socket instanceof javax.net.ssl.SSLSocket))
260             return; // I'm tempted to let it throw an exception...
261

262         try
263         {
264             SSLSocket sslSocket = (SSLSocket) socket;
265             SSLSession sslSession = sslSocket.getSession();
266             String JavaDoc cipherSuite = sslSession.getCipherSuite();
267             Integer JavaDoc keySize;
268             X509Certificate JavaDoc[] certs;
269
270             CachedInfo cachedInfo = (CachedInfo) sslSession.getValue(CACHED_INFO_ATTR);
271             if (cachedInfo != null)
272             {
273                 keySize = cachedInfo.getKeySize();
274                 certs = cachedInfo.getCerts();
275             }
276             else
277             {
278                 keySize = new Integer JavaDoc(ServletSSL.deduceKeyLength(cipherSuite));
279                 certs = getCertChain(sslSession);
280                 cachedInfo = new CachedInfo(keySize, certs);
281                 sslSession.putValue(CACHED_INFO_ATTR, cachedInfo);
282             }
283
284             if (certs != null)
285                 request.setAttribute("javax.servlet.request.X509Certificate", certs);
286             else if (_needClientAuth) // Sanity check
287
throw new HttpException(HttpResponse.__403_Forbidden);
288
289             request.setAttribute("javax.servlet.request.cipher_suite", cipherSuite);
290             request.setAttribute("javax.servlet.request.key_size", keySize);
291         }
292         catch (Exception JavaDoc e)
293         {
294             log.warn(LogSupport.EXCEPTION, e);
295         }
296     }
297
298     /**
299      * Return the chain of X509 certificates used to negotiate the SSL Session.
300      * <p>
301      * Note: in order to do this we must convert a javax.security.cert.X509Certificate[], as used by
302      * JSSE to a java.security.cert.X509Certificate[],as required by the Servlet specs.
303      *
304      * @param sslSession the javax.net.ssl.SSLSession to use as the source of the cert chain.
305      * @return the chain of java.security.cert.X509Certificates used to negotiate the SSL
306      * connection. <br>
307      * Will be null if the chain is missing or empty.
308      */

309     private static X509Certificate JavaDoc[] getCertChain(SSLSession sslSession)
310     {
311         try
312         {
313             javax.security.cert.X509Certificate javaxCerts[] = sslSession.getPeerCertificateChain();
314             if (javaxCerts == null || javaxCerts.length == 0)
315                 return null;
316
317             int length = javaxCerts.length;
318             X509Certificate JavaDoc[] javaCerts = new X509Certificate JavaDoc[length];
319
320             java.security.cert.CertificateFactory JavaDoc cf = java.security.cert.CertificateFactory.getInstance("X.509");
321             for (int i = 0; i < length; i++)
322             {
323                 byte bytes[] = javaxCerts[i].getEncoded();
324                 ByteArrayInputStream JavaDoc stream = new ByteArrayInputStream JavaDoc(bytes);
325                 javaCerts[i] = (X509Certificate JavaDoc) cf.generateCertificate(stream);
326             }
327
328             return javaCerts;
329         }
330         catch (SSLPeerUnverifiedException pue)
331         {
332             return null;
333         }
334         catch (Exception JavaDoc e)
335         {
336             log.warn(LogSupport.EXCEPTION, e);
337             return null;
338         }
339     }
340
341     /**
342      * Simple bundle of information that is cached in the SSLSession. Stores the effective keySize
343      * and the client certificate chain.
344      */

345     private class CachedInfo
346     {
347         private Integer JavaDoc _keySize;
348         private X509Certificate JavaDoc[] _certs;
349
350         CachedInfo(Integer JavaDoc keySize, X509Certificate JavaDoc[] certs)
351         {
352             this._keySize = keySize;
353             this._certs = certs;
354         }
355
356         Integer JavaDoc getKeySize()
357         {
358             return _keySize;
359         }
360
361         X509Certificate JavaDoc[] getCerts()
362         {
363             return _certs;
364         }
365     }
366 }
367
Popular Tags