KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > slamd > example > JSSERandomClientCertSocketFactory


1 /*
2  * Sun Public License
3  *
4  * The contents of this file are subject to the Sun Public License Version
5  * 1.0 (the "License"). You may not use this file except in compliance with
6  * the License. A copy of the License is available at http://www.sun.com/
7  *
8  * The Original Code is the SLAMD Distributed Load Generation Engine.
9  * The Initial Developer of the Original Code is Neil A. Wilson.
10  * Portions created by Neil A. Wilson are Copyright (C) 2004.
11  * Some preexisting portions Copyright (C) 2002-2004 Sun Microsystems, Inc.
12  * All Rights Reserved.
13  *
14  * Contributor(s): Neil A. Wilson
15  */

16 package com.sun.slamd.example;
17
18
19
20 import java.io.*;
21 import java.net.*;
22 import java.security.*;
23 import java.security.cert.*;
24 import java.util.*;
25 import javax.net.ssl.*;
26 import netscape.ldap.*;
27
28
29
30 /**
31  * This class provides an implementation of an SSL socket factory that will use
32  * JSSE to create an SSL client socket. The first time the server requests a
33  * client certificate, one will be chosen at random from the appropriate set of
34  * keys in the JSSE JKS-format key store. Subsequent requests will continue to
35  * use the same client certificate until the <CODE>chooseNewClientCert</CODE>
36  * method is called. In addition, this class implements a trust manager so that
37  * any SSL certificate presented by the server will be trusted.
38  *
39  *
40  * @author Neil A. Wilson
41  */

