KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > HsqlSocketFactorySecure


1 /* Copyright (c) 2001-2005, The HSQL Development Group
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the HSQL Development Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

30
31
32 package org.hsqldb;
33
34 import java.net.InetAddress JavaDoc;
35 import java.net.ServerSocket JavaDoc;
36 import java.net.Socket JavaDoc;
37 import java.net.UnknownHostException JavaDoc;
38 import java.security.Principal JavaDoc;
39 import java.security.Provider JavaDoc;
40 import java.security.PublicKey JavaDoc;
41 import java.security.Security JavaDoc;
42
43 import javax.net.ssl.HandshakeCompletedEvent;
44 import javax.net.ssl.HandshakeCompletedListener;
45 import javax.net.ssl.SSLServerSocket;
46 import javax.net.ssl.SSLServerSocketFactory;
47 import javax.net.ssl.SSLSession;
48 import javax.net.ssl.SSLSocket;
49 import javax.net.ssl.SSLSocketFactory;
50 import javax.security.cert.X509Certificate;
51
52 import org.hsqldb.lib.StringConverter;
53
54 /**
55  * The default secure socket factory implementation.
56  *
57  * @author unsaved@users
58  * @author boucherb@users
59  * @version 1.7.2
60  * @since 1.7.2
61  */

62 public final class HsqlSocketFactorySecure extends HsqlSocketFactory
63 implements HandshakeCompletedListener {
64
65 // --------------------------------- members -----------------------------------
66

67     /** The underlying socket factory implementation. */
68     protected Object JavaDoc socketFactory;
69
70     /** The underlying server socket factory implementation. */
71     protected Object JavaDoc serverSocketFactory;
72
73     /**
74      * Monitor object to guard against conncurrent modification
75      * of the underlying socket factory implementation member.
76      */

77     protected final Object JavaDoc socket_factory_mutex = new Object JavaDoc();
78
79     /**
80      * Monitor object to guard against concurrent modification of
81      * the underlying server socket factory implementation member.
82      */

83     protected final Object JavaDoc server_socket_factory_mutex = new Object JavaDoc();
84
85 // ------------------------------ constructors ---------------------------------
86

87     /**
88      * External construction disabled. New factory instances are retreived
89      * through the newHsqlSocketFactory method instead.
90      */

91     protected HsqlSocketFactorySecure() throws Exception JavaDoc {
92
93         super();
94
95         Provider JavaDoc p;
96         String JavaDoc cls;
97
98         if (Security.getProvider("SunJSSE") == null) {
99             try {
100                 p = (Provider JavaDoc) Class.forName(
101                     "com.sun.net.ssl.internal.ssl.Provider").newInstance();
102
103                 Security.addProvider(p);
104             } catch (Exception JavaDoc e) {}
105         }
106     }
107
108 // ----------------------------- subclass overrides ----------------------------
109
public void configureSocket(Socket JavaDoc socket) {
110
111         SSLSocket s;
112
113         super.configureSocket(socket);
114
115         s = (SSLSocket) socket;
116
117         s.addHandshakeCompletedListener(this);
118     }
119
120     /**
121      * Creates a secure server socket bound to the specified port.
122      * The socket is configured with the socket options
123      * given to this factory.
124      *
125      * @return the secure ServerSocket
126      * @param port the port to which to bind the secure ServerSocket
127      * @throws Exception if a network or security provider error occurs
128      */

129     public ServerSocket JavaDoc createServerSocket(int port) throws Exception JavaDoc {
130
131         SSLServerSocket ss;
132
133         ss = (SSLServerSocket) getServerSocketFactoryImpl()
134             .createServerSocket(port);
135
136         if (Trace.TRACE) {
137             Trace.printSystemOut("[" + this + "]: createServerSocket()");
138             Trace.printSystemOut("capabilities for " + ss + ":");
139             Trace.printSystemOut("----------------------------");
140             dump("supported cipher suites", ss.getSupportedCipherSuites());
141             dump("enabled cipher suites", ss.getEnabledCipherSuites());
142         }
143
144         return ss;
145     }
146
147     /**
148      * Creates a secure server socket bound to the specified port.
149      * The socket is configured with the socket options
150      * given to this factory.
151      *
152      * @return the secure ServerSocket
153      * @param port the port to which to bind the secure ServerSocket
154      * @throws Exception if a network or security provider error occurs
155      */

156     public ServerSocket JavaDoc createServerSocket(int port,
157                                            String JavaDoc address) throws Exception JavaDoc {
158
159         SSLServerSocket ss;
160         InetAddress JavaDoc addr;
161
162         addr = InetAddress.getByName(address);
163         ss = (SSLServerSocket) getServerSocketFactoryImpl()
164             .createServerSocket(port, 128, addr);
165
166         if (Trace.TRACE) {
167             Trace.printSystemOut("[" + this + "]: createServerSocket()");
168             Trace.printSystemOut("capabilities for " + ss + ":");
169             Trace.printSystemOut("----------------------------");
170             dump("supported cipher suites", ss.getSupportedCipherSuites());
171             dump("enabled cipher suites", ss.getEnabledCipherSuites());
172         }
173
174         return ss;
175     }
176
177     private static void dump(String JavaDoc title, String JavaDoc[] as) {
178
179         Trace.printSystemOut(title);
180         Trace.printSystemOut("----------------------------");
181
182         for (int i = 0; i < as.length; i++) {
183             Trace.printSystemOut(String.valueOf(as[i]));
184         }
185
186         Trace.printSystemOut("----------------------------");
187     }
188
189     /**
190      * Creates a secure Socket and connects it to the specified remote host
191      * at the specified remote port. This socket is configured using the
192      * socket options established for this factory.
193      *
194      * @return the socket
195      * @param host the server host
196      * @param port the server port
197      * @throws Exception if a network or security provider error occurs
198      */

199     public Socket JavaDoc createSocket(String JavaDoc host, int port) throws Exception JavaDoc {
200
201         SSLSocket socket;
202
203         socket = (SSLSocket) getSocketFactoryImpl().createSocket(host, port);
204
205         socket.addHandshakeCompletedListener(this);
206         socket.startHandshake();
207
208 // unsaved@users
209
// For https protocol, the protocol handler should do this verification
210
// (Sun's implementation does), but if we do not use the Protocol
211
// handler (which is only available in Java >= 1.4), then we need to do
212
// the verification: hostname == cert CN
213
//
214
// boucherb@users 20030503:
215
// CHEKME/TODO:
216
//
217
// Stricter verify? Either require SunJSSE (assume its trust manager properly
218
// verifies whole chain), or implement our own TrustManager layer?
219
//
220
// What about v1/v3 and signing checks (re: man-in-the-middle attack),
221
// CRL check, basic constraints? notBefore? notAfter?
222
//
223
// Reference: http://www.securitytracker.com/alerts/2002/Aug/1005030.html
224
//
225
// That is, we can't guarantee that installed/prefered provider trust manager
226
// implementations verify the whole chain properly and there are still
227
// v1 certs out there (i.e. have no basic constraints, etc.), meaning that
228
// we should check for and reject any intermediate certs that are not v3+
229
// (cannot be checked for basic constraints). Only root and intermediate
230
// certs found in the trust store should be allowed to be v1 (since we must
231
// be trusing them for them to be there). All other intermediate signers,
232
// however, should be required to be v3+, otherwise anybody with any kind
233
// of cert issued somehow via a trust chain from the root can pose as an
234
// intermediate signing CA and hence leave things open to man-in-the-middle
235
// style attack. Also, we should really check CRLs, just in case
236
// it turns out that trust chain has been breached and thus issuer has revoked
237
// on some cert(s). Of course, this really begs the question, as it is not
238
// guaranteed that all CAs in trust store have valid, working CRL URL
239
//
240
// So what to do?
241
//
242
// Maybe best to leave this all up to DBA?
243
verify(host, socket.getSession());
244
245         return socket;
246     }
247
248     /**
249      * Retrieves whether this factory produces secure sockets.
250      *
251      * @return true iff this factory creates secure sockets
252      */

253     public boolean isSecure() {
254         return true;
255     }
256
257 // ----------------------- internal implementation -----------------------------
258

259     /**
260      * Retrieves the underlying javax.net.ssl.SSLServerSocketFactory.
261      *
262      * @throws Exception if there is a problem retrieving the
263      * underlying factory
264      * @return the underlying javax.net.ssl.SSLServerSocketFactory
265      */

266     protected SSLServerSocketFactory getServerSocketFactoryImpl()
267     throws Exception JavaDoc {
268
269         Object JavaDoc factory;
270
271         synchronized (server_socket_factory_mutex) {
272             factory = serverSocketFactory;
273
274             if (factory == null) {
275                 factory = SSLServerSocketFactory.getDefault();
276                 serverSocketFactory = factory;
277             }
278         }
279
280         return (SSLServerSocketFactory) factory;
281     }
282
283     /**
284      * Retrieves the underlying javax.net.ssl.SSLSocketFactory.
285      *
286      * @throws Exception if there is a problem retrieving the
287      * underlying factory
288      * @return the underlying javax.net.ssl.SSLSocketFactory
289      */

290     protected SSLSocketFactory getSocketFactoryImpl() throws Exception JavaDoc {
291
292         Object JavaDoc factory;
293
294         synchronized (socket_factory_mutex) {
295             factory = socketFactory;
296
297             if (factory == null) {
298                 factory = SSLSocketFactory.getDefault();
299                 socketFactory = factory;
300             }
301         }
302
303         return (SSLSocketFactory) factory;
304     }
305
306     /**
307      * Verifyies the certificate chain presented by the server to which
308      * a secure Socket has just connected. Specifically, the provided host
309      * name is checked against the Common Name of the server certificate;
310      * additional checks may or may not be performed.
311      *
312      * @param host the requested host name
313      * @param session SSLSession used on the connection to host
314      * @throws Exception if the certificate chain cannot be verified
315      */

316     protected void verify(String JavaDoc host, SSLSession session) throws Exception JavaDoc {
317
318         X509Certificate[] chain;
319         X509Certificate certificate;
320         Principal JavaDoc principal;
321         PublicKey JavaDoc publicKey;
322         String JavaDoc DN;
323         String JavaDoc CN;
324         int start;
325         int end;
326         String JavaDoc emsg;
327
328         chain = session.getPeerCertificateChain();
329         certificate = chain[0];
330         principal = certificate.getSubjectDN();
331         DN = String.valueOf(principal);
332         start = DN.indexOf("CN=");
333
334         if (start < 0) {
335             throw new UnknownHostException JavaDoc(
336                 Trace.getMessage(Trace.HsqlSocketFactorySecure_verify));
337         }
338
339         start += 3;
340         end = DN.indexOf(',', start);
341         CN = DN.substring(start, (end > -1) ? end
342                                                : DN.length());
343
344         if (CN.length() < 1) {
345             throw new UnknownHostException JavaDoc(
346                 Trace.getMessage(Trace.HsqlSocketFactorySecure_verify2));
347         }
348
349         if (!CN.equalsIgnoreCase(host)) {
350
351             // TLS_HOSTNAME_MISMATCH
352
throw new UnknownHostException JavaDoc(
353                 Trace.getMessage(
354                     Trace.HsqlSocketFactorySecure_verify3, true,
355                     new Object JavaDoc[] {
356                 CN, host
357             }));
358         }
359     }
360
361     public void handshakeCompleted(HandshakeCompletedEvent evt) {
362
363         SSLSession session;
364         String JavaDoc sessionId;
365         SSLSocket socket;
366
367         if (Trace.TRACE) {
368             socket = evt.getSocket();
369             session = evt.getSession();
370
371             Trace.printSystemOut("SSL handshake completed:");
372             Trace.printSystemOut(
373                 "------------------------------------------------");
374             Trace.printSystemOut("socket: : " + socket);
375             Trace.printSystemOut("cipher suite : "
376                                  + session.getCipherSuite());
377
378             sessionId = StringConverter.byteToHex(session.getId());
379
380             Trace.printSystemOut("session id : " + sessionId);
381             Trace.printSystemOut(
382                 "------------------------------------------------");
383         }
384     }
385 }
386
Popular Tags