KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tomcat > util > net > jsse > JSSESocketFactory


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.tomcat.util.net.jsse;
19
20 import java.io.File JavaDoc;
21 import java.io.FileInputStream JavaDoc;
22 import java.io.FileNotFoundException JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.InputStream JavaDoc;
25 import java.net.InetAddress JavaDoc;
26 import java.net.ServerSocket JavaDoc;
27 import java.net.Socket JavaDoc;
28 import java.net.SocketException JavaDoc;
29 import java.security.KeyStore JavaDoc;
30 import java.security.SecureRandom JavaDoc;
31 import java.security.cert.CRL JavaDoc;
32 import java.security.cert.CRLException JavaDoc;
33 import java.security.cert.CertPathParameters JavaDoc;
34 import java.security.cert.CertStore JavaDoc;
35 import java.security.cert.CertStoreParameters JavaDoc;
36 import java.security.cert.CertificateException JavaDoc;
37 import java.security.cert.CertificateFactory JavaDoc;
38 import java.security.cert.CollectionCertStoreParameters JavaDoc;
39 import java.security.cert.PKIXBuilderParameters JavaDoc;
40 import java.security.cert.X509CertSelector JavaDoc;
41 import java.util.Collection JavaDoc;
42 import java.util.Vector JavaDoc;
43
44 import javax.net.ssl.CertPathTrustManagerParameters;
45 import javax.net.ssl.KeyManager;
46 import javax.net.ssl.KeyManagerFactory;
47 import javax.net.ssl.ManagerFactoryParameters;
48 import javax.net.ssl.SSLContext;
49 import javax.net.ssl.SSLException;
50 import javax.net.ssl.SSLServerSocket;
51 import javax.net.ssl.SSLServerSocketFactory;
52 import javax.net.ssl.SSLSocket;
53 import javax.net.ssl.TrustManager;
54 import javax.net.ssl.TrustManagerFactory;
55 import javax.net.ssl.X509KeyManager;
56
57 import org.apache.tomcat.util.res.StringManager;
58
59 /*
60   1. Make the JSSE's jars available, either as an installed
61      extension (copy them into jre/lib/ext) or by adding
62      them to the Tomcat classpath.
63   2. keytool -genkey -alias tomcat -keyalg RSA
64      Use "changeit" as password ( this is the default we use )
65  */

66
67 /**
68  * SSL server socket factory. It _requires_ a valid RSA key and
69  * JSSE.
70  *
71  * @author Harish Prabandham
72  * @author Costin Manolache
73  * @author Stefan Freyr Stefansson
74  * @author EKR -- renamed to JSSESocketFactory
75  * @author Jan Luehe
76  * @author Bill Barker
77  */

