/*
* SoapUI, Copyright (C) 2004-2016 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.wsdl.support.http;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.support.StringUtils;
import org.apache.commons.ssl.KeyMaterial;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.log4j.Logger;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SoapUISSLSocketFactory extends SSLSocketFactory {
// a cache of factories for custom certificates/Keystores at the project level - never cleared
private static final Map<String, SSLSocketFactory> factoryMap = new ConcurrentHashMap<String, SSLSocketFactory>();
private final String sslContextAlgorithm = System.getProperty("soapui.sslcontext.algorithm", "TLS");
private final SSLContext sslContext = SSLContext.getInstance(sslContextAlgorithm);
private final static Logger log = Logger.getLogger(SoapUISSLSocketFactory.class);
@SuppressWarnings("deprecation")
public SoapUISSLSocketFactory(KeyStore keyStore, String keystorePassword) throws KeyManagementException,
UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {
super(keyStore);
// trust everyone!
X509TrustManager tm = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
};
if (keyStore != null) {
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmfactory.init(keyStore, keystorePassword != null ? keystorePassword.toCharArray() : null);
KeyManager[] keymanagers = kmfactory.getKeyManagers();
sslContext.init(keymanagers, new TrustManager[]{tm}, null);
} else {
sslContext.init(null, new TrustManager[]{tm}, null);
}
setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
}
private static SSLSocket enableSocket(SSLSocket socket) {
String invalidateSession = System.getProperty("soapui.https.session.invalidate");
String protocols = System.getProperty("soapui.https.protocols");
String ciphers = System.getProperty("soapui.https.ciphers");
if (StringUtils.hasContent(invalidateSession)) {
socket.getSession().invalidate();
}
if (StringUtils.hasContent(protocols)) {
socket.setEnabledProtocols(protocols.split(","));
}
// else if( socket.getSupportedProtocols() != null )
// {
// socket.setEnabledProtocols( socket.getSupportedProtocols() );
// }
if (StringUtils.hasContent(ciphers)) {
socket.setEnabledCipherSuites(ciphers.split(","));
}
// else if( socket.getSupportedCipherSuites() != null )
// {
// socket.setEnabledCipherSuites( socket.getSupportedCipherSuites() );
// }
return socket;
}
@Override
public Socket createSocket(HttpParams params) throws IOException {
String sslConfig = (String) params.getParameter(SoapUIHttpRoute.SOAPUI_SSL_CONFIG);
if (StringUtils.isNullOrEmpty(sslConfig)) {
return enableSocket((SSLSocket) sslContext.getSocketFactory().createSocket());
}
SSLSocketFactory factory = factoryMap.get(sslConfig);
if (factory != null) {
if (factory == this) {
return enableSocket((SSLSocket) sslContext.getSocketFactory().createSocket());
} else {
return enableSocket((SSLSocket) factory.createSocket(params));
}
}
try {
// try to create new factory for specified config
int ix = sslConfig.lastIndexOf(' ');
String keyStore = sslConfig.substring(0, ix);
String pwd = sslConfig.substring(ix + 1);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
if (keyStore.trim().length() > 0) {
File f = new File(keyStore);
if (f.exists()) {
log.info("Initializing Keystore from [" + keyStore + "]");
try {
KeyMaterial km = new KeyMaterial(f, pwd.toCharArray());
ks = km.getKeyStore();
} catch (Exception e) {
SoapUI.logError(e);
pwd = null;
}
}
}
factory = new SoapUISSLSocketFactory(ks, pwd);
factoryMap.put(sslConfig, factory);
return enableSocket((SSLSocket) factory.createSocket(params));
} catch (Exception gse) {
SoapUI.logError(gse);
return enableSocket((SSLSocket) super.createSocket(params));
}
}
/**
* @since 4.1
*/
@Override
public Socket connectSocket(final Socket socket, final InetSocketAddress remoteAddress,
final InetSocketAddress localAddress, final HttpParams params) throws IOException, UnknownHostException,
ConnectTimeoutException {
if (remoteAddress == null) {
throw new IllegalArgumentException("Remote address may not be null");
}
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
Socket sock = socket != null ? socket : new Socket();
if (localAddress != null) {
sock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params));
sock.bind(localAddress);
}
int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
int soTimeout = HttpConnectionParams.getSoTimeout(params);
try {
sock.setSoTimeout(soTimeout);
sock.connect(remoteAddress, connTimeout);
} catch (SocketTimeoutException ex) {
throw new ConnectTimeoutException("Connect to " + remoteAddress.getHostName() + "/"
+ remoteAddress.getAddress() + " timed out");
}
SSLSocket sslsock;
// Setup SSL layering if necessary
if (sock instanceof SSLSocket) {
sslsock = (SSLSocket) sock;
} else {
sslsock = (SSLSocket)sslContext.getSocketFactory().createSocket(sock, remoteAddress.getHostName(),
remoteAddress.getPort(), true);
sslsock = enableSocket(sslsock);
}
// do we need it? trust all hosts
// if( getHostnameVerifier() != null )
// {
// try
// {
// getHostnameVerifier().verify( remoteAddress.getHostName(), sslsock );
// // verifyHostName() didn't blowup - good!
// }
// catch( IOException iox )
// {
// // close the socket before re-throwing the exception
// try
// {
// sslsock.close();
// }
// catch( Exception x )
// { /* ignore */
// }
// throw iox;
// }
// }
return sslsock;
}
/**
* @since 4.1
*/
@Override
public Socket createLayeredSocket(final Socket socket, final String host, final int port, final boolean autoClose)
throws IOException, UnknownHostException {
SSLSocket sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
sslSocket = enableSocket(sslSocket);
// if( getHostnameVerifier() != null )
// {
// getHostnameVerifier().verify( host, sslSocket );
// }
// verifyHostName() didn't blowup - good!
return sslSocket;
}
}