KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > commons > security > JXSSLSocketFactory


1 package com.ca.commons.security;
2
3 /**
4  * JXSSLSocketFactory.java
5  */

6
7 import java.io.*;
8 import java.net.*;
9 import javax.net.SocketFactory;
10 import javax.net.ssl.SSLSocketFactory;
11
12 import java.security.*;
13 import java.awt.*;
14
15 import javax.net.ssl.*; // jdk 1.4 includes ssl in standard distro
16

17
18 /**
19  * <p>Socket factory for SSL jndi links that returns an SSL socket.
20  * It incorporates a keystore, which must contain the certs used
21  * to authenticate the client.</p>
22  *
23  * <p>This code is based on sample code made freely available by author
24  * Spencer W. Thomas on his web site http://hubris.engin.umich.edu/java/
25  * On Wed 24 May, 2000.</p>
26  *
27  * <p><b>Warning</b></p>
28  *
29  * <p>This class relies heavily on an internal, single, static SSLSocketFactory.
30  * multiple objects of this type in fact will use the same internal SSLSocketFactory.
31  * (This is why a single static init() method sets up everything for the entire
32  * class.) The reason for this structure is that JndiSocketFactory is dynamically
33  * invoked by the jndi connection, and we have no other chance to initialise the
34  * object.</p>
35  */

