KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > commons > jndi > JndiSocketFactory


1 package com.ca.commons.jndi;
2
3 /**
4  * JndiSocketFactory.java
5  *
6  */

7
8 import java.io.*;
9 import java.net.*;
10 import javax.net.SocketFactory;
11 import javax.net.ssl.SSLSocketFactory;
12 import java.lang.reflect.Constructor JavaDoc;
13 import java.lang.reflect.Method JavaDoc;
14
15 import java.security.KeyStore JavaDoc;
16 import java.security.GeneralSecurityException JavaDoc;
17
18 // XXX 1.3 <-> 1.4
19
//import com.sun.net.ssl.*; // jdk 1.3 requires alljssl.jar in external
20
import javax.net.ssl.*;
21 import javax.naming.NamingException JavaDoc; // jdk 1.4 includes ssl in standard distro
22

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

42
43 public class JndiSocketFactory extends SSLSocketFactory
44 {
45
46     /**
47      * This is the (static) factory internally shared between
48      * all JndiSocketFactory objects.
49      */

50
51     private static SSLSocketFactory factory = null;
52
53     /**
54      * A single default object of this class. It is
55      * initialised when first called for, and then reused
56      * whenever called again.
57      */

58
59     private static JndiSocketFactory default_factory = null;
60
61
62     private static KeyStore JavaDoc clientKeystore;
63
64     /**
65      * The sun 'JKS' keystore is the simplest and most commonly
66      * available keystore type.
67      */

68
69     private static final String JavaDoc DEFAULT_KEYSTORE_TYPE = "JKS";
70
71     /**
72      * The 'KSE' keystore is used by the Glen Iris group to
73      * wrap openssl calls. It has some peculiarities requiring
74      * special handling.
75      */

76
77     private static final String JavaDoc PKI_INTERNAL_TYPE = "KSE";
78
79
80     /**
81      * the class loader to use for loading security providers
82      * 'n stuff. Defaults to the system loader.
83      */

84
85     private static ClassLoader JavaDoc myClassLoader = null;
86
87     /**
88      * Register a custom class loader to be used by
89      * the class when getting security providers.
90      */

91
92     public static void setClassLoader(ClassLoader JavaDoc newLoader)
93     {
94         myClassLoader = newLoader;
95     }
96
97     /**
98      * checks that myClassLoader is initialised, uses the System
99      * default loader if it isn't, and returns the guaranteed
100      * initialised loader.
101      */

102
103     private static ClassLoader JavaDoc getClassLoader()
104     {
105         if (myClassLoader == null)
106             myClassLoader = ClassLoader.getSystemClassLoader();
107
108         return myClassLoader;
109     }
110
111
112
113     /**
114      * Enable debugging...
115      */

116
117     public static void setDebugOn()
118     {
119         /*
120         all turn on all debugging
121         ssl turn on ssl debugging
122
123         The following can be used with ssl:
124
125             record enable per-record tracing
126             handshake print each handshake message
127             keygen print key generation data
128             session print session activity
129
130             handshake debugging can be widened with:
131             data hex dump of each handshake message
132             verbose verbose handshake message printing
133
134             record debugging can be widened with:
135             plaintext hex dump of record plaintext
136         */

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

172
173     public static void init(String JavaDoc caKeystoreFile, String JavaDoc clientKeystoreFile,
174                             char[] caPassphrase, char[] clientPassphrase,
175                             String JavaDoc caKeystoreType, String JavaDoc clientKeystoreType)
176         throws NamingException JavaDoc
177     {
178         if (default_factory != null)
179             return;
180
181         try
182         {
183             checkFileSanity(caKeystoreFile, clientKeystoreFile, clientPassphrase);
184
185             // use the client store if there is no caKeystoreFile.
186
if (caKeystoreFile == null)
187                 caKeystoreFile = clientKeystoreFile;
188
189             SSLContext sslctx;
190
191     /* XXX HERE BE DRAGONS
192      *
193      * ... attempting to make file compile and run under both 1.3 (using com.sun.net.ssl.*) and
194      * javax.net.ssl.* (where they moved SSLContext in v1.4). By leaving it unspecified, we
195      * hope that the compiler will pick up the right one at compile time. Probably means grief
196      * if you run a 1.3 compiled version on 1.4 or vica-versa...
197      */

198     // XXX 1.3 <-> 1.4
199
// XXX This is a STOOPID Idea, since we can't set the imports at run time, so it
200
// shouldn't work, because it will get con-foos-ed about which version of "SSLContext" to load.
201
//
202
// TODO: could we tweak it by using fully qualified class names? Doesn't matter now I suppose,
203
// so much other 1.4 stuff is in the code now...
204
/*
205             if ("1.4".compareTo(System.getProperty("java.version")) >= 0)
206             {
207                 sslctx = SSLContext.getInstance("TLS"); // TLS for java 1.4
208             }
209             else
210             {
211                 sslctx = SSLContext.getInstance("SSLv3"); // "SSLv3" for java 1.3 ...
212             }
213     */

214             // ONLY SUPPORTS Java 1.4+
215

216             // evil undocumented feature - can change SSL protocol on command line
217
// (needed for mainframe TOPSECRET folks who have crippled TLS implementatiuon and want to use SSLv3)
218
String JavaDoc protocol = System.getProperty("sslversion", "TLS"); // TLS for java 1.4
219
if (!"TLS".equals(protocol))
220                 System.out.println("SECURITY: Using non-standard ssl version: '" + protocol + "'");
221     //TODO: proper logging
222
sslctx = SSLContext.getInstance(protocol);
223
224             /*
225              * The KeyManagerFactory manages the clients certificates *and* private keys,
226              * which allow the client to authenticate itself to others. It is not required
227              * for 'simple' SSL.
228              */

229
230             KeyManagerFactory clientKeyManagerFactory = null;
231             TrustManagerFactory caTrustManagerFactory;
232             KeyStore JavaDoc caKeystore;
233
234
235             if ((clientPassphrase!=null) && (clientPassphrase.length>0))
236             {
237                 /*
238                  * Special hack for Glen Iris PKI group, until the pki
239                  * keystore is fully functional.
240                  *
241                  * We make heavy use of reflection to allow code to
242                  * compile and run when the com.ca.pki... libraries
243                  * aren't available.
244                  */

245
246
247                 if (PKI_INTERNAL_TYPE.equals(clientKeystoreType))
248                 {
249                     try
250                     {
251                         Class JavaDoc c = getClassLoader().loadClass("com.ca.commons.security.openssl.ParsePkcs12");
252                         if (c==null)
253                         {
254                             System.out.println("PKI internal error");
255                             return;
256                         }
257
258                         Constructor JavaDoc constructor = c.getConstructor(new Class JavaDoc[] {String JavaDoc.class, byte[].class});
259
260                         // get password safely - rely on calling routine to clear clientPassphrase
261
int size = clientPassphrase.length;
262                         byte[] password = new byte[size];
263                         for (int i=0; i<size; i++)
264                              password[i] = (byte) clientPassphrase[i];
265
266                         Object JavaDoc pkcs12Parser = constructor.newInstance(new Object JavaDoc[] {clientKeystoreFile, password});
267
268                         Method JavaDoc getSunKeyStore = c.getMethod("getSunKeyStore", new Class JavaDoc[] {String JavaDoc.class} );
269
270                         clientKeystore = (KeyStore JavaDoc) getSunKeyStore.invoke(pkcs12Parser, new Object JavaDoc[] {"MyFriend"});
271
272                         for (int i=0; i<size; i++) // clear temp password store.
273
password[i] = 0;
274                     }
275                     catch (Exception JavaDoc e)
276                     {
277                         System.err.println("unable to load pkcs12 parser (not in class path?)");
278                         e.printStackTrace();
279                         return;
280                     }
281                 }
282                 else
283                 {
284                     /*
285                      * Create a keystore to hold the client certificates and private keys.
286                      */

287
288                     if (clientKeystoreType == null)
289                         clientKeystoreType = DEFAULT_KEYSTORE_TYPE;
290
291                     clientKeystore = KeyStore.getInstance(clientKeystoreType); // key manager key store
292

293                     /*
294                      * Load the keystore from the client keystore file using the client
295                      * keystore password.
296                      */

297
298                     if (clientKeystoreFile != null)
299                         clientKeystore.load(new FileInputStream(clientKeystoreFile), clientPassphrase);
300                 }
301                 /*
302                  * Create a key manager using the default sun X509 key manager
303                  */

304
305                 clientKeyManagerFactory = KeyManagerFactory.getInstance("SunX509");
306
307                 /*
308                  * Initialise the client keystore manager with the just loaded keystore,
309                  * and the keystore password.
310                  */

311
312                 clientKeyManagerFactory.init(clientKeystore, clientPassphrase);
313
314             }
315
316             /*
317              * Initialise the list of key managers (may be null if the client keystore is not
318              * being used).
319              */

320
321             KeyManager[] keyManagers = null;
322             if (clientKeyManagerFactory != null)
323                 keyManagers = clientKeyManagerFactory.getKeyManagers();
324
325             /*
326              * Initialise the trusted server certificate keystore.
327              */

328
329             if (caKeystoreType == null)
330                 caKeystoreType = DEFAULT_KEYSTORE_TYPE;
331
332             caKeystore = KeyStore.getInstance(caKeystoreType);
333
334             /*
335              * Load the keys from the 'certificate authority' keystore (the trusted server keystore) file.
336              */

337
338             if (caKeystoreFile != null)
339             {
340                 // caPassword may be null for some keystores (e.g. a 'JKS' keystore), and it is not an error.
341
//if (caPassphrase == null && DEFAULT_KEYSTORE_TYPE.equals(caKeystoreType) == false)
342
// throw new Exception("Internal SSL Initialisation error: No password for non standard trusted server (CA) keystore.");
343

344                 caKeystore.load(new FileInputStream(caKeystoreFile), caPassphrase);
345             }
346
347             /*
348              * Create a trust manager factory using the default java X509 certificate based trust manager.
349              */

350
351             caTrustManagerFactory = TrustManagerFactory.getInstance("SunX509");
352
353             /*
354              * Initialise the trust manager with the keystore containing the trusted server certs.
355              */

356
357             caTrustManagerFactory.init(caKeystore);
358
359             /*
360              * Get the list of trust managers from the trust manager factory, to initialise the
361              * ssl context with.
362              */

363
364             TrustManager[] trustManagers = caTrustManagerFactory.getTrustManagers();
365
366
367
368             sslctx.init(keyManagers, trustManagers, null);
369
370             synchronized(JndiSocketFactory.class)
371             {
372                 factory = sslctx.getSocketFactory();
373
374                 default_factory = new JndiSocketFactory();
375             }
376         }
377         catch (GeneralSecurityException JavaDoc e)
378         {
379             NamingException JavaDoc ne = new NamingException JavaDoc("security error: unable to initialise JndiSocketFactory");
380             ne.initCause(e);
381             throw ne;
382         }
383         catch (IOException e)
384         {
385             NamingException JavaDoc ne = new NamingException JavaDoc("file access error: unable to initialise JndiSocketFactory");
386             ne.initCause(e);
387             throw ne;
388         }
389     }
390
391     /**
392      * Checks that the files containing the keystores really exist.
393      * Throws an exception (that can be bubbled through to the gui)
394      * if they don't. This is much clearer than relying on the
395      * Sun ssl stuff to meaningfully report back the error :-).
396      */

397
398     private static void checkFileSanity(String JavaDoc caKeystoreFile, String JavaDoc clientKeystoreFile, char[] clientPassphrase)
399         throws NamingException JavaDoc
400     {
401         if (clientKeystoreFile == null && caKeystoreFile == null)
402             throw new NamingException JavaDoc("SSL Initialisation error: No valid keystore files available.");
403
404         if (caKeystoreFile != null)
405             if (new File(caKeystoreFile).exists() == false)
406                 throw new NamingException JavaDoc("SSL Initialisation error: file '" + caKeystoreFile + "' does not exist.");
407
408         if (clientKeystoreFile != null && clientPassphrase != null)
409             if (new File(clientKeystoreFile).exists() == false)
410                 throw new NamingException JavaDoc("SSL Initialisation error: file '" + clientKeystoreFile + "' does not exist.");
411     }
412
413
414
415
416
417
418   // DEBUG PRINT CODE
419
/*
420         KeyManager[] myKM = new KeyManager[keyManagers.length];
421         for (int i=0; i<keyManagers.length; i++)
422         {
423             myKM[i] = new MyX509KeyManager((X509KeyManager)keyManagers[i]);
424         }
425
426         TrustManager[] myTM = new TrustManager[trustManagers.length];
427         for (int i=0; i<trustManagers.length; i++)
428         {
429             myTM[i] = new MyX509TrustManager((X509TrustManager)trustManagers[i]);
430         }
431
432         System.out.println("Number of Keymanagers = " + myKM.length);
433         if (myKM.length >=1)
434         {
435             KeyManager bloop = myKM[0];
436             if (bloop == null) System.out.println("Bloop is Null???!");
437             System.out.println("bloop is a " + bloop.getClass());
438             if (bloop instanceof X509KeyManager)
439             {
440                 System.out.println("bloop is X509KeyManager!");
441                 String[] clients = ((X509KeyManager)bloop).getClientAliases("SunX509", null);
442                 System.out.println("Num clients = " + clients.length);
443                 for (int i=0; i<clients.length; i++)
444                     System.out.println("client: " + i + " = " + clients[i]);
445             }
446         }
447
448
449         System.out.println("Number of Trustmanagers = " + myTM.length);
450         if (myTM.length >=1)
451         {
452             TrustManager bloop = myTM[0];
453             if (bloop == null) System.out.println("Bloop is Null???!");
454             System.out.println("bloop is a " + bloop.getClass());
455             if (bloop instanceof X509TrustManager)
456             {
457                 System.out.println("bloop is X509TrustManager!");
458                ((X509TrustManager)bloop).getAcceptedIssuers();
459             }
460         }
461 */

462
463     /**
464      * Constructor
465      */

466     public JndiSocketFactory()
467     {
468     }
469
470     /**
471      * Return an instance of this class.
472      *
473      * @return An instance of JndiSocketFactory.
474      */

475
476     public static SocketFactory getDefault()
477     {
478         synchronized(JndiSocketFactory.class)
479         {
480             if (default_factory == null)
481                 default_factory = new JndiSocketFactory();
482         }
483
484         return (SocketFactory)default_factory;
485     }
486
487
488     public static KeyStore JavaDoc getClientKeyStore() {
489         return clientKeystore;
490     }
491
492     /**
493      * Return an SSLSocket (upcast to Socket) given host and port.
494      *
495      * @param host Name of the host to which the socket will be opened.
496      * @param port Port to connect to.
497      * @return An SSLSocket instance (as a Socket).
498      * @throws IOException If the connection can't be established.
499      * @throws UnknownHostException If the host is not known.
500      */

501     public Socket createSocket(String JavaDoc host, int port)
502         throws IOException, UnknownHostException
503     {
504         return factory.createSocket(host, port);
505     }
506
507     /**
508      * Return an SSLSocket (upcast to Socket) given host and port.
509      *
510      * @param host Address of the server host.
511      * @param port Port to connect to.
512      * @return An SSLSocket instance (as a Socket).
513      * @throws IOException If the connection can't be established.
514      * @throws UnknownHostException If the host is not known.
515      */

516     public Socket createSocket(InetAddress host, int port)
517        throws IOException, UnknownHostException
518     {
519         return factory.createSocket(host, port);
520     }
521
522
523     /**
524      * Return an SSLSocket (upcast to Socket) given host and port.
525      * The client is bound to the specified network address and port.
526      *
527      * @param host Address of the server host.
528      * @param port Port to connect to.
529      * @param client_host Address of this (client) host.
530      * @param port Port to connect from.
531      * @return An SSLSocket instance (as a Socket).
532      * @throws IOException If the connection can't be established.
533      * @throws UnknownHostException If the host is not known.
534      */

535     public Socket createSocket(InetAddress host, int port,
536                  InetAddress client_host, int client_port)
537        throws IOException, UnknownHostException
538     {
539         return factory.createSocket(host, port, client_host, client_port);
540     }
541
542
543     /**
544      * Return an SSLSocket (upcast to Socket) given host and port.
545      * The client is bound to the specified network address and port.
546      *
547      * @param host Address of the server host.
548      * @param port Port to connect to.
549      * @param client_host Address of this (client) host.
550      * @param port Port to connect from.
551      * @return An SSLSocket instance (as a Socket).
552      * @throws IOException If the connection can't be established.
553      * @throws UnknownHostException If the host is not known.
554      */

555     public Socket createSocket(String JavaDoc host, int port,
556                  InetAddress client_host, int client_port)
557        throws IOException, UnknownHostException
558     {
559         return factory.createSocket(host, port, client_host, client_port);
560     }
561
562     /**
563      * Return an SSLSocket layered on top of the given Socket.
564      */

565     public Socket createSocket(Socket socket, String JavaDoc host, int port, boolean autoclose)
566        throws IOException, UnknownHostException
567     {
568         return factory.createSocket(socket, host, port, autoclose);
569     }
570
571     /**
572      * Return default cipher suites.
573      */

574     public String JavaDoc[] getDefaultCipherSuites()
575     {
576         return factory.getDefaultCipherSuites();
577     }
578
579     /**
580      * Return supported cipher suites.
581      */

582     public String JavaDoc[] getSupportedCipherSuites()
583     {
584         return factory.getSupportedCipherSuites();
585     }
586 }
Popular Tags