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;
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.net.InetAddress;
26 import java.net.ServerSocket;
27 import java.net.Socket;
28 import java.net.SocketException;
29 import java.security.KeyStore;
30 import java.security.SecureRandom;
31 import java.security.cert.CRL;
32 import java.security.cert.CRLException;
33 import java.security.cert.CertPathParameters;
34 import java.security.cert.CertStore;
35 import java.security.cert.CertStoreParameters;
36 import java.security.cert.CertificateException;
37 import java.security.cert.CertificateFactory;
38 import java.security.cert.CollectionCertStoreParameters;
39 import java.security.cert.PKIXBuilderParameters;
40 import java.security.cert.X509CertSelector;
41 import java.util.Collection;
42 import java.util.Vector;
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 defaultProtocol = "TLS";
86 static boolean defaultClientAuth = false;
87 static String defaultKeystoreType = "JKS";
88 private static final String defaultKeystoreFile
89 = System.getProperty("user.home") + "/.keystore";
90 private static final String defaultKeyPass = "changeit";
91 static org.apache.juli.logging.Log log =
92 org.apache.juli.logging.LogFactory.getLog(JSSESocketFactory.class);
93
94 protected boolean initialized;
95 protected String clientAuth = "false";
96 protected SSLServerSocketFactory sslProxy = null;
97 protected String[] 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 createSocket (int port)
114 throws IOException
115 {
116 if (!initialized) init();
117 ServerSocket socket = sslProxy.createServerSocket(port);
118 initServerSocket(socket);
119 return socket;
120 }
121
122 public ServerSocket createSocket (int port, int backlog)
123 throws IOException
124 {
125 if (!initialized) init();
126 ServerSocket socket = sslProxy.createServerSocket(port, backlog);
127 initServerSocket(socket);
128 return socket;
129 }
130
131 public ServerSocket createSocket (int port, int backlog,
132 InetAddress ifAddress)
133 throws IOException
134 {
135 if (!initialized) init();
136 ServerSocket socket = sslProxy.createServerSocket(port, backlog,
137 ifAddress);
138 initServerSocket(socket);
139 return socket;
140 }
141
142 public Socket acceptSocket(ServerSocket socket)
143 throws IOException
144 {
145 SSLSocket asock = null;
146 try {
147 asock = (SSLSocket)socket.accept();
148 configureClientAuth(asock);
149 } catch (SSLException e){
150 throw new SocketException("SSL handshake error" + e.toString());
151 }
152 return asock;
153 }
154
155 public void handshake(Socket sock) throws IOException {
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[] getEnabledCiphers(String requestedCiphers,
169 String[] supportedCiphers) {
170
171 String[] enabledCiphers = null;
172
173 if (requestedCiphers != null) {
174 Vector vec = null;
175 String 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();
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();
215 }
216 vec.addElement(cipher);
217 break;
218 }
219 }
220 }
221 }
222
223 if (vec != null) {
224 enabledCiphers = new String[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 getKeystorePassword() {
238 String keyPass = (String)attributes.get("keypass");
239 if (keyPass == null) {
240 keyPass = defaultKeyPass;
241 }
242 String keystorePass = (String)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 getKeystore(String type, String pass)
253 throws IOException {
254
255 String keystoreFile = (String)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 getTrustStore(String keystoreType) throws IOException {
266 KeyStore trustStore = null;
267
268 String trustStoreFile = (String)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 trustStorePassword = (String)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 truststoreType = (String)attributes.get("truststoreType");
286 if( truststoreType == null) {
287 truststoreType = System.getProperty("javax.net.ssl.trustStoreType");
288 }
289 if(truststoreType == null) {
290 truststoreType = keystoreType;
291 }
292 if(log.isDebugEnabled()) {
293 log.debug("trustType = " + truststoreType);
294 }
295 if (trustStoreFile != null && trustStorePassword != null){
296 trustStore = getStore(truststoreType, trustStoreFile,
297 trustStorePassword);
298 }
299
300 return trustStore;
301 }
302
303 /*
304 * Gets the key- or truststore with the specified type, path, and password.
305 */
306 private KeyStore getStore(String type, String path, String pass)
307 throws IOException {
308
309 KeyStore ks = null;
310 InputStream istream = null;
311 try {
312 ks = KeyStore.getInstance(type);
313 if(! "PKCS11".equalsIgnoreCase(type) ) {
314 File keyStoreFile = new File(path);
315 if (!keyStoreFile.isAbsolute()) {
316 keyStoreFile = new File(System.getProperty("catalina.base"),
317 path);
318 }
319 istream = new FileInputStream(keyStoreFile);
320 }
321
322 ks.load(istream, pass.toCharArray());
323 } catch (FileNotFoundException fnfe) {
324 throw fnfe;
325 } catch (IOException ioe) {
326 throw ioe;
327 } catch(Exception ex) {
328 log.error("Exception trying to load keystore " +path,ex);
329 throw new IOException("Exception trying to load keystore " +
330 path + ": " + ex.getMessage() );
331 } finally {
332 if (istream != null) {
333 try {
334 istream.close();
335 } catch (IOException ioe) {
336 // Do nothing
337 }
338 }
339 }
340
341 return ks;
342 }
343
344 /**
345 * Reads the keystore and initializes the SSL socket factory.
346 */
347 void init() throws IOException {
348 try {
349
350 String clientAuthStr = (String) attributes.get("clientauth");
351 if("true".equalsIgnoreCase(clientAuthStr) ||
352 "yes".equalsIgnoreCase(clientAuthStr)) {
353 requireClientAuth = true;
354 } else if("want".equalsIgnoreCase(clientAuthStr)) {
355 wantClientAuth = true;
356 }
357
358 // SSL protocol variant (e.g., TLS, SSL v3, etc.)
359 String protocol = (String) attributes.get("protocol");
360 if (protocol == null) {
361 protocol = defaultProtocol;
362 }
363
364 // Certificate encoding algorithm (e.g., SunX509)
365 String algorithm = (String) attributes.get("algorithm");
366 if (algorithm == null) {
367 algorithm = KeyManagerFactory.getDefaultAlgorithm();;
368 }
369
370 String keystoreType = (String) attributes.get("keystoreType");
371 if (keystoreType == null) {
372 keystoreType = defaultKeystoreType;
373 }
374
375 String trustAlgorithm = (String)attributes.get("truststoreAlgorithm");
376 if( trustAlgorithm == null ) {
377 trustAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
378 }
379 // Create and init SSLContext
380 SSLContext context = SSLContext.getInstance(protocol);
381 context.init(getKeyManagers(keystoreType, algorithm,
382 (String) attributes.get("keyAlias")),
383 getTrustManagers(keystoreType, trustAlgorithm),
384 new SecureRandom());
385
386 // create proxy
387 sslProxy = context.getServerSocketFactory();
388
389 // Determine which cipher suites to enable
390 String requestedCiphers = (String)attributes.get("ciphers");
391 enabledCiphers = getEnabledCiphers(requestedCiphers,
392 sslProxy.getSupportedCipherSuites());
393
394 } catch(Exception e) {
395 if( e instanceof IOException )
396 throw (IOException)e;
397 throw new IOException(e.getMessage());
398 }
399 }
400
401 /**
402 * Gets the initialized key managers.
403 */
404 protected KeyManager[] getKeyManagers(String keystoreType,
405 String algorithm,
406 String keyAlias)
407 throws Exception {
408
409 KeyManager[] kms = null;
410
411 String keystorePass = getKeystorePassword();
412
413 KeyStore ks = getKeystore(keystoreType, keystorePass);
414 if (keyAlias != null && !ks.isKeyEntry(keyAlias)) {
415 throw new IOException(sm.getString("jsse.alias_no_key_entry", keyAlias));
416 }
417
418 KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
419 kmf.init(ks, keystorePass.toCharArray());
420
421 kms = kmf.getKeyManagers();
422 if (keyAlias != null) {
423 if (JSSESocketFactory.defaultKeystoreType.equals(keystoreType)) {
424 keyAlias = keyAlias.toLowerCase();
425 }
426 for(int i=0; i<kms.length; i++) {
427 kms[i] = new JSSEKeyManager((X509KeyManager)kms[i], keyAlias);
428 }
429 }
430
431 return kms;
432 }
433
434 /**
435 * Gets the intialized trust managers.
436 */
437 protected TrustManager[] getTrustManagers(String keystoreType, String algorithm)
438 throws Exception {
439 String crlf = (String) attributes.get("crlFile");
440
441 TrustManager[] tms = null;
442
443 String truststoreType = (String) attributes.get("truststoreType");
444 if (truststoreType == null) {
445 truststoreType = keystoreType;
446 }
447 KeyStore trustStore = getTrustStore(truststoreType);
448 if (trustStore != null) {
449 if (crlf == null) {
450 TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
451 tmf.init(trustStore);
452 tms = tmf.getTrustManagers();
453 } else {
454 TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
455 CertPathParameters params = getParameters(algorithm, crlf, trustStore);
456 ManagerFactoryParameters mfp = new CertPathTrustManagerParameters(params);
457 tmf.init(mfp);
458 tms = tmf.getTrustManagers();
459 }
460 }
461
462 return tms;
463 }
464
465 /**
466 * Return the initialization parameters for the TrustManager.
467 * Currently, only the default <code>PKIX</code> is supported.
468 *
469 * @param algorithm The algorithm to get parameters for.
470 * @param crlf The path to the CRL file.
471 * @param trustStore The configured TrustStore.
472 * @return The parameters including the CRLs and TrustStore.
473 */
474 protected CertPathParameters getParameters(String algorithm,
475 String crlf,
476 KeyStore trustStore)
477 throws Exception {
478 CertPathParameters params = null;
479 if("PKIX".equalsIgnoreCase(algorithm)) {
480 PKIXBuilderParameters xparams = new PKIXBuilderParameters(trustStore,
481 new X509CertSelector());
482 Collection crls = getCRLs(crlf);
483 CertStoreParameters csp = new CollectionCertStoreParameters(crls);
484 CertStore store = CertStore.getInstance("Collection", csp);
485 xparams.addCertStore(store);
486 xparams.setRevocationEnabled(true);
487 String trustLength = (String)attributes.get("trustMaxCertLength");
488 if(trustLength != null) {
489 try {
490 xparams.setMaxPathLength(Integer.parseInt(trustLength));
491 } catch(Exception ex) {
492 log.warn("Bad maxCertLength: "+trustLength);
493 }
494 }
495
496 params = xparams;
497 } else {
498 throw new CRLException("CRLs not supported for type: "+algorithm);
499 }
500 return params;
501 }
502
503
504 /**
505 * Load the collection of CRLs.
506 *
507 */
508 protected Collection<? extends CRL> getCRLs(String crlf)
509 throws IOException, CRLException, CertificateException {
510
511 File crlFile = new File(crlf);
512 if( !crlFile.isAbsolute() ) {
513 crlFile = new File(System.getProperty("catalina.base"), crlf);
514 }
515 Collection<? extends CRL> crls = null;
516 InputStream is = null;
517 try {
518 CertificateFactory cf = CertificateFactory.getInstance("X.509");
519 is = new FileInputStream(crlFile);
520 crls = cf.generateCRLs(is);
521 } catch(IOException iex) {
522 throw iex;
523 } catch(CRLException crle) {
524 throw crle;
525 } catch(CertificateException ce) {
526 throw ce;
527 } finally {
528 if(is != null) {
529 try{
530 is.close();
531 } catch(Exception ex) {
532 }
533 }
534 }
535 return crls;
536 }
537
538 /**
539 * Set the SSL protocol variants to be enabled.
540 * @param socket the SSLServerSocket.
541 * @param protocols the protocols to use.
542 */
543 protected void setEnabledProtocols(SSLServerSocket socket, String []protocols){
544 if (protocols != null) {
545 socket.setEnabledProtocols(protocols);
546 }
547 }
548
549 /**
550 * Determines the SSL protocol variants to be enabled.
551 *
552 * @param socket The socket to get supported list from.
553 * @param requestedProtocols Comma-separated list of requested SSL
554 * protocol variants
555 *
556 * @return Array of SSL protocol variants to be enabled, or null if none of
557 * the requested protocol variants are supported
558 */
559 protected String[] getEnabledProtocols(SSLServerSocket socket,
560 String requestedProtocols){
561 String[] supportedProtocols = socket.getSupportedProtocols();
562
563 String[] enabledProtocols = null;
564
565 if (requestedProtocols != null) {
566 Vector vec = null;
567 String protocol = requestedProtocols;
568 int index = requestedProtocols.indexOf(',');
569 if (index != -1) {
570 int fromIndex = 0;
571 while (index != -1) {
572 protocol = requestedProtocols.substring(fromIndex, index).trim();
573 if (protocol.length() > 0) {
574 /*
575 * Check to see if the requested protocol is among the
576 * supported protocols, i.e., may be enabled
577 */
578 for (int i=0; supportedProtocols != null
579 && i<supportedProtocols.length; i++) {
580 if (supportedProtocols[i].equals(protocol)) {
581 if (vec == null) {
582 vec = new Vector();
583 }
584 vec.addElement(protocol);
585 break;
586 }
587 }
588 }
589 fromIndex = index+1;
590 index = requestedProtocols.indexOf(',', fromIndex);
591 } // while
592 protocol = requestedProtocols.substring(fromIndex);
593 }
594
595 if (protocol != null) {
596 protocol = protocol.trim();
597 if (protocol.length() > 0) {
598 /*
599 * Check to see if the requested protocol is among the
600 * supported protocols, i.e., may be enabled
601 */
602 for (int i=0; supportedProtocols != null
603 && i<supportedProtocols.length; i++) {
604 if (supportedProtocols[i].equals(protocol)) {
605 if (vec == null) {
606 vec = new Vector();
607 }
608 vec.addElement(protocol);
609 break;
610 }
611 }
612 }
613 }
614
615 if (vec != null) {
616 enabledProtocols = new String[vec.size()];
617 vec.copyInto(enabledProtocols);
618 }
619 }
620
621 return enabledProtocols;
622 }
623
624 /**
625 * Configure Client authentication for this version of JSSE. The
626 * JSSE included in Java 1.4 supports the 'want' value. Prior
627 * versions of JSSE will treat 'want' as 'false'.
628 * @param socket the SSLServerSocket
629 */
630 protected void configureClientAuth(SSLServerSocket socket){
631 if (wantClientAuth){
632 socket.setWantClientAuth(wantClientAuth);
633 } else {
634 socket.setNeedClientAuth(requireClientAuth);
635 }
636 }
637
638 /**
639 * Configure Client authentication for this version of JSSE. The
640 * JSSE included in Java 1.4 supports the 'want' value. Prior
641 * versions of JSSE will treat 'want' as 'false'.
642 * @param socket the SSLSocket
643 */
644 protected void configureClientAuth(SSLSocket socket){
645 // Per JavaDocs: SSLSockets returned from
646 // SSLServerSocket.accept() inherit this setting.
647 }
648
649 /**
650 * Configures the given SSL server socket with the requested cipher suites,
651 * protocol versions, and need for client authentication
652 */
653 private void initServerSocket(ServerSocket ssocket) {
654
655 SSLServerSocket socket = (SSLServerSocket) ssocket;
656
657 if (enabledCiphers != null) {
658 socket.setEnabledCipherSuites(enabledCiphers);
659 }
660
661 String requestedProtocols = (String) attributes.get("protocols");
662 setEnabledProtocols(socket, getEnabledProtocols(socket,
663 requestedProtocols));
664
665 // we don't know if client auth is needed -
666 // after parsing the request we may re-handshake
667 configureClientAuth(socket);
668 }
669
670 }