36
37 public class JXSSLSocketFactory extends SSLSocketFactory
38 {
39
40     /**
41      * This is the (static) factory internally shared between
42      * all JndiSocketFactory objects.
43      */

44
45     private static SSLSocketFactory factory = null;
46
47     /**
48      * A single default object of this class. It is
49      * initialised when first called for, and then reused
50      * whenever called again.
51      */

52
53     private static JXSSLSocketFactory default_factory = null;
54
55
56     private static KeyStore clientKeystore;
57
58     /**
59      * The sun 'JKS' keystore is the simplest and most commonly
60      * available keystore type.
61      */

62
63     private static final String JavaDoc DEFAULT_KEYSTORE_TYPE = "JKS";
64
65     /**
66      * the class loader to use for loading security providers
67      * 'n stuff. Defaults to the system loader.
68      */

69
70     private static ClassLoader JavaDoc myClassLoader = null;
71
72
73     /**
74      * Register a custom class loader to be used by
75      * the class when getting security providers.
76      */

77
78
79
80     public static void setClassLoader(ClassLoader JavaDoc newLoader)
81     {
82         myClassLoader = newLoader;
83     }
84
85     /**
86      * checks that myClassLoader is initialised, uses the System
87      * default loader if it isn't, and returns the guaranteed
88      * initialised loader.
89      */

90
91     private static ClassLoader JavaDoc getClassLoader()
92     {
93         if (myClassLoader == null)
94             myClassLoader = ClassLoader.getSystemClassLoader();
95
96         return myClassLoader;
97     }
98
99
100
101     /**
102      * <p>Enable debugging...</p>
103      * <P>WARNING - this doesn't seem to be working...??</p>
104      */

105
106     public static void setDebug(boolean status)
107     {
108         /*
109         all turn on all debugging
110         ssl turn on ssl debugging
111
112         The following can be used with ssl:
113
114             record enable per-record tracing
115             handshake print each handshake message
116             keygen print key generation data
117             session print session activity
118
119             handshake debugging can be widened with:
120             data hex dump of each handshake message
121             verbose verbose handshake message printing
122
123             record debugging can be widened with:
124             plaintext hex dump of record plaintext
125         */

126
127         if (status == true)
128             System.setProperty("javax.net.debug", "ssl,handshake,verbose");
129         else
130         {
131             System.setProperty("javax.net.debug", " ");
132         }
133     }
134
135     /**
136      * <p>Initialize the socket factory with a particular key store(s) and
137      * security provider. The minimum requirement is for a keystore
138      * containing trusted directory servers (the 'castore', or trusted
139      * certificate authority store, since the servers are usually signed
140      * by a common CA, whose cert would be held in this file).</p>
141      *
142      * <p>Further options include a private key store (the 'clientstore')
143      * that allows for client-authenticated ssl and SASL).</p>
144      *
145      * <p>Finally, it is possible to configure a non-standard keystore
146      * type and security provider. The keystore type defaults to Sun's
147      * JKS (at time of writting, the only keystore type that the default
148      * Sun security provider will handle).</p>
149      *
150      * <p>Nb. - it is possible to set a custom class loader (using
151      * 'registerClassLoader()' ) in which case this loader can be used
152      * to load the security provider.</p>
153      *
154      * @param caKeystoreFile A keystore file name of public certificates (trusted CA signs)
155      * @param clientKeystoreFile A keystore file name of the client's certificates, containing private keys.
156      * (may be null if only simple, 'server authenticated' ssl is being used).
157      * @param caPassphrase A password for the caKeystoreFile certificate.
158      * (may be null if only simple, 'server authenticated' ssl is being used, and keystore type is 'JKS').
159      * <b>Calling Program must manually clear passphrase after init() call.</b>
160      * @param clientPassphrase A password for the clientKeystoreFile certificate.
161      * (may be null if only simple, 'server authenticated' ssl is being used).
162      * <b>Calling Program must manually clear passphrase after init() call.</b>
163      * @param caKeystoreType The type of cakeystore file. (null => 'JKS')
164      * @param clientKeystoreType The type of clientkeystore file. (null => 'JKS')
165      * @param owner The owning GUI frame used for possible user interaction.
166      */

167
168
169
170     //
171
// Implementation note: this may be called repeatedly, with different info,
172
// as new connections are made. This is unsatisfactory, and dangerous if it
173
// is being used from multiple threads, but is unavoidable due to the difficulty
174
// of setting jndi to use a specific SSL socket class (we have to pass it a
175
// factory). Hence we need to be sure that different calls don't interfere,
176
// even at the cost of recreating objects unnecessarily...
177
//
178
public static void init(String JavaDoc caKeystoreFile, String JavaDoc clientKeystoreFile,
179                             char[] caPassphrase, char[] clientPassphrase,
180                             String JavaDoc caKeystoreType, String JavaDoc clientKeystoreType, Frame owner)
181         //throws Exception
182
throws GeneralSecurityException, IOException
183     {
184         boolean usingSASL = false; // whether we are using client-authenticated SSL
185

186         checkFileSanity(caKeystoreFile, clientKeystoreFile, clientPassphrase);
187
188
189         if ((clientPassphrase!=null) && (clientPassphrase.length>0) && (clientKeystoreFile != null))
190             usingSASL = true;
191
192         // use the client store if there is no caKeystoreFile.
193
if (caKeystoreFile == null && clientKeystoreFile != null)
194             caKeystoreFile = clientKeystoreFile;
195
196         // set cert authority keystore type to default if required.
197
if (caKeystoreType == null)
198             caKeystoreType = DEFAULT_KEYSTORE_TYPE;
199
200         // set the ssl protocol - usually "TLS" unless over-ridden
201
SSLContext sslctx = setSSLContextProtocol();
202
203         /*
204          * The KeyManagerFactory manages the clients certificates *and* private keys,
205          * which allow the client to authenticate itself to others. It is not required
206          * for 'simple' SSL, only for SASL.
207          */

208
209         KeyManagerFactory clientKeyManagerFactory = null;
210         TrustManagerFactory caTrustManagerFactory = null;
211         KeyStore caKeystore = null;
212         KeyManager[] clientKeyManagers = null;
213
214
215         /*
216          * 'traditional' server-authenticated ssl only requires the use of a trusted server
217          * keystore. Stronger client-authenticated ssl that requires both parties to authenticate
218          * (as used in LDAP SASL/External) requires a client keystore with a client private key.
219          */

220         if (usingSASL)
221         {
222             /*
223              * Create a keystore to hold the client certificates and private keys.
224              */

225
226             if (clientKeystoreType == null)
227                 clientKeystoreType = DEFAULT_KEYSTORE_TYPE;
228
229             clientKeystore = KeyStore.getInstance(clientKeystoreType); // key manager key store
230

231             /*
232              * Load the keystore from the client keystore file using the client
233              * keystore password.
234              */

235
236             if (clientKeystoreFile != null)
237                 clientKeystore.load(new FileInputStream(clientKeystoreFile), clientPassphrase);
238
239             /*
240              * Create a key manager using the default sun X509 key manager
241              */

242
243             clientKeyManagerFactory = KeyManagerFactory.getInstance("SunX509");
244
245             /*
246              * Initialise the client keystore manager with the just loaded keystore,
247              * and the keystore password.
248              */

249
250             clientKeyManagerFactory.init(clientKeystore, clientPassphrase);
251
252             /*
253              * Initialise the list of key managers
254              */

255
256             clientKeyManagers = clientKeyManagerFactory.getKeyManagers();
257         }
258         else
259         {
260             clientKeystore = null;
261         }
262
263         /*
264          * Initialise the trusted server certificate keystore.
265          */

266
267
268         caKeystore = KeyStore.getInstance(caKeystoreType);
269
270         /*
271          * Load the keys from the 'certificate authority' keystore (the trusted server keystore) file.
272          */

273
274         if (caKeystoreFile != null)
275         {
276             // caPassword may be null for some keystores (e.g. a 'JKS' keystore), and it is not an error.
277
caKeystore.load(new FileInputStream(caKeystoreFile), caPassphrase);
278         }
279
280         /**
281          * Create a trust manager using the default algorithm
282          * (can be set using 'ssl.TrustManagerFactory.algorithm=...' in java.security file - default is usually 'SunX509')
283          * - code suggestion from Vadim Tarassov
284          */

285         String JavaDoc defaultTrustAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
286
287         caTrustManagerFactory = TrustManagerFactory.getInstance(defaultTrustAlgorithm);
288
289         /*
290          * Create a trust manager factory using the default java X509 certificate based trust manager.
291          */

292 // caTrustManagerFactory = TrustManagerFactory.getInstance("SunX509");
293

294         /*
295          * Initialise the trust manager with the keystore containing the trusted server certs.
296          */

297
298         caTrustManagerFactory.init(caKeystore);
299
300         /*
301          * Get the list of trust managers from the trust manager factory, to initialise the
302          * ssl context with.
303          */

304
305          TrustManager[] caTrustManagers = caTrustManagerFactory.getTrustManagers();
306
307         caTrustManagers = JXTrustManager.convert(caTrustManagers, caKeystore, caKeystoreFile, caPassphrase, caKeystoreType, owner);
308
309         TrustManager[] trustedServerAndCAManagers = caTrustManagers;
310
311         sslctx.init(clientKeyManagers, trustedServerAndCAManagers, null);
312
313         factory = sslctx.getSocketFactory();
314
315         // we need to set/reset the default factory to take account of the new initialisation data received
316
// (this method may be called multiple times in the course of JXplorer's use.
317

318         synchronized(JXSSLSocketFactory.class)
319         {
320             default_factory = new JXSSLSocketFactory();
321         }
322     }
323
324     /**
325      * evil undocumented feature - can change SSL protocol on command line
326      * (needed for mainframe TOPSECRET folks who have want to use SSLv3).
327      * ... normally it just returns "TLS".
328      * @return
329      * @throws NoSuchAlgorithmException
330      */

331     private static SSLContext setSSLContextProtocol() throws NoSuchAlgorithmException
332     {
333         SSLContext sslctx;
334
335         String JavaDoc protocol = System.getProperty("sslversion", "TLS"); // TLS for java 1.4
336
if (!"TLS".equals(protocol))
337             System.out.println("SECURITY WARNING: Using non-standard ssl version: '" + protocol + "'");
338         sslctx = SSLContext.getInstance(protocol);
339         return sslctx;
340     }
341
342     /**
343      * Checks that the files containing the keystores really exist.
344      * Throws an exception (that can be bubbled through to the gui)
345      * if they don't. This is much clearer than relying on the
346      * Sun ssl stuff to meaningfully report back the error :-).
347      *
348      * Also insist that we have at least one viable keystore to work with.
349      */

350
351     private static void checkFileSanity(String JavaDoc caKeystoreFile, String JavaDoc clientKeystoreFile, char[] clientPassphrase)
352         throws SSLException
353     {
354         if (clientKeystoreFile == null && caKeystoreFile == null)
355             throw new SSLException("SSL Initialisation error: No valid keystore files available.");
356
357         if (caKeystoreFile != null)
358             if (new File(caKeystoreFile).exists() == false)
359                 throw new SSLException("SSL Initialisation error: file '" + caKeystoreFile + "' does not exist.");
360
361         if (clientKeystoreFile != null && clientPassphrase != null)
362             if (new File(clientKeystoreFile).exists() == false)
363                 throw new SSLException("SSL Initialisation error: file '" + clientKeystoreFile + "' does not exist.");
364     }
365
366
367   // DEBUG PRINT CODE - don't remove, can be quite usefull...
368
/*
369         KeyManager[] myKM = new KeyManager[keyManagers.length];
370         for (int i=0; i<keyManagers.length; i++)
371         {
372             myKM[i] = new MyX509KeyManager((X509KeyManager)keyManagers[i]);
373         }
374
375         TrustManager[] myTM = new TrustManager[trustManagers.length];
376         for (int i=0; i<trustManagers.length; i++)
377         {
378             myTM[i] = new MyX509TrustManager((X509TrustManager)trustManagers[i]);
379         }
380
381         System.out.println("Number of Keymanagers = " + myKM.length);
382         if (myKM.length >=1)
383         {
384             KeyManager bloop = myKM[0];
385             if (bloop == null) System.out.println("Bloop is Null???!");
386             System.out.println("bloop is a " + bloop.getClass());
387             if (bloop instanceof X509KeyManager)
388             {
389                 System.out.println("bloop is X509KeyManager!");
390                 String[] clients = ((X509KeyManager)bloop).getClientAliases("SunX509", null);
391                 System.out.println("Num clients = " + clients.length);
392                 for (int i=0; i<clients.length; i++)
393                     System.out.println("client: " + i + " = " + clients[i]);
394             }
395         }
396
397
398         System.out.println("Number of Trustmanagers = " + myTM.length);
399         if (myTM.length >=1)
400         {
401             TrustManager bloop = myTM[0];
402             if (bloop == null) System.out.println("Bloop is Null???!");
403             System.out.println("bloop is a " + bloop.getClass());
404             if (bloop instanceof X509TrustManager)
405             {
406                 System.out.println("bloop is X509TrustManager!");
407                ((X509TrustManager)bloop).getAcceptedIssuers();
408             }
409         }
410 */

411
412     /**
413      * Constructor
414      */

415     public JXSSLSocketFactory()
416     {
417     }
418
419     /**
420      * <p>Return an instance of this class.</p>
421      *
422      * <p>Each call to 'init()' should reset the default factory.</p>
423      *
424      *
425      * @return An instance of JndiSocketFactory.
426      */

427
428     public static SocketFactory getDefault()
429     {
430         synchronized(JXSSLSocketFactory.class)
431         {
432             if (default_factory == null)
433                 default_factory = new JXSSLSocketFactory();
434         }
435
436         return default_factory;
437     }
438
439
440     public static KeyStore getClientKeyStore() {
441         return clientKeystore;
442     }
443
444     /**
445      * Return an SSLSocket (upcast to Socket) given host and port.
446      *
447      * @param host Name of the host to which the socket will be opened.
448      * @param port Port to connect to.
449      * @return An SSLSocket instance (as a Socket).
450      * @throws IOException If the connection can't be established.
451      * @throws UnknownHostException If the host is not known.
452      */

453     public Socket createSocket(String JavaDoc host, int port)
454         throws IOException, UnknownHostException
455     {
456         return factory.createSocket(host, port);
457     }
458
459     /**
460      * Return an SSLSocket (upcast to Socket) given host and port.
461      *
462      * @param host Address of the server host.
463      * @param port Port to connect to.
464      * @return An SSLSocket instance (as a Socket).
465      * @throws IOException If the connection can't be established.
466      * @throws UnknownHostException If the host is not known.
467      */

468     public Socket createSocket(InetAddress host, int port)
469        throws IOException, UnknownHostException
470     {
471         return factory.createSocket(host, port);
472     }
473
474
475     /**
476      * Return an SSLSocket (upcast to Socket) given host and port.
477      * The client is bound to the specified network address and port.
478      *
479      * @param host Address of the server host.
480      * @param port Port to connect to.
481      * @param client_host Address of this (client) host.
482      * @param port Port to connect from.
483      * @return An SSLSocket instance (as a Socket).
484      * @throws IOException If the connection can't be established.
485      * @throws UnknownHostException If the host is not known.
486      */

487     public Socket createSocket(InetAddress host, int port,
488                  InetAddress client_host, int client_port)
489        throws IOException, UnknownHostException
490     {
491         return factory.createSocket(host, port, client_host, client_port);
492     }
493
494
495     /**
496      * Return an SSLSocket (upcast to Socket) given host and port.
497      * The client is bound to the specified network address and port.
498      *
499      * @param host Address of the server host.
500      * @param port Port to connect to.
501      * @param client_host Address of this (client) host.
502      * @param port Port to connect from.
503      * @return An SSLSocket instance (as a Socket).
504      * @throws IOException If the connection can't be established.
505      * @throws UnknownHostException If the host is not known.
506      */

507     public Socket createSocket(String JavaDoc host, int port,
508                  InetAddress client_host, int client_port)
509        throws IOException, UnknownHostException
510     {
511         return factory.createSocket(host, port, client_host, client_port);
512     }
513
514     /**
515      * Return an SSLSocket layered on top of the given Socket.
516      */

517     public Socket createSocket(Socket socket, String JavaDoc host, int port, boolean autoclose)
518        throws IOException, UnknownHostException
519     {
520         return factory.createSocket(socket, host, port, autoclose);
521     }
522
523     /**
524      * Return default cipher suites.
525      */

526     public String JavaDoc[] getDefaultCipherSuites()
527     {
528         return factory.getDefaultCipherSuites();
529     }
530
531     /**
532      * Return supported cipher suites.
533      */

534     public String JavaDoc[] getSupportedCipherSuites()
535     {
536         return factory.getSupportedCipherSuites();
537     }
538 }
Popular Tags