42 public class JSSERandomClientCertSocketFactory
43        extends SSLSocketFactory
44        implements LDAPSocketFactory, X509KeyManager, X509TrustManager
45 {
46   // Indicates whether the client certificate to use should always be chosen at
47
// random, or if it should only be chosen at random the first time a
48
// certificate is needed and any time the chooseNewClientCert method is
49
// called.
50
boolean alwaysRandom;
51
52   // Indicates whether debug mode will be enabled (will print a message to
53
// standard error whenever any method is called).
54
boolean debugMode;
55
56   // Indicates whether to disable session caching for connections created with
57
// this socket factory.
58
boolean disableSessionCaching;
59
60   // The random number generator that will be used to select which client
61
// ceritficate to present.
62
Random random;
63
64   // The SSL context that will be used to manage all things SSL.
65
SSLContext sslContext;
66
67   // The SSL socket factory that will actually be used to create the sockets.
68
SSLSocketFactory sslSocketFactory;
69
70   // The alias of the certificate that should be presented to the server
71
// whenever one is requested.
72
String JavaDoc currentAlias;
73
74   // The set of aliases of the client certificates that are available for use.
75
String JavaDoc[] aliases;
76
77   // The parent key manager that we will use for operations other than choosing
78
// the alias of the client certificate to present.
79
X509KeyManager parentKeyManager;
80
81
82
83   /**
84    * Creates a new instance of this SSL socket factory.
85    *
86    * @param keyStoreFile The path to the JKS-format JSSE keystore
87    * containing the client certificates to use in the
88    * authentication process.
89    * @param keyStorePassword The password needed to access the information in
90    * the keystore, formatted as a character array.
91    *
92    * @throws LDAPException If a problem occurs while initializing this socket
93    * factory.
94    */

95   public JSSERandomClientCertSocketFactory(String JavaDoc keyStoreFile,
96                                            char[] keyStorePassword)
97          throws LDAPException
98   {
99     this(keyStoreFile, keyStorePassword, false);
100   }
101
102
103
104   /**
105    * Creates a new instance of this SSL socket factory.
106    *
107    * @param keyStoreFile The path to the JKS-format JSSE keystore
108    * containing the client certificates to use in the
109    * authentication process.
110    * @param keyStorePassword The password needed to access the information in
111    * the keystore, formatted as a character array.
112    * @param debugMode Indicates whether this socket factory will
113    * operate in debug mode.
114    *
115    * @throws LDAPException If a problem occurs while initializing this socket
116    * factory.
117    */

118   public JSSERandomClientCertSocketFactory(String JavaDoc keyStoreFile,
119                                            char[] keyStorePassword,
120                                            boolean debugMode)
121          throws LDAPException
122   {
123     this.debugMode = debugMode;
124     alwaysRandom = false;
125     disableSessionCaching = false;
126
127
128     // Read in the contents of the JSSE keystore.
129
KeyStore keyStore;
130     try
131     {
132       FileInputStream inputStream = new FileInputStream(keyStoreFile);
133       keyStore = KeyStore.getInstance("JKS");
134       keyStore.load(inputStream, keyStorePassword);
135       inputStream.close();
136     }
137     catch (Exception JavaDoc e)
138     {
139       String JavaDoc message = "Unable to read key store file \"" + keyStoreFile +
140                        "\" -- " + e;
141
142       if (debugMode)
143       {
144         System.err.println(message);
145       }
146
147       throw new LDAPException(message);
148     }
149
150
151     try
152     {
153       // Iterate through the key store to find all the aliases of the client
154
// certificates.
155
ArrayList aliasList = new ArrayList();
156       Enumeration keyStoreAliases = keyStore.aliases();
157
158       while (keyStoreAliases.hasMoreElements())
159       {
160         String JavaDoc alias = (String JavaDoc) keyStoreAliases.nextElement();
161         if (keyStore.isKeyEntry(alias))
162         {
163           aliasList.add(keyStoreAliases.nextElement());
164         }
165       }
166
167       aliases = new String JavaDoc[aliasList.size()];
168       aliasList.toArray(aliases);
169     }
170     catch (KeyStoreException kse)
171     {
172       String JavaDoc message = "Unable to retrieve aliases of client certificates " +
173                        "from the key store -- " + kse;
174
175       if (debugMode)
176       {
177         System.err.println(message);
178       }
179
180       throw new LDAPException(message);
181     }
182
183
184     // Make sure that there is at least one client certificate available.
185
if ((aliases == null) || (aliases.length == 0))
186     {
187       String JavaDoc message = "No client certificates found in key store \"" +
188                        keyStoreFile + "\"";
189
190       if (debugMode)
191       {
192         System.err.println(message);
193       }
194
195       throw new LDAPException(message);
196     }
197
198
199     // Get the default X.509 key manager.
200
try
201     {
202       KeyManagerFactory keyManagerFactory =
203            KeyManagerFactory.getInstance("SunX509");
204       keyManagerFactory.init(keyStore, keyStorePassword);
205       KeyManager[] managers = keyManagerFactory.getKeyManagers();
206       if ((managers == null) || (managers.length == 0))
207       {
208         throw new NoSuchAlgorithmException("No X.509 key managers are " +
209                                            "available.");
210       }
211
212       parentKeyManager = (X509KeyManager) managers[0];
213     }
214     catch (Exception JavaDoc e)
215     {
216       String JavaDoc message = "Unable to obtain a handle to the default X.509 key " +
217                        "manager -- " + e;
218
219       if (debugMode)
220       {
221         System.err.println(message);
222       }
223
224       throw new LDAPException(message);
225     }
226
227
228
229     // Indicate that we will be using JSSE for the SSL-based connections.
230
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
231     System.setProperty("java.protocol.handler.pkgs",
232                        "com.sun.net.ssl.internal.www.protocol");
233
234
235     // Get the default SSL context.
236
try
237     {
238       sslContext = SSLContext.getInstance("SSLv3");
239     }
240     catch (NoSuchAlgorithmException nsae)
241     {
242       String JavaDoc message = "Unable to initialize the SSL context -- " + nsae;
243
244       if (debugMode)
245       {
246         System.err.println(message);
247       }
248
249       throw new LDAPException(message);
250     }
251
252
253     // Initialize the SSL context with our own trust manager (this class) to
254
// use when determining whether to trust a client certificate.
255
try
256     {
257       sslContext.init(new KeyManager[] { this }, new TrustManager[] { this },
258                       null);
259     }
260     catch (KeyManagementException kme)
261     {
262       String JavaDoc message = "Unable to regsiter a key and trust managers with the " +
263                        "SSL context: " + kme;
264
265       if (debugMode)
266       {
267         System.err.println(message);
268       }
269
270       throw new LDAPException(message);
271     }
272
273
274     // Get the socket factory to use when creating the certificates.
275
sslSocketFactory = sslContext.getSocketFactory();
276
277
278     // Initialize the random number generator that we will use for selecting
279
// client certificates.
280
random = new Random();
281
282
283     // If we are in debug mode, indicate that the socket factory has been
284
// created.
285
if (debugMode)
286     {
287       System.err.println("New JSSERandomClientCertSocketFactory created");
288     }
289   }
290
291
292
293   /**
294    * Retrieves the alias of the client certificate that will be used the next
295    * time the client must present a certificate to an SSL server.
296    *
297    * @return The alias of the client certificate that will be used the next
298    * time the client must present a certificate to an SSL server, or
299    * <CODE>null</CODE> if the next certificate will be chosen at
300    * random.
301    */

302   public String JavaDoc getCurrentAlias()
303   {
304     return currentAlias;
305   }
306
307
308
309   /**
310    * Specifies the alias of the client certificate that should be used the next
311    * time the client must present a certificate to an SSL server. This
312    * selection will remain in effect until the <CODE>chooseNewClientCert</CODE>
313    * method is called (in which case the next certificate will be chosen at
314    * random) or the <CODE>setCurrentAlias</CODE> method is called again to
315    * choose a different alias. Note that no error checking is performed, so if
316    * the specified alias does not exist in the keystore, then attempts to use
317    * that certificate will fail. Also note that this will override the setting
318    * of the <CODE>alwaysRandom</CODE> flag, so this certificate will always be
319    * used until the <CODE>chooseNewClientCert</CODE> method is called, at which
320    * point the <CODE>alwaysRandom</CODE> flag will again be honored.
321    *
322    * @param alias The alias of the client certificate that should be used the
323    * next time the client must present a certificate to an SSL
324    * server. A value of <CODE>null</CODE> indicates that the
325    * next selection should be random.
326    */

327   public void setCurrentAlias(String JavaDoc alias)
328   {
329     this.currentAlias = alias;
330   }
331
332
333
334   /**
335    * Retrieves the aliases of the client certificates that are available for use
336    * in the key store.
337    *
338    * @return The aliases of the client certificates that are available for use
339    * in the key store.
340    */

341   public String JavaDoc[] getAliases()
342   {
343     return aliases;
344   }
345
346
347
348   /**
349    * Indicates that this socket factory should choose a new client certificate
350    * at random the next time it must present a certificate to an SSL server.
351    */

352   public void chooseNewClientCert()
353   {
354     currentAlias = null;
355   }
356
357
358
359   /**
360    * Indicates whether the client certificate selection will be always taken at
361    * random, or if the selection should only be random the first time a
362    * certificate is needed or after the <CODE>chooseNewClientCert</CODE> method
363    * is called.
364    *
365    * @return <CODE>true</CODE> if the client certificate selection will always
366    * be random, or <CODE>false</CODE> if not.
367    */

368   public boolean alwaysRandom()
369   {
370     return alwaysRandom;
371   }
372
373
374
375   /**
376    * Specifies whether the client certificate selection should always be random,
377    * or if the selection should only be random the first time a certificate is
378    * nneeded or after the <CODE>chooseNewClientCert</CODE> method is called.
379    *
380    * @param alwaysRandom Specifies whether the client certificate selection
381    * should always be random.
382    */

383   public void setAlwaysRandom(boolean alwaysRandom)
384   {
385     this.alwaysRandom = alwaysRandom;
386   }
387
388
389
390   /**
391    * Indicates whether session caching has been disabled for SSL sockets created
392    * using this socket factory.
393    *
394    * @return <CODE>true</CODE> if session caching has been disabled, or
395    * <CODE>false</CODE> if not.
396    */

397   public boolean disableSessionCaching()
398   {
399     return disableSessionCaching;
400   }
401
402
403
404   /**
405    * Specifies whether session caching should be disabled for SSL sockets
406    * created using this socket factory.
407    *
408    * @param disableSessionCaching Indicates whether session caching should be
409    * disabled for SSL sockets created using this
410    * socket factory.
411    */

412   public void setDisableSessionCaching(boolean disableSessionCaching)
413   {
414     this.disableSessionCaching = disableSessionCaching;
415   }
416
417
418
419   /**
420    * Chooses the alias of the client certificate that should be presented to the
421    * server.
422    *
423    * @param keyTypes The key type algorithm name(s) to use in making the
424    * selection.
425    * @param issuers The set of accepted issuers to use in making the
426    * selection.
427    * @param socket The socket to use in making the selection.
428    *
429    * @return The alias of the client certificate that should be presented to
430    * the server.
431    */

432   public String JavaDoc chooseClientAlias(String JavaDoc[] keyTypes, Principal[] issuers,
433                                   Socket socket)
434   {
435     if (currentAlias != null)
436     {
437       return currentAlias;
438     }
439
440     String JavaDoc alias = aliases[(random.nextInt() & 0x7FFFFFFF) % aliases.length];
441     if (! alwaysRandom)
442     {
443       currentAlias = alias;
444     }
445
446     return alias;
447   }
448
449
450
451   /**
452    * Retrieves the aliases of the certificates available for use by clients, in
453    * accordance with the provided criteria.
454    *
455    * @param keyType The key type algorithm name of certificates to include in
456    * the set of aliases returned.
457    * @param issuers The set of accepted issuers of certificates to include in
458    * the set of aliases returned.
459    *
460    * @return The aliases of the certificates available for use by clients, in
461    * accordance with the provided criteria.
462    */

463   public String JavaDoc[] getClientAliases(String JavaDoc keyType, Principal[] issuers)
464   {
465     return parentKeyManager.getClientAliases(keyType, issuers);
466   }
467
468
469
470   /**
471    * Chooses the alias of the server certificate that should be presented to
472    * clients.
473    *
474    * @param keyType The key type algorithm name to use in making the
475    * selection.
476    * @param issuers The set of accepted issuers to use in making the
477    * selection.
478    * @param socket The socket to use in making the selection.
479    *
480    * @return The alias of the server certificate that should be presented to
481    * clients.
482    */

483   public String JavaDoc chooseServerAlias(String JavaDoc keyType, Principal[] issuers,
484                                   Socket socket)
485   {
486     return parentKeyManager.chooseServerAlias(keyType, issuers, socket);
487   }
488
489
490
491   /**
492    * Retrieves the aliases of the certificates available for use by an SSL
493    * server, in accordance with the provided criteria.
494    *
495    * @param keyType The key type algorithm name of certificates to include in
496    * the set of aliases returned.
497    * @param issuers The set of accepted issuers of certificates to include in
498    * the set of aliases returned.
499    *
500    * @return The aliases of the certificates available for use by an SSL
501    * server, in accordance with the provided criteria.
502    */

503   public String JavaDoc[] getServerAliases(String JavaDoc keyType, Principal[] issuers)
504   {
505     return parentKeyManager.getServerAliases(keyType, issuers);
506   }
507
508
509
510   /**
511    * Retrieves the private key for the certificate with the specified alias.
512    *
513    * @param alias The alias of the certificate for which to retrieve the
514    * private key.
515    *
516    * @return The private key of the requested certificate, or <CODE>null</CODE>
517    * if the specified certificate cannot be found.
518    */

519   public PrivateKey getPrivateKey(String JavaDoc alias)
520   {
521     return parentKeyManager.getPrivateKey(alias);
522   }
523
524
525
526   /**
527    * Retrieves the certificate chain for the certificate with the given alias.
528    * The chain will be returned in order, with the specified certificate first
529    * and the root issuer last.
530    *
531    * @param alias The alias of the certificate for which to retrieve the
532    * certificate chain.
533    *
534    * @return The certificate chain for the certificate with the given alias, or
535    * <CODE>null</CODE> if the specified certificate cannot be found.
536    */

537   public X509Certificate[] getCertificateChain(String JavaDoc alias)
538   {
539     return parentKeyManager.getCertificateChain(alias);
540   }
541
542
543
544   /**
545    * Determines whether the provided client certificate should be trusted. In
546    * this case, the certificate will always be trusted.
547    *
548    * @param chain The peer certificate chain.
549    * @param authType The authentication type based on the client certificate.
550    */

551   public void checkClientTrusted(X509Certificate[] chain, String JavaDoc authType)
552   {
553     // No implementation required. If we don't throw an exception, then there
554
// is no problem with the cert.
555
if (debugMode)
556     {
557       System.err.println("checkClientTrusted() invoked");
558     }
559   }
560
561
562
563   /**
564    * Determines whether the provided server certificate should be trusted. In
565    * this case, the certificate will always be trusted.
566    *
567    * @param chain The peer certificate chain.
568    * @param authType The authentication type based on the server certificate.
569    */

570   public void checkServerTrusted(X509Certificate[] chain, String JavaDoc authType)
571   {
572     // No implementation required. If we don't throw an exception, then there
573
// is no problem with the cert.
574
if (debugMode)
575     {
576       System.err.println("checkServerTrusted() invoked");
577     }
578   }
579
580
581
582   /**
583    * Retrieves an array of CA certificates that are trusted for authenticating
584    * peers.
585    *
586    * @return An empty array, because we don't care about any list of CAs.
587    */

588   public X509Certificate[] getAcceptedIssuers()
589   {
590     if (debugMode)
591     {
592       System.err.println("getAcceptedIssuers() invoked");
593     }
594
595     return null;
596   }
597
598
599
600   /**
601    * Establishes an SSL socket to the provided host and port that can be used by
602    * the LDAP SDK for Java for communicating with an LDAP directory server.
603    *
604    * @param host The address of the server to which the connection is to be
605    * established.
606    * @param port The port number of the server to which the connection is to
607    * be established.
608    *
609    * @return The SSL socket that may be used for communicating with the
610    * directory server.
611    *
612    * @throws LDAPException If a problem occurs while trying to establish the
613    * connection.
614    */

615   public Socket makeSocket(String JavaDoc host, int port)
616          throws LDAPException
617   {
618     if (debugMode)
619     {
620       System.err.println("makeSocket(" + host + "," + port + ") invoked");
621     }
622
623     try
624     {
625       SSLSocket sslSocket =
626            (SSLSocket) sslSocketFactory.createSocket(host, port);
627       if (disableSessionCaching)
628       {
629         sslSocket.getSession().invalidate();
630       }
631
632       return sslSocket;
633     }
634     catch (Exception JavaDoc e)
635     {
636       throw new LDAPException("Unable to establish the SSL connection: " + e);
637     }
638   }
639
640
641
642   /**
643    * Creates a new SSL socket connected to the specified host and port.
644    *
645    * @param host The address of the system to which the SSL socket should be
646    * connected.
647    * @param port The port on the target system to which the SSL socket should
648    * be connected.
649    *
650    * @throws IOException If a problem occurs while creating the SSL socket.
651    */

652   public Socket createSocket(String JavaDoc host, int port)
653          throws IOException
654   {
655     if (debugMode)
656     {
657       System.err.println("createSocket(" + host + "," + port + ") invoked");
658     }
659
660     SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(host, port);
661     if (disableSessionCaching)
662     {
663       sslSocket.getSession().invalidate();
664     }
665
666     return sslSocket;
667   }
668
669
670
671   /**
672    * Creates a new SSL socket connected to the specified host and port.
673    *
674    * @param host The address of the system to which the SSL socket should
675    * be connected.
676    * @param port The port on the target system to which the SSL socket
677    * should be connected.
678    * @param localHost The address on the local system from which the socket
679    * should originate.
680    * @param localPort The port on the local system from which the socket
681    * should originate.
682    *
683    * @throws IOException If a problem occurs while creating the SSL socket.
684    */

685   public Socket createSocket(String JavaDoc host, int port, InetAddress localHost,
686                              int localPort)
687          throws IOException
688   {
689     if (debugMode)
690     {
691       System.err.println("createSocket(" + host + "," + port + ", " +
692                          localHost.getHostAddress() + ", " + localPort +
693                          ") invoked");
694     }
695
696     SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(host, port,
697                                                                     localHost,
698                                                                     localPort);
699     if (disableSessionCaching)
700     {
701       sslSocket.getSession().invalidate();
702     }
703
704     return sslSocket;
705   }
706
707
708
709   /**
710    * Creates a new SSL socket connected to the specified host and port.
711    *
712    * @param host The address of the system to which the SSL socket should be
713    * connected.
714    * @param port The port on the target system to which the SSL socket should
715    * be connected.
716    *
717    * @throws IOException If a problem occurs while creating the SSL socket.
718    */

719   public Socket createSocket(InetAddress host, int port)
720          throws IOException
721   {
722     if (debugMode)
723     {
724       System.err.println("createSocket(" + host.getHostAddress() + ", " + port +
725                          ") invoked");
726     }
727
728     SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(host, port);
729     if (disableSessionCaching)
730     {
731       sslSocket.getSession().invalidate();
732     }
733
734     return sslSocket;
735   }
736
737
738
739   /**
740    * Creates a new SSL socket connected to the specified host and port.
741    *
742    * @param host The address of the system to which the SSL socket
743    * should be connected.
744    * @param port The port on the target system to which the SSL socket
745    * should be connected.
746    * @param localAddress The address on the local system from which the socket
747    * should originate.
748    * @param localPort The port on the local system from which the socket
749    * should originate.
750    *
751    * @throws IOException If a problem occurs while creating the SSL socket.
752    */

753   public Socket createSocket(InetAddress host, int port,
754                              InetAddress localAddress, int localPort)
755          throws IOException
756   {
757     if (debugMode)
758     {
759       System.err.println("createSocket(" + host.getHostAddress() + "," + port +
760                          ", " + localAddress.getHostAddress() + ", " +
761                          localPort + ") invoked");
762     }
763
764     SSLSocket sslSocket =
765          (SSLSocket) sslSocketFactory.createSocket(host, port, localAddress,
766                                                    localPort);
767     if (disableSessionCaching)
768     {
769       sslSocket.getSession().invalidate();
770     }
771
772     return sslSocket;
773   }
774
775
776
777   /**
778    * Converts the provided socket to an SSL socket using this socket factory.
779    *
780    * @param socket The socket to convert to an SSL socket.
781    * @param host The host to which the socket is connected.
782    * @param port The port to which the socket is connected.
783    * @param autoClose Indicates whether the underlying socket should be closed
784    * when the returned SSL socket is closed.
785    *
786    * @throws IOException If a problem occurs while creating the SSL socket.
787    */

788   public Socket createSocket(Socket socket, String JavaDoc host, int port,
789                              boolean autoClose)
790          throws IOException
791   {
792     if (debugMode)
793     {
794       System.err.println("createSocket(Socket, " + host + ", " + port + ", " +
795                          autoClose + ") invoked");
796     }
797
798     SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket,
799                                                                     host, port,
800                                                                     autoClose);
801     if (disableSessionCaching)
802     {
803       sslSocket.getSession().invalidate();
804     }
805
806     return sslSocket;
807   }
808
809
810
811   /**
812    * Retrieves the set of cipher suites that are enabled by default.
813    *
814    * @return The set of cipher suites that are enabled by default.
815    */

816   public String JavaDoc[] getDefaultCipherSuites()
817   {
818     if (debugMode)
819     {
820       System.err.println("getDefaultCipherSuites() invoked");
821     }
822
823     return sslSocketFactory.getDefaultCipherSuites();
824   }
825
826
827
828   /**
829    * Retrieves the set of cipher suites that can be used to create SSL sockets.
830    *
831    * @return The set of cipher suites that can be used to create SSL sockets.
832    */

833   public String JavaDoc[] getSupportedCipherSuites()
834   {
835     if (debugMode)
836     {
837       System.err.println("getSupportedCipherSuites() invoked");
838     }
839
840     return sslSocketFactory.getSupportedCipherSuites();
841   }
842 }
843
844
Popular Tags