001: /*
002: * $Id: TlsConfiguration.java 10489 2008-01-23 17:53:38Z dfeist $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.api.security.tls;
012:
013: import org.mule.api.lifecycle.CreateException;
014: import org.mule.api.security.TlsDirectKeyStore;
015: import org.mule.api.security.TlsDirectTrustStore;
016: import org.mule.api.security.TlsIndirectKeyStore;
017: import org.mule.api.security.TlsProtocolHandler;
018: import org.mule.api.security.provider.AutoDiscoverySecurityProviderFactory;
019: import org.mule.api.security.provider.SecurityProviderFactory;
020: import org.mule.api.security.provider.SecurityProviderInfo;
021: import org.mule.config.i18n.CoreMessages;
022: import org.mule.util.FileUtils;
023: import org.mule.util.IOUtils;
024:
025: import java.io.FileNotFoundException;
026: import java.io.IOException;
027: import java.io.InputStream;
028: import java.security.KeyManagementException;
029: import java.security.KeyStore;
030: import java.security.NoSuchAlgorithmException;
031: import java.security.Provider;
032: import java.security.Security;
033:
034: import javax.net.ssl.KeyManager;
035: import javax.net.ssl.KeyManagerFactory;
036: import javax.net.ssl.SSLContext;
037: import javax.net.ssl.SSLServerSocketFactory;
038: import javax.net.ssl.SSLSocketFactory;
039: import javax.net.ssl.TrustManager;
040: import javax.net.ssl.TrustManagerFactory;
041:
042: import org.apache.commons.logging.Log;
043: import org.apache.commons.logging.LogFactory;
044:
045: /**
046: * Support for configuring TLS/SSL connections.
047: * <p/>
048: * <h2>Introduction</h2>
049: * <p/>
050: * This class was introduced to centralise the work of TLS/SSL configuration. It is intended
051: * to be backwards compatible with earlier code (as much as possible) and so is perhaps more
052: * complex than would be necessary if starting from zero - the main source of confusion is the
053: * distinction between direct and indirect creation of sockets and stores.
054: * <p/>
055: * <h2>Configuration</h2>
056: * <p/>
057: * The documentation in this class is intended more for programmers than end uses. If you are
058: * configuring a connector the interfaces {@link org.mule.api.security.TlsIndirectTrustStore},
059: * {@link TlsDirectTrustStore},
060: * {@link TlsDirectKeyStore} and {@link TlsIndirectKeyStore} should provide guidance to individual
061: * properties. In addition you should check the documentation for the specific protocol / connector
062: * used and may also need to read the discussion on direct and indirect socket and store creation
063: * below (or, more simply, just use whichever key store interface your connector implements!).
064: * <p/>
065: * <h2>Programming</h2>
066: * <p/>
067: * This class is intended to be used as a delegate as we typically want to add security to an
068: * already existing connector (so we inherit from that connector, implement the appropriate
069: * interfaces from {@link org.mule.api.security.TlsIndirectTrustStore}, {@link TlsDirectTrustStore},
070: * {@link TlsDirectKeyStore} and {@link TlsIndirectKeyStore}, and then forward calls to the
071: * interfaces to an instance of this class).
072: * <p/>
073: * <p>For setting System properties (and reading them) use {@link TlsPropertiesMapper}. This
074: * can take a "namespace" which can then be used by {@link TlsPropertiesSocketFactory} to
075: * construct an appropriate socket factory. This approach (storing to proeprties and then
076: * retrieving that information later in a socket factory) lets us pass TLS/SSL configuration
077: * into libraries that are configured by specifying on the socket factory class.</p>
078: * <p/>
079: * <h2>Direct and Indirect Socket and Store Creation</h2>
080: * <p/>
081: * For the SSL transport, which historically defined parameters for many different secure
082: * transports, the configuration interfaces worked as follows:
083: * <p/>
084: * <dl>
085: * <dt>{@link TlsDirectTrustStore}</dt><dd>Used to generate trust store directly and indirectly
086: * for all TLS/SSL conections via System properties</dd>
087: * <dt>{@link TlsDirectKeyStore}</dt><dd>Used to generate key store directly</dd>
088: * <dt>{@link TlsIndirectKeyStore}</dt><dd>Used to generate key store indirectly for all
089: * TLS/SSL conections via System properties</dd>
090: * </dl>
091: * <p/>
092: * Historically, many other transports relied on the indirect configurations defined above.
093: * So they implemented {@link org.mule.api.security.TlsIndirectTrustStore}
094: * (a superclass of {@link TlsDirectTrustStore})
095: * and relied on {@link TlsIndirectKeyStore} from the SSL configuration. For continuity these
096: * interfaces continue to be used, even though
097: * the configurations are now typically (see individual connector/protocol documentation) specific
098: * to a protocol or connector. <em>Note - these interfaces are new, but the original code had
099: * those methods, used as described. The new interfaces only make things explicit.</em>
100: * <p/>
101: * <p><em>Note for programmers</em> One way to understand the above is to see that many
102: * protocols are handled by libraries that are configured by providing either properties or
103: * a socket factory. In both cases (the latter via {@link TlsPropertiesSocketFactory}) we
104: * continue to use properties and the "indirect" interface. Note also that the mapping
105: * in {@link TlsPropertiesMapper} correctly handles the asymmetry, so an initial call to
106: * {@link TlsConfiguration} uses the keystore defined via {@link TlsDirectKeyStore}, but
107: * when a {@link TlsConfiguration} is retrieved from System proerties using
108: * {@link TlsPropertiesMapper#readFromProperties(TlsConfiguration,java.util.Properties)}
109: * the "indirect" properties are supplied as "direct" values, meaning that the "indirect"
110: * socket factory can be retrieved from {@link #getKeyManagerFactory()}. It just works.</p>
111: */
112: public final class TlsConfiguration implements TlsDirectTrustStore,
113: TlsDirectKeyStore, TlsIndirectKeyStore, TlsProtocolHandler {
114:
115: public static final String DEFAULT_KEYSTORE = ".keystore";
116: public static final String DEFAULT_KEYSTORE_TYPE = KeyStore
117: .getDefaultType();
118: public static final String DEFAULT_SSL_TYPE = "SSLv3";
119: public static final String JSSE_NAMESPACE = "javax.net";
120:
121: private Log logger = LogFactory.getLog(getClass());
122:
123: private SecurityProviderFactory spFactory = new AutoDiscoverySecurityProviderFactory();
124: private SecurityProviderInfo spInfo = spFactory
125: .getSecurityProviderInfo();
126: private Provider provider = spFactory.getProvider();
127: private String sslType = DEFAULT_SSL_TYPE;
128:
129: // global
130: private String protocolHandler = spInfo.getProtocolHandler();
131:
132: // this is the key store that is generated in-memory and available to connectors explicitly.
133: // it is local to the socket.
134: private String keyStoreName = DEFAULT_KEYSTORE; // was default in https but not ssl
135: private String keyPassword = null;
136: private String keyStorePassword = null;
137: private String keystoreType = DEFAULT_KEYSTORE_TYPE;
138: private String keyManagerAlgorithm = spInfo
139: .getKeyManagerAlgorithm();
140: private KeyManagerFactory keyManagerFactory = null;
141:
142: // this is the key store defined in system properties that is used implicitly.
143: // note that some transports use different namespaces within system properties,
144: // so this is typically global across a particular transport.
145: // it is also used as the trust store defined in system properties if no other trust
146: // store is given and explicitTrustStoreOnly is false
147: private String clientKeyStoreName = null;
148: private String clientKeyStorePassword = null;
149: private String clientKeyStoreType = DEFAULT_KEYSTORE_TYPE;
150:
151: // this is the trust store used to construct sockets both explicitly
152: // and globally (if not set, see client key above) via the jvm defaults.
153: private String trustStoreName = null;
154: private String trustStorePassword = null;
155: private String trustStoreType = DEFAULT_KEYSTORE_TYPE;
156: private String trustManagerAlgorithm = spInfo
157: .getKeyManagerAlgorithm();
158: private TrustManagerFactory trustManagerFactory = null;
159: private boolean explicitTrustStoreOnly = false;
160: private boolean requireClientAuthentication = false;
161:
162: /**
163: * Support for TLS connections with a given initial value for the key store
164: *
165: * @param keyStore initial value for the key store
166: */
167: public TlsConfiguration(String keyStore) {
168: this .keyStoreName = keyStore;
169: }
170:
171: // note - in what follows i'm using "raw" variables rather than accessors because
172: // i think the names are clearer. the API names for the accessors are historical
173: // and not a close fit to actual use (imho).
174:
175: /**
176: * @param anon If the connection is anonymous then we don't care about client keys
177: * @param namespace Namespace to use for global properties (for JSSE use JSSE_NAMESPACE)
178: * @throws CreateException ON initialisation problems
179: */
180: public void initialise(boolean anon, String namespace)
181: throws CreateException {
182: if (logger.isDebugEnabled()) {
183: logger.debug("initialising: anon " + anon);
184: }
185: validate(anon);
186:
187: Security.addProvider(provider);
188: System.setProperty("java.protocol.handler.pkgs",
189: protocolHandler);
190:
191: if (!anon) {
192: initKeyManagerFactory();
193: }
194: initTrustManagerFactory();
195:
196: if (null != namespace) {
197: new TlsPropertiesMapper(namespace).writeToProperties(System
198: .getProperties(), this );
199: }
200: }
201:
202: private void validate(boolean anon) throws CreateException {
203: assertNotNull(getProvider(),
204: "The security provider cannot be null");
205: if (!anon) {
206: assertNotNull(getKeyStore(),
207: "The KeyStore location cannot be null");
208: assertNotNull(getKeyPassword(),
209: "The Key password cannot be null");
210: assertNotNull(getKeyStorePassword(),
211: "The KeyStore password cannot be null");
212: assertNotNull(getKeyManagerAlgorithm(),
213: "The Key Manager Algorithm cannot be null");
214: }
215: }
216:
217: private void initKeyManagerFactory() throws CreateException {
218: if (logger.isDebugEnabled()) {
219: logger
220: .debug("initialising key manager factory from keystore data");
221: }
222: KeyStore tempKeyStore;
223: try {
224: tempKeyStore = KeyStore.getInstance(keystoreType);
225: InputStream is = IOUtils.getResourceAsStream(keyStoreName,
226: getClass());
227: if (null == is) {
228: throw new FileNotFoundException(CoreMessages
229: .cannotLoadFromClasspath(
230: "Keystore: " + keyStoreName)
231: .getMessage());
232: }
233: tempKeyStore.load(is, keyStorePassword.toCharArray());
234: } catch (Exception e) {
235: throw new CreateException(CoreMessages
236: .failedToLoad("KeyStore: " + keyStoreName), e, this );
237: }
238: try {
239: keyManagerFactory = KeyManagerFactory
240: .getInstance(getKeyManagerAlgorithm());
241: keyManagerFactory.init(tempKeyStore, keyPassword
242: .toCharArray());
243: } catch (Exception e) {
244: throw new CreateException(CoreMessages
245: .failedToLoad("Key Manager"), e, this );
246: }
247: }
248:
249: private void initTrustManagerFactory() throws CreateException {
250: if (null != trustStoreName) {
251: trustStorePassword = null == trustStorePassword ? ""
252: : trustStorePassword;
253:
254: KeyStore trustStore;
255: try {
256: trustStore = KeyStore.getInstance(trustStoreType);
257: InputStream is = IOUtils.getResourceAsStream(
258: trustStoreName, getClass());
259: if (null == is) {
260: throw new FileNotFoundException(
261: "Failed to load truststore from classpath or local file: "
262: + trustStoreName);
263: }
264: trustStore.load(is, trustStorePassword.toCharArray());
265: } catch (Exception e) {
266: throw new CreateException(CoreMessages
267: .failedToLoad("TrustStore: " + trustStoreName),
268: e, this );
269: }
270:
271: try {
272: trustManagerFactory = TrustManagerFactory
273: .getInstance(trustManagerAlgorithm);
274: trustManagerFactory.init(trustStore);
275: } catch (Exception e) {
276: throw new CreateException(CoreMessages
277: .failedToLoad("Trust Manager ("
278: + trustManagerAlgorithm + ")"), e, this );
279: }
280: }
281: }
282:
283: private static void assertNotNull(Object value, String message) {
284: if (null == value) {
285: throw new IllegalArgumentException(message);
286: }
287: }
288:
289: private static String defaultForNull(String value, String deflt) {
290: if (null == value) {
291: return deflt;
292: } else {
293: return value;
294: }
295: }
296:
297: public SSLSocketFactory getSocketFactory()
298: throws NoSuchAlgorithmException, KeyManagementException {
299: return getSslContext().getSocketFactory();
300: }
301:
302: public SSLServerSocketFactory getServerSocketFactory()
303: throws NoSuchAlgorithmException, KeyManagementException {
304: return getSslContext().getServerSocketFactory();
305: }
306:
307: public SSLContext getSslContext() throws NoSuchAlgorithmException,
308: KeyManagementException {
309: KeyManager[] keyManagers = null == getKeyManagerFactory() ? null
310: : getKeyManagerFactory().getKeyManagers();
311: TrustManager[] trustManagers = null == getTrustManagerFactory() ? null
312: : getTrustManagerFactory().getTrustManagers();
313:
314: SSLContext context = SSLContext.getInstance(getSslType());
315: // TODO - nice to have a configurable random number source set here
316: context.init(keyManagers, trustManagers, null);
317: return context;
318: }
319:
320: public String getSslType() {
321: return sslType;
322: }
323:
324: public void setSslType(String sslType) {
325: this .sslType = sslType;
326: }
327:
328: public Provider getProvider() {
329: return provider;
330: }
331:
332: public void setProvider(Provider provider) {
333: this .provider = provider;
334: }
335:
336: public String getProtocolHandler() {
337: return protocolHandler;
338: }
339:
340: public void setProtocolHandler(String protocolHandler) {
341: this .protocolHandler = protocolHandler;
342: }
343:
344: public SecurityProviderFactory getSecurityProviderFactory() {
345: return spFactory;
346: }
347:
348: public void setSecurityProviderFactory(
349: SecurityProviderFactory spFactory) {
350: this .spFactory = spFactory;
351: }
352:
353: // access to the explicit key store variables
354:
355: public String getKeyStore() {
356: return keyStoreName;
357: }
358:
359: public void setKeyStore(String name) throws IOException {
360: keyStoreName = name;
361: if (null != keyStoreName) {
362: keyStoreName = FileUtils.getResourcePath(keyStoreName,
363: getClass());
364: if (logger.isDebugEnabled()) {
365: logger.debug("Normalised keyStore path to: "
366: + keyStoreName);
367: }
368: }
369: }
370:
371: public String getKeyPassword() {
372: return keyPassword;
373: }
374:
375: public void setKeyPassword(String keyPassword) {
376: this .keyPassword = keyPassword;
377: }
378:
379: public String getKeyStorePassword() {
380: return keyStorePassword;
381: }
382:
383: public void setKeyStorePassword(String storePassword) {
384: this .keyStorePassword = storePassword;
385: }
386:
387: public String getKeyStoreType() {
388: return keystoreType;
389: }
390:
391: public void setKeyStoreType(String keystoreType) {
392: this .keystoreType = keystoreType;
393: }
394:
395: public String getKeyManagerAlgorithm() {
396: return keyManagerAlgorithm;
397: }
398:
399: public void setKeyManagerAlgorithm(String keyManagerAlgorithm) {
400: this .keyManagerAlgorithm = keyManagerAlgorithm;
401: }
402:
403: public KeyManagerFactory getKeyManagerFactory() {
404: return keyManagerFactory;
405: }
406:
407: // access to the implicit key store variables
408:
409: public String getClientKeyStore() {
410: return clientKeyStoreName;
411: }
412:
413: public void setClientKeyStore(String name) throws IOException {
414: clientKeyStoreName = name;
415: if (null != clientKeyStoreName) {
416: clientKeyStoreName = FileUtils.getResourcePath(
417: clientKeyStoreName, getClass());
418: if (logger.isDebugEnabled()) {
419: logger.debug("Normalised clientKeyStore path to: "
420: + clientKeyStoreName);
421: }
422: }
423: }
424:
425: public String getClientKeyStorePassword() {
426: return clientKeyStorePassword;
427: }
428:
429: public void setClientKeyStorePassword(String clientKeyStorePassword) {
430: this .clientKeyStorePassword = clientKeyStorePassword;
431: }
432:
433: public void setClientKeyStoreType(String clientKeyStoreType) {
434: this .clientKeyStoreType = clientKeyStoreType;
435: }
436:
437: public String getClientKeyStoreType() {
438: return clientKeyStoreType;
439: }
440:
441: // access to trust store variables
442:
443: public String getTrustStore() {
444: return trustStoreName;
445: }
446:
447: public void setTrustStore(String name) throws IOException {
448: trustStoreName = name;
449: if (null != trustStoreName) {
450: trustStoreName = FileUtils.getResourcePath(trustStoreName,
451: getClass());
452: if (logger.isDebugEnabled()) {
453: logger.debug("Normalised trustStore path to: "
454: + trustStoreName);
455: }
456: }
457: }
458:
459: public String getTrustStorePassword() {
460: return trustStorePassword;
461: }
462:
463: public void setTrustStorePassword(String trustStorePassword) {
464: this .trustStorePassword = trustStorePassword;
465: }
466:
467: public String getTrustStoreType() {
468: return trustStoreType;
469: }
470:
471: public void setTrustStoreType(String trustStoreType) {
472: this .trustStoreType = trustStoreType;
473: }
474:
475: public String getTrustManagerAlgorithm() {
476: return trustManagerAlgorithm;
477: }
478:
479: public void setTrustManagerAlgorithm(String trustManagerAlgorithm) {
480: this .trustManagerAlgorithm = defaultForNull(
481: trustManagerAlgorithm, spInfo.getKeyManagerAlgorithm());
482: }
483:
484: public TrustManagerFactory getTrustManagerFactory() {
485: return trustManagerFactory;
486: }
487:
488: public void setTrustManagerFactory(
489: TrustManagerFactory trustManagerFactory) {
490: this .trustManagerFactory = trustManagerFactory;
491: }
492:
493: public boolean isExplicitTrustStoreOnly() {
494: return explicitTrustStoreOnly;
495: }
496:
497: public void setExplicitTrustStoreOnly(boolean explicitTrustStoreOnly) {
498: this .explicitTrustStoreOnly = explicitTrustStoreOnly;
499: }
500:
501: public boolean isRequireClientAuthentication() {
502: return requireClientAuthentication;
503: }
504:
505: public void setRequireClientAuthentication(
506: boolean requireClientAuthentication) {
507: this.requireClientAuthentication = requireClientAuthentication;
508: }
509:
510: }
|