/*
* Copyright 2015-2025 the original author or authors.
*
* 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.
*/
package sockslib.common;
import com.google.common.base.Strings;
import sockslib.utils.PathUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.security.KeyStore;
import java.util.Properties;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* The class <code>SSLConfiguration</code> represents a configuration of SSL.
*
* @author Youchao Feng
* @version 1.0
* @date May 17, 2015 6:52:52 PM
*/
public class SSLConfiguration {
private static final Logger logger = LoggerFactory.getLogger(SSLConfiguration.class);
private KeyStoreInfo keyStoreInfo;
private KeyStoreInfo trustKeyStoreInfo;
private boolean needClientAuth = false;
public SSLConfiguration(KeyStoreInfo keyStoreInfo, KeyStoreInfo trustKeyStoreInfo) {
this(keyStoreInfo, trustKeyStoreInfo, false);
}
public SSLConfiguration(@Nullable KeyStoreInfo keyStoreInfo, @Nullable KeyStoreInfo
trustKeyStoreInfo, boolean clientAuth) {
this.keyStoreInfo = keyStoreInfo;
this.trustKeyStoreInfo = trustKeyStoreInfo;
this.needClientAuth = clientAuth;
}
/**
* Creates a {@link SSLConfiguration} instance with a string.<br>
* The string should format as: <br>
* <pre>
* KEYS_TORE_PATH,KEY_STORE_PASSWORD,TRUST_KEY_STORE_PATH,TRUST_KEY_STORE_PASSWORD,CLIENT_AUTH
* </pre>
*
* @param configuration configuration as a string.
* @return the instance of {@link SSLConfiguration}.
*/
public static SSLConfiguration parse(String configuration) {
String[] strings = configuration.split(",");
if (strings.length == 2) {
KeyStoreInfo keyStoreInfo = new KeyStoreInfo(strings[0], strings[1]);
return new SSLConfiguration(keyStoreInfo, null);
} else if (strings.length == 4) {
KeyStoreInfo keyStoreInfo = new KeyStoreInfo(strings[0], strings[1]);
KeyStoreInfo trustKeyStoreInfo = new KeyStoreInfo(strings[2], strings[3]);
return new SSLConfiguration(keyStoreInfo, trustKeyStoreInfo);
} else if (strings.length == 5) {
KeyStoreInfo keyStoreInfo = new KeyStoreInfo(strings[0], strings[1]);
KeyStoreInfo trustKeyStoreInfo = new KeyStoreInfo(strings[2], strings[3]);
return new SSLConfiguration(keyStoreInfo, trustKeyStoreInfo, strings[4].equals("true"));
}
return null;
}
public static SSLConfiguration load(String filePath) throws FileNotFoundException, IOException {
checkNotNull(filePath, "Argument [filePath] may not be null");
logger.debug("load SSL configuration file:{}", filePath);
KeyStoreInfo keyStoreInfo = null;
KeyStoreInfo trustKeyStoreInfo = null;
Properties properties = new Properties();
properties.load(new FileInputStream(filePath));
String keystorePath = PathUtil.getAbstractPath(properties.getProperty("ssl.keystore.location"));
String password = properties.getProperty("ssl.keystore.password");
String type = properties.getProperty("ssl.keystore.type", "JSK");
String trustKeystorePath =
PathUtil.getAbstractPath(properties.getProperty("ssl.trustStore.location"));
String trustPassword = properties.getProperty("ssl.trustStore.password");
String trustType = properties.getProperty("ssl.trustStore.type", "JSK");
if (!Strings.isNullOrEmpty(keystorePath)) {
keyStoreInfo = new KeyStoreInfo(keystorePath, password, type);
}
if (!Strings.isNullOrEmpty(trustKeystorePath)) {
trustKeyStoreInfo = new KeyStoreInfo(trustKeystorePath, trustPassword, trustType);
}
String clientAuthValue = properties.getProperty("ssl.client.auth", "false");
boolean clientAuth = false;
if (clientAuthValue.equalsIgnoreCase("true")) {
clientAuth = true;
}
return new SSLConfiguration(keyStoreInfo, trustKeyStoreInfo, clientAuth);
}
public static SSLConfiguration loadClassPath(String filePath) throws FileNotFoundException,
IOException {
checkNotNull(filePath, "Argument [filePath] may not be null");
if (!filePath.startsWith(File.separator)) {
filePath = File.separator + filePath;
}
URL url = SSLConfiguration.class.getResource(filePath);
if (url == null) {
throw new FileNotFoundException("classpath:" + filePath);
}
String path = url.getPath();
return load(path);
}
public SSLSocketFactory getSSLSocketFactory() throws SSLConfigurationException {
checkNotNull(trustKeyStoreInfo, "trustKeyStoreInfo may not be null");
KeyStore keyStore = null;
KeyStore trustKeyStore = null;
try {
SSLContext context = SSLContext.getInstance("SSL");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustKeyStore = KeyStore.getInstance(trustKeyStoreInfo.getType());
trustKeyStore.load(new FileInputStream(trustKeyStoreInfo.getKeyStorePath()),
trustKeyStoreInfo.getPassword().toCharArray());
trustManagerFactory.init(trustKeyStore);
if (keyStoreInfo != null && keyStoreInfo.getKeyStorePath() != null) {
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyStore = KeyStore.getInstance(keyStoreInfo.getType());
keyStore.load(new FileInputStream(keyStoreInfo.getKeyStorePath()), keyStoreInfo
.getPassword().toCharArray());
keyManagerFactory.init(keyStore, keyStoreInfo.getPassword().toCharArray());
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(),
null);
} else {
context.init(null, trustManagerFactory.getTrustManagers(), null);
}
if (keyStore != null) {
logger.info("SSL: Key store:{}", keyStoreInfo.getKeyStorePath());
}
logger.info("SSL: Trust key store:{}", trustKeyStoreInfo.getKeyStorePath());
return context.getSocketFactory();
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new SSLConfigurationException(e.getMessage());
}
}
public SSLServerSocketFactory getSSLServerSocketFactory() throws SSLConfigurationException {
checkNotNull(keyStoreInfo, "keyStoreInfo may not be null");
String KEY_STORE_PASSWORD = getKeyStoreInfo().getPassword();
String KEY_STORE_PATH = getKeyStoreInfo().getKeyStorePath();
KeyStore keyStore = null;
KeyStore trustKeyStore = null;
try {
SSLContext ctx = SSLContext.getInstance("SSL");
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(KEY_STORE_PATH), KEY_STORE_PASSWORD.toCharArray());
keyManagerFactory.init(keyStore, KEY_STORE_PASSWORD.toCharArray());
if (needClientAuth && trustKeyStoreInfo != null) {
String TRUST_KEY_STORE_PATH = getTrustKeyStoreInfo().getKeyStorePath();
String TRUST_KEY_STORE_PASSWORD = getTrustKeyStoreInfo().getPassword();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustKeyStore = KeyStore.getInstance("JKS");
trustKeyStore.load(new FileInputStream(TRUST_KEY_STORE_PATH), TRUST_KEY_STORE_PASSWORD
.toCharArray());
trustManagerFactory.init(trustKeyStore);
ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
} else {
ctx.init(keyManagerFactory.getKeyManagers(), null, null);
}
logger.info("SSL: Key store:{}", keyStoreInfo.getKeyStorePath());
if (trustKeyStore != null) {
logger.info("SSL: Trust key store:{}", trustKeyStoreInfo.getKeyStorePath());
}
logger.info("SSL: Client authentication:{}", needClientAuth);
;
return ctx.getServerSocketFactory();
} catch (Exception e) {
throw new SSLConfigurationException(e.getMessage());
}
}
/**
* Returns the keyStoreInfo.
*
* @return the keyStoreInfo
*/
public KeyStoreInfo getKeyStoreInfo() {
return keyStoreInfo;
}
/**
* @param keyStoreInfo the keyStoreInfo to set
*/
public void setKeyStoreInfo(KeyStoreInfo keyStoreInfo) {
this.keyStoreInfo = keyStoreInfo;
}
/**
* @return the trustKeyStoreInfo
*/
public KeyStoreInfo getTrustKeyStoreInfo() {
return trustKeyStoreInfo;
}
/**
* @param trustKeyStoreInfo the trustKeyStoreInfo to set
*/
public void setTrustKeyStoreInfo(KeyStoreInfo trustKeyStoreInfo) {
this.trustKeyStoreInfo = trustKeyStoreInfo;
}
/**
* Returns the needClientAuth.
*
* @return the needClientAuth
*/
public boolean isNeedClientAuth() {
return needClientAuth;
}
/**
* @param needClientAuth the needClientAuth to set
*/
public void setNeedClientAuth(boolean needClientAuth) {
this.needClientAuth = needClientAuth;
}
}