001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.tomcat.util.net.jsse;
018:
019: import java.io.File;
020: import java.io.FileInputStream;
021: import java.io.FileNotFoundException;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.net.InetAddress;
025: import java.net.ServerSocket;
026: import java.net.Socket;
027: import java.net.SocketException;
028: import java.security.KeyStore;
029: import java.util.Vector;
030:
031: import javax.net.ssl.SSLException;
032: import javax.net.ssl.SSLServerSocket;
033: import javax.net.ssl.SSLServerSocketFactory;
034: import javax.net.ssl.SSLSocket;
035:
036: /*
037: 1. Make the JSSE's jars available, either as an installed
038: extension (copy them into jre/lib/ext) or by adding
039: them to the Tomcat classpath.
040: 2. keytool -genkey -alias tomcat -keyalg RSA
041: Use "changeit" as password ( this is the default we use )
042: */
043:
044: /**
045: * SSL server socket factory. It _requires_ a valid RSA key and
046: * JSSE.
047: *
048: * @author Harish Prabandham
049: * @author Costin Manolache
050: * @author Stefan Freyr Stefansson
051: * @author EKR -- renamed to JSSESocketFactory
052: */
053: public abstract class JSSESocketFactory extends
054: org.apache.tomcat.util.net.ServerSocketFactory {
055: // defaults
056: static String defaultProtocol = "TLS";
057: static String defaultAlgorithm = "SunX509";
058: static boolean defaultClientAuth = false;
059: static String defaultKeystoreType = "JKS";
060: private static final String defaultKeystoreFile = System
061: .getProperty("user.home")
062: + "/.keystore";
063: private static final String defaultKeyPass = "changeit";
064: static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
065: .getLog(JSSESocketFactory.class);
066:
067: protected boolean initialized;
068: protected String clientAuth = "false";
069: protected SSLServerSocketFactory sslProxy = null;
070: protected String[] enabledCiphers;
071:
072: public JSSESocketFactory() {
073: }
074:
075: public ServerSocket createSocket(int port) throws IOException {
076: if (!initialized)
077: init();
078: ServerSocket socket = sslProxy.createServerSocket(port);
079: initServerSocket(socket);
080: return socket;
081: }
082:
083: public ServerSocket createSocket(int port, int backlog)
084: throws IOException {
085: if (!initialized)
086: init();
087: ServerSocket socket = sslProxy
088: .createServerSocket(port, backlog);
089: initServerSocket(socket);
090: return socket;
091: }
092:
093: public ServerSocket createSocket(int port, int backlog,
094: InetAddress ifAddress) throws IOException {
095: if (!initialized)
096: init();
097: ServerSocket socket = sslProxy.createServerSocket(port,
098: backlog, ifAddress);
099: initServerSocket(socket);
100: return socket;
101: }
102:
103: public Socket acceptSocket(ServerSocket socket) throws IOException {
104: SSLSocket asock = null;
105: try {
106: asock = (SSLSocket) socket.accept();
107: configureClientAuth(asock);
108: } catch (SSLException e) {
109: throw new SocketException("SSL handshake error"
110: + e.toString());
111: }
112: return asock;
113: }
114:
115: public void handshake(Socket sock) throws IOException {
116: ((SSLSocket) sock).startHandshake();
117: }
118:
119: /*
120: * Determines the SSL cipher suites to be enabled.
121: *
122: * @param requestedCiphers Comma-separated list of requested ciphers
123: * @param supportedCiphers Array of supported ciphers
124: *
125: * @return Array of SSL cipher suites to be enabled, or null if none of the
126: * requested ciphers are supported
127: */
128: protected String[] getEnabledCiphers(String requestedCiphers,
129: String[] supportedCiphers) {
130:
131: String[] enabledCiphers = null;
132:
133: if (requestedCiphers != null) {
134: Vector vec = null;
135: String cipher = requestedCiphers;
136: int index = requestedCiphers.indexOf(',');
137: if (index != -1) {
138: int fromIndex = 0;
139: while (index != -1) {
140: cipher = requestedCiphers.substring(fromIndex,
141: index).trim();
142: if (cipher.length() > 0) {
143: /*
144: * Check to see if the requested cipher is among the
145: * supported ciphers, i.e., may be enabled
146: */
147: for (int i = 0; supportedCiphers != null
148: && i < supportedCiphers.length; i++) {
149: if (supportedCiphers[i].equals(cipher)) {
150: if (vec == null) {
151: vec = new Vector();
152: }
153: vec.addElement(cipher);
154: break;
155: }
156: }
157: }
158: fromIndex = index + 1;
159: index = requestedCiphers.indexOf(',', fromIndex);
160: } // while
161: cipher = requestedCiphers.substring(fromIndex);
162: }
163:
164: if (cipher != null) {
165: cipher = cipher.trim();
166: if (cipher.length() > 0) {
167: /*
168: * Check to see if the requested cipher is among the
169: * supported ciphers, i.e., may be enabled
170: */
171: for (int i = 0; supportedCiphers != null
172: && i < supportedCiphers.length; i++) {
173: if (supportedCiphers[i].equals(cipher)) {
174: if (vec == null) {
175: vec = new Vector();
176: }
177: vec.addElement(cipher);
178: break;
179: }
180: }
181: }
182: }
183:
184: if (vec != null) {
185: enabledCiphers = new String[vec.size()];
186: vec.copyInto(enabledCiphers);
187: }
188: }
189:
190: return enabledCiphers;
191: }
192:
193: /*
194: * Gets the SSL server's keystore password.
195: */
196: protected String getKeystorePassword() {
197: String keyPass = (String) attributes.get("keypass");
198: if (keyPass == null) {
199: keyPass = defaultKeyPass;
200: }
201: String keystorePass = (String) attributes.get("keystorePass");
202: if (keystorePass == null) {
203: keystorePass = keyPass;
204: }
205: return keystorePass;
206: }
207:
208: /*
209: * Gets the SSL server's keystore.
210: */
211: protected KeyStore getKeystore(String type, String pass)
212: throws IOException {
213:
214: String keystoreFile = (String) attributes.get("keystore");
215: if (keystoreFile == null)
216: keystoreFile = defaultKeystoreFile;
217:
218: return getStore(type, keystoreFile, pass);
219: }
220:
221: /*
222: * Gets the SSL server's truststore.
223: */
224: protected KeyStore getTrustStore(String keystoreType)
225: throws IOException {
226: KeyStore trustStore = null;
227:
228: String trustStoreFile = (String) attributes
229: .get("truststoreFile");
230: if (trustStoreFile == null) {
231: trustStoreFile = System
232: .getProperty("javax.net.ssl.trustStore");
233: }
234: if (log.isDebugEnabled()) {
235: log.debug("Truststore = " + trustStoreFile);
236: }
237: String trustStorePassword = (String) attributes
238: .get("truststorePass");
239: if (trustStorePassword == null) {
240: trustStorePassword = System
241: .getProperty("javax.net.ssl.trustStorePassword");
242: }
243: if (trustStorePassword == null) {
244: trustStorePassword = getKeystorePassword();
245: }
246: if (log.isDebugEnabled()) {
247: log.debug("TrustPass = " + trustStorePassword);
248: }
249: String truststoreType = (String) attributes
250: .get("truststoreType");
251: if (truststoreType == null) {
252: truststoreType = keystoreType;
253: }
254: if (log.isDebugEnabled()) {
255: log.debug("trustType = " + truststoreType);
256: }
257: if (trustStoreFile != null && trustStorePassword != null) {
258: trustStore = getStore(truststoreType, trustStoreFile,
259: trustStorePassword);
260: }
261:
262: return trustStore;
263: }
264:
265: /*
266: * Gets the key- or truststore with the specified type, path, and password.
267: */
268: private KeyStore getStore(String type, String path, String pass)
269: throws IOException {
270:
271: KeyStore ks = null;
272: InputStream istream = null;
273: try {
274: ks = KeyStore.getInstance(type);
275: File keyStoreFile = new File(path);
276: if (!keyStoreFile.isAbsolute()) {
277: keyStoreFile = new File(System
278: .getProperty("catalina.base"), path);
279: }
280: istream = new FileInputStream(keyStoreFile);
281:
282: ks.load(istream, pass.toCharArray());
283: istream.close();
284: istream = null;
285: } catch (FileNotFoundException fnfe) {
286: throw fnfe;
287: } catch (IOException ioe) {
288: throw ioe;
289: } catch (Exception ex) {
290: ex.printStackTrace();
291: throw new IOException("Exception trying to load keystore "
292: + path + ": " + ex.getMessage());
293: } finally {
294: if (istream != null) {
295: try {
296: istream.close();
297: } catch (IOException ioe) {
298: // Do nothing
299: }
300: }
301: }
302:
303: return ks;
304: }
305:
306: /**
307: * Reads the keystore and initializes the SSL socket factory.
308: *
309: * Place holder method to initialize the KeyStore, etc.
310: */
311: abstract void init() throws IOException;
312:
313: /*
314: * Determines the SSL protocol variants to be enabled.
315: *
316: * @param socket The socket to get supported list from.
317: * @param requestedProtocols Comma-separated list of requested SSL
318: * protocol variants
319: *
320: * @return Array of SSL protocol variants to be enabled, or null if none of
321: * the requested protocol variants are supported
322: */
323: abstract protected String[] getEnabledProtocols(
324: SSLServerSocket socket, String requestedProtocols);
325:
326: /**
327: * Set the SSL protocol variants to be enabled.
328: * @param socket the SSLServerSocket.
329: * @param protocols the protocols to use.
330: */
331: abstract protected void setEnabledProtocols(SSLServerSocket socket,
332: String[] protocols);
333:
334: /**
335: * Configure Client authentication for this version of JSSE. The
336: * JSSE included in Java 1.4 supports the 'want' value. Prior
337: * versions of JSSE will treat 'want' as 'false'.
338: * @param socket the SSLServerSocket
339: */
340: abstract protected void configureClientAuth(SSLServerSocket socket);
341:
342: /**
343: * Configure Client authentication for this version of JSSE. The
344: * JSSE included in Java 1.4 supports the 'want' value. Prior
345: * versions of JSSE will treat 'want' as 'false'.
346: * @param ssocket the SSLSocket
347: */
348: abstract protected void configureClientAuth(SSLSocket socket);
349:
350: /**
351: * Configures the given SSL server socket with the requested cipher suites,
352: * protocol versions, and need for client authentication
353: */
354: private void initServerSocket(ServerSocket ssocket) {
355:
356: SSLServerSocket socket = (SSLServerSocket) ssocket;
357:
358: if (attributes.get("ciphers") != null) {
359: socket.setEnabledCipherSuites(enabledCiphers);
360: }
361:
362: String requestedProtocols = (String) attributes
363: .get("protocols");
364: setEnabledProtocols(socket, getEnabledProtocols(socket,
365: requestedProtocols));
366:
367: // we don't know if client auth is needed -
368: // after parsing the request we may re-handshake
369: configureClientAuth(socket);
370: }
371:
372: }
|