/**
* VMware Continuent Tungsten Replicator
* Copyright (C) 2015 VMware, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Initial developer(s): Ludovic Launer
* Contributor(s):
*/
package com.continuent.tungsten.common.sockets;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import org.apache.log4j.Logger;
import com.continuent.tungsten.common.config.cluster.ConfigurationException;
import com.continuent.tungsten.common.security.AuthenticationInfo;
import com.continuent.tungsten.common.security.SecurityHelper;
/**
* Implements an class to generate SSLSocketFactory instances. The author
* gratefully acknowledges the blog article by Alexandre Saudate for providing
* guidance in the implementation.
*
* @see <a href=
* "http://alesaudate.wordpress.com/2010/08/09/how-to-dynamically-select-a-certificate-alias-when-invoking-web-services/">
* How to dynamically select a certificate alias when invoking web
* services</a>
*/
public class SSLSocketFactoryGenerator
{
private static final Logger logger = Logger.getLogger(SSLSocketFactoryGenerator.class);
private String alias = null;
private String keystoreLocation = null;
private String trustStoreLocation = null;
AuthenticationInfo securityPropertiesAuthenticationInfo = null;
public SSLSocketFactoryGenerator(String alias,
AuthenticationInfo securityPropertiesAuthenticationInfo)
throws ConfigurationException
{
this.alias = alias;
this.securityPropertiesAuthenticationInfo = securityPropertiesAuthenticationInfo;
this.keystoreLocation = (securityPropertiesAuthenticationInfo != null)
? securityPropertiesAuthenticationInfo.getKeystoreLocation()
: SecurityHelper.getKeyStoreLocation();
this.trustStoreLocation = (securityPropertiesAuthenticationInfo != null)
? securityPropertiesAuthenticationInfo.getTruststoreLocation()
: SecurityHelper.getTrustStoreLocation();
this.checkConsistency();
}
/**
* Ensure that prerequisites are met.
*
* @throws ConfigurationException
*/
private void checkConsistency() throws ConfigurationException
{
if (this.securityPropertiesAuthenticationInfo == null
&& this.alias != null)
{
throw new ConfigurationException("\n\tError : "
+ "Both securityPropertiesAuthenticationInfo and alias are "
+ "null which makes it impossible to fetch the associated "
+ "key from keyStore.");
}
}
/**
* Creates the SSLContext to be used when creating sockets or server
* sockets. This uses the custom alias selector
*
* @return
* @throws IOException
* @throws GeneralSecurityException
* @throws ConfigurationException
*/
private SSLContext getSSLContext() throws IOException,
GeneralSecurityException, ConfigurationException
{
KeyManager[] keyManagers = getKeyManagers();
TrustManager[] trustManagers = getTrustManagers();
// For each key manager, check if it is a X509KeyManager (because we
// will override its //functionality
for (int i = 0; i < keyManagers.length; i++)
{
if (keyManagers[i] instanceof X509KeyManager)
{
keyManagers[i] = new AliasSelectorKeyManager(
(X509KeyManager) keyManagers[i], alias);
}
}
// Use the first protocol in SSLContext - the fact that there may be
// multiple configured protocols is not handled here;
// SSLContext.getInstance only takes one
SSLContext context = SSLContext.getInstance(SecurityHelper
.getProtocol());
context.init(keyManagers, trustManagers, null);
return context;
}
/**
* Get an SSLSocketFactory with the custom alias selector
*
* @return
* @throws IOException
* @throws GeneralSecurityException
* @throws ConfigurationException
*/
public SSLSocketFactory getSSLSocketFactory()
throws IOException, GeneralSecurityException, ConfigurationException
{
// --- No alias defined. Use default SSL socket factory ---
if (this.alias == null || SecurityHelper.getProtocol() == null)
{
logger.debug("No keystore alias entry defined. Will use default "
+ "SSLSocketFactory selecting 1st entry in keystore !");
return (SSLSocketFactory) SSLSocketFactory.getDefault();
}
// --- Alias defined. Use custom alias selector ---
else
{
SSLContext context = this.getSSLContext();
SSLSocketFactory ssf = context.getSocketFactory();
return ssf;
}
}
/**
* Get an SSLServerSocketFactory with the custom alias selector
*
* @return
* @throws IOException
* @throws GeneralSecurityException
* @throws ConfigurationException
*/
public SSLServerSocketFactory getSSLServerSocketFactory() throws IOException,
GeneralSecurityException, ConfigurationException
{
// --- No alias defined. Use default SSL socket factory ---
if (this.alias == null)
{
logger.debug("No keystore alias entry defined. Will use default SSLServerSocketFactory selecting 1st entry in keystore !");
return (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
}
// --- Alias defined. Use custom alias selector ---
else
{
SSLContext context = this.getSSLContext();
SSLServerSocketFactory ssf = context.getServerSocketFactory();
return ssf;
}
}
public String getKeyStorePassword()
{
return this.securityPropertiesAuthenticationInfo.getKeystorePassword();
}
public String getTrustStorePassword()
{
return this.securityPropertiesAuthenticationInfo
.getTruststorePassword();
}
public String getKeyStore()
{
return keystoreLocation;
}
public String getTrustStore()
{
return trustStoreLocation;
}
private KeyManager[] getKeyManagers()
throws IOException, GeneralSecurityException
{
// Init a key store with the given file.
String alg = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmFact = KeyManagerFactory.getInstance(alg);
FileInputStream fis = new FileInputStream(getKeyStore());
KeyStore ks = KeyStore.getInstance("jks");
ks.load(fis, getKeyStorePassword().toCharArray());
fis.close();
// Init the key manager factory with the loaded key store
kmFact.init(ks, getKeyStorePassword().toCharArray());
KeyManager[] kms = kmFact.getKeyManagers();
return kms;
}
protected TrustManager[] getTrustManagers()
throws IOException, GeneralSecurityException
{
String alg = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmFact = TrustManagerFactory.getInstance(alg);
FileInputStream fis = new FileInputStream(getTrustStore());
KeyStore ks = KeyStore.getInstance("jks");
ks.load(fis, getTrustStorePassword().toCharArray());
fis.close();
tmFact.init(ks);
TrustManager[] tms = tmFact.getTrustManagers();
return tms;
}
}