78 public class JSSESocketFactory
79     extends org.apache.tomcat.util.net.ServerSocketFactory {
80
81     private static StringManager sm =
82         StringManager.getManager("org.apache.tomcat.util.net.jsse.res");
83
84     // defaults
85
static String JavaDoc defaultProtocol = "TLS";
86     static boolean defaultClientAuth = false;
87     static String JavaDoc defaultKeystoreType = "JKS";
88     private static final String JavaDoc defaultKeystoreFile
89         = System.getProperty("user.home") + "/.keystore";
90     private static final String JavaDoc defaultKeyPass = "changeit";
91     static org.apache.commons.logging.Log log =
92         org.apache.commons.logging.LogFactory.getLog(JSSESocketFactory.class);
93
94     protected boolean initialized;
95     protected String JavaDoc clientAuth = "false";
96     protected SSLServerSocketFactory sslProxy = null;
97     protected String JavaDoc[] enabledCiphers;
98
99     /**
100      * Flag to state that we require client authentication.
101      */

102     protected boolean requireClientAuth = false;
103
104     /**
105      * Flag to state that we would like client authentication.
106      */

107     protected boolean wantClientAuth = false;
108
109
110     public JSSESocketFactory () {
111     }
112
113     public ServerSocket JavaDoc createSocket (int port)
114         throws IOException JavaDoc
115     {
116         if (!initialized) init();
117         ServerSocket JavaDoc socket = sslProxy.createServerSocket(port);
118         initServerSocket(socket);
119         return socket;
120     }
121     
122     public ServerSocket JavaDoc createSocket (int port, int backlog)
123         throws IOException JavaDoc
124     {
125         if (!initialized) init();
126         ServerSocket JavaDoc socket = sslProxy.createServerSocket(port, backlog);
127         initServerSocket(socket);
128         return socket;
129     }
130     
131     public ServerSocket JavaDoc createSocket (int port, int backlog,
132                                       InetAddress JavaDoc ifAddress)
133         throws IOException JavaDoc
134     {
135         if (!initialized) init();
136         ServerSocket JavaDoc socket = sslProxy.createServerSocket(port, backlog,
137                                                           ifAddress);
138         initServerSocket(socket);
139         return socket;
140     }
141     
142     public Socket JavaDoc acceptSocket(ServerSocket JavaDoc socket)
143         throws IOException JavaDoc
144     {
145         SSLSocket asock = null;
146         try {
147              asock = (SSLSocket)socket.accept();
148              configureClientAuth(asock);
149         } catch (SSLException e){
150           throw new SocketException JavaDoc("SSL handshake error" + e.toString());
151         }
152         return asock;
153     }
154
155     public void handshake(Socket JavaDoc sock) throws IOException JavaDoc {
156         ((SSLSocket)sock).startHandshake();
157     }
158
159     /*
160      * Determines the SSL cipher suites to be enabled.
161      *
162      * @param requestedCiphers Comma-separated list of requested ciphers
163      * @param supportedCiphers Array of supported ciphers
164      *
165      * @return Array of SSL cipher suites to be enabled, or null if none of the
166      * requested ciphers are supported
167      */

168     protected String JavaDoc[] getEnabledCiphers(String JavaDoc requestedCiphers,
169                                          String JavaDoc[] supportedCiphers) {
170
171         String JavaDoc[] enabledCiphers = null;
172
173         if (requestedCiphers != null) {
174             Vector JavaDoc vec = null;
175             String JavaDoc cipher = requestedCiphers;
176             int index = requestedCiphers.indexOf(',');
177             if (index != -1) {
178                 int fromIndex = 0;
179                 while (index != -1) {
180                     cipher = requestedCiphers.substring(fromIndex, index).trim();
181                     if (cipher.length() > 0) {
182                         /*
183                          * Check to see if the requested cipher is among the
184                          * supported ciphers, i.e., may be enabled
185                          */

186                         for (int i=0; supportedCiphers != null
187                                      && i<supportedCiphers.length; i++) {
188                             if (supportedCiphers[i].equals(cipher)) {
189                                 if (vec == null) {
190                                     vec = new Vector JavaDoc();
191                                 }
192                                 vec.addElement(cipher);
193                                 break;
194                             }
195                         }
196                     }
197                     fromIndex = index+1;
198                     index = requestedCiphers.indexOf(',', fromIndex);
199                 } // while
200
cipher = requestedCiphers.substring(fromIndex);
201             }
202
203             if (cipher != null) {
204                 cipher = cipher.trim();
205                 if (cipher.length() > 0) {
206                     /*
207                      * Check to see if the requested cipher is among the
208                      * supported ciphers, i.e., may be enabled
209                      */

210                     for (int i=0; supportedCiphers != null
211                                  && i<supportedCiphers.length; i++) {
212                         if (supportedCiphers[i].equals(cipher)) {
213                             if (vec == null) {
214                                 vec = new Vector JavaDoc();
215                             }
216                             vec.addElement(cipher);
217                             break;
218                         }
219                     }
220                 }
221             }
222
223             if (vec != null) {
224                 enabledCiphers = new String JavaDoc[vec.size()];
225                 vec.copyInto(enabledCiphers);
226             }
227         } else {
228             enabledCiphers = sslProxy.getDefaultCipherSuites();
229         }
230
231         return enabledCiphers;
232     }
233      
234     /*
235      * Gets the SSL server's keystore password.
236      */

237     protected String JavaDoc getKeystorePassword() {
238         String JavaDoc keyPass = (String JavaDoc)attributes.get("keypass");
239         if (keyPass == null) {
240             keyPass = defaultKeyPass;
241         }
242         String JavaDoc keystorePass = (String JavaDoc)attributes.get("keystorePass");
243         if (keystorePass == null) {
244             keystorePass = keyPass;
245         }
246         return keystorePass;
247     }
248
249     /*
250      * Gets the SSL server's keystore.
251      */

252     protected KeyStore JavaDoc getKeystore(String JavaDoc type, String JavaDoc pass)
253             throws IOException JavaDoc {
254
255         String JavaDoc keystoreFile = (String JavaDoc)attributes.get("keystore");
256         if (keystoreFile == null)
257             keystoreFile = defaultKeystoreFile;
258
259         return getStore(type, keystoreFile, pass);
260     }
261
262     /*
263      * Gets the SSL server's truststore.
264      */

265     protected KeyStore JavaDoc getTrustStore(String JavaDoc keystoreType) throws IOException JavaDoc {
266         KeyStore JavaDoc trustStore = null;
267
268         String JavaDoc trustStoreFile = (String JavaDoc)attributes.get("truststoreFile");
269         if(trustStoreFile == null) {
270             trustStoreFile = System.getProperty("javax.net.ssl.trustStore");
271         }
272         if(log.isDebugEnabled()) {
273             log.debug("Truststore = " + trustStoreFile);
274         }
275         String JavaDoc trustStorePassword = (String JavaDoc)attributes.get("truststorePass");
276         if( trustStorePassword == null) {
277             trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
278         }
279         if( trustStorePassword == null ) {
280             trustStorePassword = getKeystorePassword();
281         }
282         if(log.isDebugEnabled()) {
283             log.debug("TrustPass = " + trustStorePassword);
284         }
285         String JavaDoc truststoreType = (String JavaDoc)attributes.get("truststoreType");
286         if(truststoreType == null) {
287             truststoreType = keystoreType;
288         }
289         if(log.isDebugEnabled()) {
290             log.debug("trustType = " + truststoreType);
291         }
292         if (trustStoreFile != null && trustStorePassword != null){
293             trustStore = getStore(truststoreType, trustStoreFile,
294                                   trustStorePassword);
295         }
296
297         return trustStore;
298     }
299
300     /*
301      * Gets the key- or truststore with the specified type, path, and password.
302      */

303     private KeyStore JavaDoc getStore(String JavaDoc type, String JavaDoc path, String JavaDoc pass)
304             throws IOException JavaDoc {
305
306         KeyStore JavaDoc ks = null;
307         InputStream JavaDoc istream = null;
308         try {
309             ks = KeyStore.getInstance(type);
310             if(! "PKCS11".equalsIgnoreCase(type) ) {
311                 File JavaDoc keyStoreFile = new File JavaDoc(path);
312                 if (!keyStoreFile.isAbsolute()) {
313                     keyStoreFile = new File JavaDoc(System.getProperty("catalina.base"),
314                                             path);
315                 }
316                 istream = new FileInputStream JavaDoc(keyStoreFile);
317             }
318
319             ks.load(istream, pass.toCharArray());
320         } catch (FileNotFoundException JavaDoc fnfe) {
321             throw fnfe;
322         } catch (IOException JavaDoc ioe) {
323             throw ioe;
324         } catch(Exception JavaDoc ex) {
325             log.error("Exception trying to load keystore " +path,ex);
326             throw new IOException JavaDoc("Exception trying to load keystore " +
327                                   path + ": " + ex.getMessage() );
328         } finally {
329             if (istream != null) {
330                 try {
331                     istream.close();
332                 } catch (IOException JavaDoc ioe) {
333                     // Do nothing
334
}
335             }
336         }
337
338         return ks;
339     }
340
341     /**
342      * Reads the keystore and initializes the SSL socket factory.
343      */

344     void init() throws IOException JavaDoc {
345         try {
346
347             String JavaDoc clientAuthStr = (String JavaDoc) attributes.get("clientauth");
348             if("true".equalsIgnoreCase(clientAuthStr) ||
349                "yes".equalsIgnoreCase(clientAuthStr)) {
350                 requireClientAuth = true;
351             } else if("want".equalsIgnoreCase(clientAuthStr)) {
352                 wantClientAuth = true;
353             }
354
355             // SSL protocol variant (e.g., TLS, SSL v3, etc.)
356
String JavaDoc protocol = (String JavaDoc) attributes.get("protocol");
357             if (protocol == null) {
358                 protocol = defaultProtocol;
359             }
360
361             // Certificate encoding algorithm (e.g., SunX509)
362
String JavaDoc algorithm = (String JavaDoc) attributes.get("algorithm");
363             if (algorithm == null) {
364                 algorithm = KeyManagerFactory.getDefaultAlgorithm();;
365             }
366
367             String JavaDoc keystoreType = (String JavaDoc) attributes.get("keystoreType");
368             if (keystoreType == null) {
369                 keystoreType = defaultKeystoreType;
370             }
371
372         String JavaDoc trustAlgorithm = (String JavaDoc)attributes.get("truststoreAlgorithm");
373         if( trustAlgorithm == null ) {
374             trustAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
375         }
376             // Create and init SSLContext
377
SSLContext context = SSLContext.getInstance(protocol);
378             context.init(getKeyManagers(keystoreType, algorithm,
379                                         (String JavaDoc) attributes.get("keyAlias")),
380                          getTrustManagers(keystoreType, trustAlgorithm),
381                          new SecureRandom JavaDoc());
382
383             // create proxy
384
sslProxy = context.getServerSocketFactory();
385
386             // Determine which cipher suites to enable
387
String JavaDoc requestedCiphers = (String JavaDoc)attributes.get("ciphers");
388             enabledCiphers = getEnabledCiphers(requestedCiphers,
389                                                sslProxy.getSupportedCipherSuites());
390
391         } catch(Exception JavaDoc e) {
392             if( e instanceof IOException JavaDoc )
393                 throw (IOException JavaDoc)e;
394             throw new IOException JavaDoc(e.getMessage());
395         }
396     }
397
398     /**
399      * Gets the initialized key managers.
400      */

401     protected KeyManager[] getKeyManagers(String JavaDoc keystoreType,
402                                           String JavaDoc algorithm,
403                                           String JavaDoc keyAlias)
404                 throws Exception JavaDoc {
405
406         KeyManager[] kms = null;
407
408         String JavaDoc keystorePass = getKeystorePassword();
409
410         KeyStore JavaDoc ks = getKeystore(keystoreType, keystorePass);
411         if (keyAlias != null && !ks.isKeyEntry(keyAlias)) {
412             throw new IOException JavaDoc(sm.getString("jsse.alias_no_key_entry", keyAlias));
413         }
414
415         KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
416         kmf.init(ks, keystorePass.toCharArray());
417
418         kms = kmf.getKeyManagers();
419         if (keyAlias != null) {
420             if (JSSESocketFactory.defaultKeystoreType.equals(keystoreType)) {
421                 keyAlias = keyAlias.toLowerCase();
422             }
423             for(int i=0; i<kms.length; i++) {
424                 kms[i] = new JSSEKeyManager((X509KeyManager)kms[i], keyAlias);
425             }
426         }
427
428         return kms;
429     }
430
431     /**
432      * Gets the intialized trust managers.
433      */

434     protected TrustManager[] getTrustManagers(String JavaDoc keystoreType, String JavaDoc algorithm)
435         throws Exception JavaDoc {
436         String JavaDoc crlf = (String JavaDoc) attributes.get("crlFile");
437         
438         TrustManager[] tms = null;
439         
440         String JavaDoc truststoreType = (String JavaDoc) attributes.get("truststoreType");
441         if (truststoreType == null) {
442             truststoreType = keystoreType;
443         }
444         KeyStore JavaDoc trustStore = getTrustStore(truststoreType);
445         if (trustStore != null) {
446             if (crlf == null) {
447                 TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
448                 tmf.init(trustStore);
449                 tms = tmf.getTrustManagers();
450             } else {
451                 TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
452                 CertPathParameters JavaDoc params = getParameters(algorithm, crlf, trustStore);
453                 ManagerFactoryParameters mfp = new CertPathTrustManagerParameters(params);
454                 tmf.init(mfp);
455                 tms = tmf.getTrustManagers();
456             }
457         }
458         
459         return tms;
460     }
461     
462     /**
463      * Return the initialization parameters for the TrustManager.
464      * Currently, only the default <code>PKIX</code> is supported.
465      *
466      * @param algorithm The algorithm to get parameters for.
467      * @param crlf The path to the CRL file.
468      * @param trustStore The configured TrustStore.
469      * @return The parameters including the CRLs and TrustStore.
470      */

471     protected CertPathParameters JavaDoc getParameters(String JavaDoc algorithm,
472                                                 String JavaDoc crlf,
473                                                 KeyStore JavaDoc trustStore)
474         throws Exception JavaDoc {
475         CertPathParameters JavaDoc params = null;
476         if("PKIX".equalsIgnoreCase(algorithm)) {
477             PKIXBuilderParameters JavaDoc xparams = new PKIXBuilderParameters JavaDoc(trustStore,
478                                                                      new X509CertSelector JavaDoc());
479             Collection JavaDoc crls = getCRLs(crlf);
480             CertStoreParameters JavaDoc csp = new CollectionCertStoreParameters JavaDoc(crls);
481             CertStore JavaDoc store = CertStore.getInstance("Collection", csp);
482             xparams.addCertStore(store);
483             xparams.setRevocationEnabled(true);
484             String JavaDoc trustLength = (String JavaDoc)attributes.get("trustMaxCertLength");
485             if(trustLength != null) {
486                 try {
487                     xparams.setMaxPathLength(Integer.parseInt(trustLength));
488                 } catch(Exception JavaDoc ex) {
489                     log.warn("Bad maxCertLength: "+trustLength);
490                 }
491             }
492
493             params = xparams;
494         } else {
495             throw new CRLException JavaDoc("CRLs not supported for type: "+algorithm);
496         }
497         return params;
498     }
499
500
501     /**
502      * Load the collection of CRLs.
503      *
504      */

505     protected Collection JavaDoc<? extends CRL JavaDoc> getCRLs(String JavaDoc crlf)
506         throws IOException JavaDoc, CRLException JavaDoc, CertificateException JavaDoc {
507
508         File JavaDoc crlFile = new File JavaDoc(crlf);
509         if( !crlFile.isAbsolute() ) {
510             crlFile = new File JavaDoc(System.getProperty("catalina.base"), crlf);
511         }
512         Collection JavaDoc<? extends CRL JavaDoc> crls = null;
513         InputStream JavaDoc is = null;
514         try {
515             CertificateFactory JavaDoc cf = CertificateFactory.getInstance("X.509");
516             is = new FileInputStream JavaDoc(crlFile);
517             crls = cf.generateCRLs(is);
518         } catch(IOException JavaDoc iex) {
519             throw iex;
520         } catch(CRLException JavaDoc crle) {
521             throw crle;
522         } catch(CertificateException JavaDoc ce) {
523             throw ce;
524         } finally {
525             if(is != null) {
526                 try{
527                     is.close();
528                 } catch(Exception JavaDoc ex) {
529                 }
530             }
531         }
532         return crls;
533     }
534
535     /**
536      * Set the SSL protocol variants to be enabled.
537      * @param socket the SSLServerSocket.
538      * @param protocols the protocols to use.
539      */

540     protected void setEnabledProtocols(SSLServerSocket socket, String JavaDoc []protocols){
541         if (protocols != null) {
542             socket.setEnabledProtocols(protocols);
543         }
544     }
545
546     /**
547      * Determines the SSL protocol variants to be enabled.
548      *
549      * @param socket The socket to get supported list from.
550      * @param requestedProtocols Comma-separated list of requested SSL
551      * protocol variants
552      *
553      * @return Array of SSL protocol variants to be enabled, or null if none of
554      * the requested protocol variants are supported
555      */

556     protected String JavaDoc[] getEnabledProtocols(SSLServerSocket socket,
557                                            String JavaDoc requestedProtocols){
558         String JavaDoc[] supportedProtocols = socket.getSupportedProtocols();
559
560         String JavaDoc[] enabledProtocols = null;
561
562         if (requestedProtocols != null) {
563             Vector JavaDoc vec = null;
564             String JavaDoc protocol = requestedProtocols;
565             int index = requestedProtocols.indexOf(',');
566             if (index != -1) {
567                 int fromIndex = 0;
568                 while (index != -1) {
569                     protocol = requestedProtocols.substring(fromIndex, index).trim();
570                     if (protocol.length() > 0) {
571                         /*
572                          * Check to see if the requested protocol is among the
573                          * supported protocols, i.e., may be enabled
574                          */

575                         for (int i=0; supportedProtocols != null
576                                      && i<supportedProtocols.length; i++) {
577                             if (supportedProtocols[i].equals(protocol)) {
578                                 if (vec == null) {
579                                     vec = new Vector JavaDoc();
580                                 }
581                                 vec.addElement(protocol);
582                                 break;
583                             }
584                         }
585                     }
586                     fromIndex = index+1;
587                     index = requestedProtocols.indexOf(',', fromIndex);
588                 } // while
589
protocol = requestedProtocols.substring(fromIndex);
590             }
591
592             if (protocol != null) {
593                 protocol = protocol.trim();
594                 if (protocol.length() > 0) {
595                     /*
596                      * Check to see if the requested protocol is among the
597                      * supported protocols, i.e., may be enabled
598                      */

599                     for (int i=0; supportedProtocols != null
600                                  && i<supportedProtocols.length; i++) {
601                         if (supportedProtocols[i].equals(protocol)) {
602                             if (vec == null) {
603                                 vec = new Vector JavaDoc();
604                             }
605                             vec.addElement(protocol);
606                             break;
607                         }
608                     }
609                 }
610             }
611
612             if (vec != null) {
613                 enabledProtocols = new String JavaDoc[vec.size()];
614                 vec.copyInto(enabledProtocols);
615             }
616         }
617
618         return enabledProtocols;
619     }
620
621     /**
622      * Configure Client authentication for this version of JSSE. The
623      * JSSE included in Java 1.4 supports the 'want' value. Prior
624      * versions of JSSE will treat 'want' as 'false'.
625      * @param socket the SSLServerSocket
626      */

627     protected void configureClientAuth(SSLServerSocket socket){
628         if (wantClientAuth){
629             socket.setWantClientAuth(wantClientAuth);
630         } else {
631             socket.setNeedClientAuth(requireClientAuth);
632         }
633     }
634
635     /**
636      * Configure Client authentication for this version of JSSE. The
637      * JSSE included in Java 1.4 supports the 'want' value. Prior
638      * versions of JSSE will treat 'want' as 'false'.
639      * @param socket the SSLSocket
640      */

641     protected void configureClientAuth(SSLSocket socket){
642         // Per JavaDocs: SSLSockets returned from
643
// SSLServerSocket.accept() inherit this setting.
644
}
645     
646     /**
647      * Configures the given SSL server socket with the requested cipher suites,
648      * protocol versions, and need for client authentication
649      */

650     private void initServerSocket(ServerSocket JavaDoc ssocket) {
651
652         SSLServerSocket socket = (SSLServerSocket) ssocket;
653
654         if (enabledCiphers != null) {
655             socket.setEnabledCipherSuites(enabledCiphers);
656         }
657
658         String JavaDoc requestedProtocols = (String JavaDoc) attributes.get("protocols");
659         setEnabledProtocols(socket, getEnabledProtocols(socket,
660                                                          requestedProtocols));
661
662         // we don't know if client auth is needed -
663
// after parsing the request we may re-handshake
664
configureClientAuth(socket);
665     }
666
667 }
668
Popular Tags