001: /*
002: * ====================================================================
003: *
004: * The Apache Software License, Version 1.1
005: *
006: * Copyright (c) 1999 The Apache Software Foundation. All rights
007: * reserved.
008: *
009: * Redistribution and use in source and binary forms, with or without
010: * modification, are permitted provided that the following conditions
011: * are met:
012: *
013: * 1. Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * 2. Redistributions in binary form must reproduce the above copyright
017: * notice, this list of conditions and the following disclaimer in
018: * the documentation and/or other materials provided with the
019: * distribution.
020: *
021: * 3. The end-user documentation included with the redistribution, if
022: * any, must include the following acknowlegement:
023: * "This product includes software developed by the
024: * Apache Software Foundation (http://www.apache.org/)."
025: * Alternately, this acknowlegement may appear in the software itself,
026: * if and wherever such third-party acknowlegements normally appear.
027: *
028: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
029: * Foundation" must not be used to endorse or promote products derived
030: * from this software without prior written permission. For written
031: * permission, please contact apache@apache.org.
032: *
033: * 5. Products derived from this software may not be called "Apache"
034: * nor may "Apache" appear in their names without prior written
035: * permission of the Apache Group.
036: *
037: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
038: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
039: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
040: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
041: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
042: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
043: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
044: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
045: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
046: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
047: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
048: * SUCH DAMAGE.
049: * ====================================================================
050: *
051: * This software consists of voluntary contributions made by many
052: * individuals on behalf of the Apache Software Foundation. For more
053: * information on the Apache Software Foundation, please see
054: * <http://www.apache.org/>.
055: *
056: * [Additional notices, if required by prior licensing conditions]
057: *
058: */
059: package org.apache.catalina.net;
060:
061: import java.io.File;
062: import java.io.FileInputStream;
063: import java.io.IOException;
064: import java.net.InetAddress;
065: import java.net.ServerSocket;
066: import java.security.KeyStore;
067: import java.security.KeyStoreException;
068: import java.security.NoSuchAlgorithmException;
069: import java.security.UnrecoverableKeyException;
070: import java.security.KeyManagementException;
071: import java.security.Security;
072: import java.security.cert.CertificateException;
073: import javax.net.ServerSocketFactory;
074: import javax.net.ssl.SSLServerSocket;
075: import javax.net.ssl.SSLSocket;
076: import javax.net.ssl.HandshakeCompletedListener;
077: import javax.net.ssl.HandshakeCompletedEvent;
078:
079: import com.sun.net.ssl.KeyManagerFactory;
080: import com.sun.net.ssl.SSLContext;
081: import com.sun.net.ssl.TrustManagerFactory;
082:
083: /**
084: * Socket factory for SSL sockets, using the Java Server Sockets Extension
085: * (JSSE) reference implementation support classes. Besides the usual
086: * configuration mechanism based on setting JavaBeans properties, this
087: * component may also be configured by passing a series of attributes set
088: * with calls to <code>setAttribute()</code>. The following attribute
089: * names are recognized, with default values in square brackets:
090: * <ul>
091: * <li><strong>algorithm</strong> - Certificate encoding algorithm
092: * to use. [SunX509]</li>
093: * <li><strong>clientAuth</strong> - Require client authentication if
094: * set to <code>true</code>. [false]</li>
095: * <li><strong>keystoreFile</strong> - Pathname to the Key Store file to be
096: * loaded. This must be an absolute path, or a relative path that
097: * is resolved against the "catalina.base" system property.
098: * ["./keystore" in the user home directory]</li>
099: * <li><strong>keystorePass</strong> - Password for the Key Store file to be
100: * loaded. ["changeit"]</li>
101: * <li><strong>keystoreType</strong> - Type of the Key Store file to be
102: * loaded. ["JKS"]</li>
103: * <li><strong>protocol</strong> - SSL protocol to use. [TLS]</li>
104: * </ul>
105: *
106: * @author Harish Prabandham
107: * @author Costin Manolache
108: * @author Craig McClanahan
109: */
110:
111: public class SSLServerSocketFactory implements
112: org.apache.catalina.net.ServerSocketFactory {
113:
114: // ----------------------------------------------------- Instance Variables
115:
116: /**
117: * The name of our protocol handler package for the "https:" protocol.
118: */
119: private static final String PROTOCOL_HANDLER = "com.sun.net.ssl.internal.www.protocol";
120:
121: /**
122: * The name of the system property containing a "|" delimited list of
123: * protocol handler packages.
124: */
125: private static final String PROTOCOL_PACKAGES = "java.protocol.handler.pkgs";
126:
127: /**
128: * The configured socket factory.
129: */
130: private javax.net.ssl.SSLServerSocketFactory sslProxy = null;
131:
132: /**
133: * The trust manager factory used with JSSE 1.0.1.
134: */
135: // TrustManagerFactory trustManagerFactory = null;
136:
137: // ------------------------------------------------------------- Properties
138:
139: /**
140: * Certificate encoding algorithm to be used.
141: */
142: private String algorithm = "SunX509";
143:
144: public String getAlgorithm() {
145: return (this .algorithm);
146: }
147:
148: public void setAlgorithm(String algorithm) {
149: this .algorithm = algorithm;
150: }
151:
152: /**
153: * Should we require client authentication?
154: */
155: private boolean clientAuth = false;
156:
157: public boolean getClientAuth() {
158: return (this .clientAuth);
159: }
160:
161: public void setClientAuth(boolean clientAuth) {
162: this .clientAuth = clientAuth;
163: }
164:
165: /**
166: * The internal represenation of the key store file that contains
167: * our server certificate.
168: */
169: private KeyStore keyStore = null;
170:
171: public KeyStore getKeyStore() throws IOException,
172: KeyStoreException, NoSuchAlgorithmException,
173: CertificateException, UnrecoverableKeyException,
174: KeyManagementException {
175: if (sslProxy == null)
176: initialize();
177: return (this .keyStore);
178: }
179:
180: /**
181: * Pathname to the key store file to be used.
182: */
183: private String keystoreFile = System.getProperty("user.home")
184: + File.separator + ".keystore";
185:
186: public String getKeystoreFile() {
187: return (this .keystoreFile);
188: }
189:
190: public void setKeystoreFile(String keystoreFile) {
191: File file = new File(keystoreFile);
192: if (!file.isAbsolute())
193: file = new File(System.getProperty("catalina.base"),
194: keystoreFile);
195: this .keystoreFile = file.getAbsolutePath();
196: }
197:
198: /**
199: * Password for accessing the key store file.
200: */
201: private String keystorePass = "changeit";
202:
203: public void setKeystorePass(String keystorePass) {
204: this .keystorePass = keystorePass;
205: }
206:
207: /**
208: * Storeage type of the key store file to be used.
209: */
210: private String keystoreType = "JKS";
211:
212: public String getKeystoreType() {
213: return (this .keystoreType);
214: }
215:
216: public void setKeystoreType(String keystoreType) {
217: this .keystoreType = keystoreType;
218: }
219:
220: /**
221: * SSL protocol variant to use.
222: */
223: private String protocol = "TLS";
224:
225: public String getProtocol() {
226: return (this .protocol);
227: }
228:
229: public void setProtocol(String protocol) {
230: this .protocol = protocol;
231: }
232:
233: // --------------------------------------------------------- Public Methods
234:
235: /**
236: * Return a server socket that uses all network interfaces on the host,
237: * and is bound to a specified port. The socket is configured with the
238: * socket options (such as accept timeout) given to this factory.
239: *
240: * @param port Port to listen to
241: *
242: * @exception IOException input/output or network error
243: * @exception KeyStoreException error instantiating the
244: * KeyStore from file
245: * @exception NoSuchAlgorithmException KeyStore algorithm unsupported
246: * by current provider
247: * @exception CertificateException general certificate error
248: * @exception UnrecoverableKeyException internal KeyStore problem with
249: * the certificate
250: * @exception KeyManagementException problem in the key management
251: * layer
252: */
253: public ServerSocket createSocket(int port) throws IOException,
254: KeyStoreException, NoSuchAlgorithmException,
255: CertificateException, UnrecoverableKeyException,
256: KeyManagementException {
257:
258: if (sslProxy == null)
259: initialize();
260: ServerSocket socket = sslProxy.createServerSocket(port);
261: initServerSocket(socket);
262: return (socket);
263:
264: }
265:
266: /**
267: * Return a server socket that uses all network interfaces on the host,
268: * and is bound to a specified port, and uses the specified
269: * connection backlog. The socket is configured with the
270: * socket options (such as accept timeout) given to this factory.
271: *
272: * @param port Port to listen to
273: * @param backlog Maximum number of connections to be queued
274: *
275: * @exception IOException input/output or network error
276: * @exception KeyStoreException error instantiating the
277: * KeyStore from file
278: * @exception NoSuchAlgorithmException KeyStore algorithm unsupported
279: * by current provider
280: * @exception CertificateException general certificate error
281: * @exception UnrecoverableKeyException internal KeyStore problem with
282: * the certificate
283: * @exception KeyManagementException problem in the key management
284: * layer
285: */
286: public ServerSocket createSocket(int port, int backlog)
287: throws IOException, KeyStoreException,
288: NoSuchAlgorithmException, CertificateException,
289: UnrecoverableKeyException, KeyManagementException {
290:
291: if (sslProxy == null)
292: initialize();
293: ServerSocket socket = sslProxy
294: .createServerSocket(port, backlog);
295: initServerSocket(socket);
296: return (socket);
297:
298: }
299:
300: /**
301: * Return a server socket that uses the specified interface on the host,
302: * and is bound to a specified port, and uses the specified
303: * connection backlog. The socket is configured with the
304: * socket options (such as accept timeout) given to this factory.
305: *
306: * @param port Port to listen to
307: * @param backlog Maximum number of connections to be queued
308: * @param ifAddress Address of the interface to be used
309: *
310: * @exception IOException input/output or network error
311: * @exception KeyStoreException error instantiating the
312: * KeyStore from file
313: * @exception NoSuchAlgorithmException KeyStore algorithm unsupported
314: * by current provider
315: * @exception CertificateException general certificate error
316: * @exception UnrecoverableKeyException internal KeyStore problem with
317: * the certificate
318: * @exception KeyManagementException problem in the key management
319: * layer
320: */
321: public ServerSocket createSocket(int port, int backlog,
322: InetAddress ifAddress) throws IOException,
323: KeyStoreException, NoSuchAlgorithmException,
324: CertificateException, UnrecoverableKeyException,
325: KeyManagementException {
326:
327: if (sslProxy == null)
328: initialize();
329: ServerSocket socket = sslProxy.createServerSocket(port,
330: backlog, ifAddress);
331: initServerSocket(socket);
332: return (socket);
333:
334: }
335:
336: // -------------------------------------------------------- Private Methods
337:
338: /**
339: * Initialize objects that will be required to create sockets.
340: *
341: * @exception IOException input/output or network error
342: * @exception KeyStoreException error instantiating the
343: * KeyStore from file
344: * @exception NoSuchAlgorithmException KeyStore algorithm unsupported
345: * by current provider
346: * @exception CertificateException general certificate error
347: * @exception UnrecoverableKeyException internal KeyStore problem with
348: * the certificate
349: * @exception KeyManagementException problem in the key management
350: * layer
351: */
352: private synchronized void initialize() throws IOException,
353: KeyStoreException, NoSuchAlgorithmException,
354: CertificateException, UnrecoverableKeyException,
355: KeyManagementException {
356:
357: initHandler();
358: initKeyStore();
359: initProxy();
360:
361: }
362:
363: /**
364: * Register our URLStreamHandler for the "https:" protocol.
365: */
366: private void initHandler() {
367:
368: String packages = System.getProperty(PROTOCOL_PACKAGES);
369: if (packages == null)
370: packages = PROTOCOL_HANDLER;
371: else if (packages.indexOf(PROTOCOL_HANDLER) < 0)
372: packages += "|" + PROTOCOL_HANDLER;
373: System.setProperty(PROTOCOL_PACKAGES, packages);
374:
375: }
376:
377: /**
378: * Initialize the internal representation of the key store file.
379: *
380: * @exception IOException input/output or network error
381: * @exception KeyStoreException error instantiating the
382: * KeyStore from file
383: * @exception NoSuchAlgorithmException KeyStore algorithm unsupported
384: * by current provider
385: * @exception CertificateException general certificate error
386: */
387: private void initKeyStore() throws IOException, KeyStoreException,
388: NoSuchAlgorithmException, CertificateException {
389:
390: FileInputStream istream = null;
391:
392: try {
393: keyStore = KeyStore.getInstance(keystoreType);
394: istream = new FileInputStream(keystoreFile);
395: keyStore.load(istream, keystorePass.toCharArray());
396: } catch (IOException ioe) {
397: throw ioe;
398: } catch (KeyStoreException kse) {
399: throw kse;
400: } catch (NoSuchAlgorithmException nsae) {
401: throw nsae;
402: } catch (CertificateException ce) {
403: throw ce;
404: } finally {
405: if (istream != null)
406: istream.close();
407: }
408:
409: }
410:
411: /**
412: * Initialize the SSL socket factory.
413: *
414: * @exception KeyStoreException error instantiating the
415: * KeyStore from file
416: * @exception NoSuchAlgorithmException KeyStore algorithm unsupported
417: * by current provider
418: * @exception UnrecoverableKeyException internal KeyStore problem with
419: * the certificate
420: * @exception KeyManagementException problem in the key management
421: * layer
422: */
423: private void initProxy() throws KeyStoreException,
424: NoSuchAlgorithmException, UnrecoverableKeyException,
425: KeyManagementException {
426:
427: // Register the JSSE security Provider (if it is not already there)
428: try {
429: Security.addProvider((java.security.Provider) Class
430: .forName("com.sun.net.ssl.internal.ssl.Provider")
431: .newInstance());
432: } catch (Throwable t) {
433: ;
434: }
435:
436: // Create an SSL context used to create an SSL socket factory
437: SSLContext context = SSLContext.getInstance(protocol);
438:
439: // Create the key manager factory used to extract the server key
440: KeyManagerFactory keyManagerFactory = KeyManagerFactory
441: .getInstance(algorithm);
442: keyManagerFactory.init(keyStore, keystorePass.toCharArray());
443:
444: // Create the trust manager factory used for checking certificates
445: /*
446: trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
447: trustManagerFactory.init(keyStore);
448: */
449:
450: // Initialize the context with the key managers
451: context.init(keyManagerFactory.getKeyManagers(), null,
452: new java.security.SecureRandom());
453:
454: // Create the proxy and return
455: sslProxy = context.getServerSocketFactory();
456:
457: }
458:
459: /**
460: * Set the requested properties for this server socket.
461: *
462: * @param ssocket The server socket to be configured
463: */
464: private void initServerSocket(ServerSocket ssocket) {
465:
466: SSLServerSocket socket = (SSLServerSocket) ssocket;
467:
468: // Enable all available cipher suites when the socket is connected
469: String cipherSuites[] = socket.getSupportedCipherSuites();
470: socket.setEnabledCipherSuites(cipherSuites);
471:
472: // Set client authentication if necessary
473: socket.setNeedClientAuth(clientAuth);
474:
475: }
476:
477: }
|