/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.module.tls.internal;
import static java.util.Arrays.copyOf;
import org.mule.runtime.api.i18n.I18nMessageFactory;
import org.mule.runtime.api.lifecycle.Initialisable;
import org.mule.runtime.api.lifecycle.InitialisationException;
import org.mule.runtime.api.tls.TlsContextFactory;
import org.mule.runtime.api.tls.TlsContextKeyStoreConfiguration;
import org.mule.runtime.api.tls.TlsContextTrustStoreConfiguration;
import org.mule.runtime.api.lifecycle.CreateException;
import org.mule.runtime.core.api.security.tls.RestrictedSSLServerSocketFactory;
import org.mule.runtime.core.api.security.tls.RestrictedSSLSocketFactory;
import org.mule.runtime.core.api.security.tls.TlsConfiguration;
import org.mule.runtime.core.util.ArrayUtils;
import org.mule.runtime.core.util.FileUtils;
import org.mule.runtime.core.util.StringUtils;
import com.google.common.base.Joiner;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of the {@code TlsContextFactory} interface, which delegates all its operations to a
* {@code TlsConfiguration} object. Only enabled cipher suites and protocols will not delegate to it if configured.
*/
public class DefaultTlsContextFactory implements TlsContextFactory, Initialisable {
private static final Logger logger = LoggerFactory.getLogger(DefaultTlsContextFactory.class);
private static final String DEFAULT = "default";
private String name;
private TlsConfiguration tlsConfiguration = new TlsConfiguration(null);
private AtomicBoolean initialized = new AtomicBoolean(false);
private boolean trustStoreInsecure = false;
private String[] enabledProtocols;
private String[] enabledCipherSuites;
@Override
public void initialise() throws InitialisationException {
if (initialized.getAndSet(true)) {
return;
}
try {
tlsConfiguration.initialise(null == getKeyStorePath(), null);
} catch (CreateException e) {
throw new InitialisationException(I18nMessageFactory.createStaticMessage("Unable to initialise TLS configuration"), e,
this);
}
if (!isUseDefaults(enabledProtocols)) {
String[] globalEnabledProtocols = tlsConfiguration.getEnabledProtocols();
if (globalEnabledProtocols != null) {
String[] validProtocols = ArrayUtils.intersection(enabledProtocols, globalEnabledProtocols);
if (validProtocols.length < enabledProtocols.length) {
globalConfigNotHonored("protocols", globalEnabledProtocols);
}
}
}
if (!isUseDefaults(enabledCipherSuites)) {
String[] globalEnabledCipherSuites = tlsConfiguration.getEnabledCipherSuites();
if (globalEnabledCipherSuites != null) {
String[] validCipherSuites = ArrayUtils.intersection(enabledCipherSuites, globalEnabledCipherSuites);
if (validCipherSuites.length < enabledCipherSuites.length) {
globalConfigNotHonored("cipher suites", globalEnabledCipherSuites);
}
}
}
}
private boolean isUseDefaults(String[] array) {
return (array == null) || ((array.length == 1) && DEFAULT.equalsIgnoreCase(array[0]));
}
private void globalConfigNotHonored(String element, String[] elementArray) throws InitialisationException {
throw new InitialisationException(I18nMessageFactory.createStaticMessage(String
.format("Some selected %1$s are invalid. Valid %1$s according to your TLS configuration file are: %2$s", element,
Joiner.on(", ").join(elementArray))), this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getKeyStorePath() {
return tlsConfiguration.getKeyStore();
}
public void setKeyStorePath(String name) throws IOException {
tlsConfiguration.setKeyStore(name);
}
public String getKeyStoreType() {
return tlsConfiguration.getKeyStoreType();
}
public void setKeyStoreType(String keyStoreType) {
tlsConfiguration.setKeyStoreType(keyStoreType);
}
public String getKeyAlias() {
return tlsConfiguration.getKeyAlias();
}
public void setKeyAlias(String keyAlias) {
tlsConfiguration.setKeyAlias(keyAlias);
}
public String getKeyStorePassword() {
return tlsConfiguration.getKeyStorePassword();
}
public void setKeyStorePassword(String storePassword) {
tlsConfiguration.setKeyStorePassword(storePassword);
}
public String getKeyPassword() {
return tlsConfiguration.getKeyPassword();
}
public void setKeyPassword(String keyManagerPassword) {
tlsConfiguration.setKeyPassword(keyManagerPassword);
}
public String getKeyManagerAlgorithm() {
return tlsConfiguration.getKeyManagerAlgorithm();
}
public void setKeyManagerAlgorithm(String keyManagerAlgorithm) {
tlsConfiguration.setKeyManagerAlgorithm(keyManagerAlgorithm);
}
public String getTrustStorePath() {
return tlsConfiguration.getTrustStore();
}
public void setTrustStorePath(String trustStorePath) throws IOException {
String trustStoreResource = FileUtils.getResourcePath(trustStorePath, getClass());
if (trustStoreResource == null) {
throw new IOException(String.format("Resource %s could not be found", trustStorePath));
}
tlsConfiguration.setTrustStore(trustStorePath);
}
public String getTrustStoreType() {
return tlsConfiguration.getTrustStoreType();
}
public void setTrustStoreType(String trustStoreType) {
tlsConfiguration.setTrustStoreType(trustStoreType);
}
public String getTrustStorePassword() {
return tlsConfiguration.getTrustStorePassword();
}
public void setTrustStorePassword(String trustStorePassword) {
tlsConfiguration.setTrustStorePassword(trustStorePassword);
}
public String getTrustManagerAlgorithm() {
return tlsConfiguration.getTrustManagerAlgorithm();
}
public void setTrustManagerAlgorithm(String trustManagerAlgorithm) {
tlsConfiguration.setTrustManagerAlgorithm(trustManagerAlgorithm);
}
public boolean isTrustStoreInsecure() {
return trustStoreInsecure;
}
public void setTrustStoreInsecure(boolean insecure) {
if (insecure) {
logger.warn(String.format(
"TLS context %s trust store set as insecure. No certificate validations will be performed, rendering connections vulnerable to attacks. Use at own risk.",
name == null ? StringUtils.EMPTY : name));
}
this.trustStoreInsecure = insecure;
}
@Override
public SSLContext createSslContext() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext sslContext;
if (trustStoreInsecure) {
sslContext = tlsConfiguration.getSslContext(new TrustManager[] {new InsecureTrustManager()});
} else {
sslContext = tlsConfiguration.getSslContext();
}
return sslContext;
}
@Override
public SSLSocketFactory getSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
return new RestrictedSSLSocketFactory(createSslContext(), getEnabledCipherSuites(), getEnabledProtocols());
}
@Override
public SSLServerSocketFactory getServerSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
return new RestrictedSSLServerSocketFactory(createSslContext(), getEnabledCipherSuites(), getEnabledProtocols());
}
@Override
public String[] getEnabledCipherSuites() {
String[] enabledCipherSuites;
if (isUseDefaults(this.enabledCipherSuites)) {
enabledCipherSuites = tlsConfiguration.getEnabledCipherSuites();
} else {
enabledCipherSuites = this.enabledCipherSuites;
}
return enabledCipherSuites != null ? copyOf(enabledCipherSuites, enabledCipherSuites.length) : null;
}
public void setEnabledCipherSuites(String enabledCipherSuites) {
this.enabledCipherSuites = StringUtils.splitAndTrim(enabledCipherSuites, ",");
}
@Override
public String[] getEnabledProtocols() {
String[] enabledProtocols;
if (isUseDefaults(this.enabledProtocols)) {
enabledProtocols = tlsConfiguration.getEnabledProtocols();
} else {
enabledProtocols = this.enabledProtocols;
}
return enabledProtocols != null ? copyOf(enabledProtocols, enabledProtocols.length) : null;
}
public void setEnabledProtocols(String enabledProtocols) {
this.enabledProtocols = StringUtils.splitAndTrim(enabledProtocols, ",");
}
@Override
public boolean isKeyStoreConfigured() {
return tlsConfiguration.getKeyStore() != null;
}
@Override
public boolean isTrustStoreConfigured() {
return tlsConfiguration.getTrustStore() != null;
}
@Override
public TlsContextKeyStoreConfiguration getKeyStoreConfiguration() {
return new TlsContextKeyStoreConfiguration() {
@Override
public String getAlias() {
return getKeyAlias();
}
@Override
public String getKeyPassword() {
return DefaultTlsContextFactory.this.getKeyPassword();
}
@Override
public String getPath() {
return getKeyStorePath();
}
@Override
public String getPassword() {
return getKeyStorePassword();
}
@Override
public String getType() {
return getKeyStoreType();
}
@Override
public String getAlgorithm() {
return getKeyManagerAlgorithm();
}
};
}
@Override
public TlsContextTrustStoreConfiguration getTrustStoreConfiguration() {
return new TlsContextTrustStoreConfiguration() {
@Override
public String getPath() {
return getTrustStorePath();
}
@Override
public String getPassword() {
return getTrustStorePassword();
}
@Override
public String getType() {
return getTrustStoreType();
}
@Override
public String getAlgorithm() {
return getTrustManagerAlgorithm();
}
@Override
public boolean isInsecure() {
return isTrustStoreInsecure();
}
};
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DefaultTlsContextFactory)) {
return false;
}
DefaultTlsContextFactory that = (DefaultTlsContextFactory) o;
if (!tlsConfiguration.equals(that.tlsConfiguration)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return tlsConfiguration.hashCode();
}
private static class InsecureTrustManager implements X509TrustManager {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[0];
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
}
}