/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.web.http;
import io.netty.handler.ssl.OpenSsl;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStore.Entry;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManager;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import divconq.hub.Hub;
import divconq.log.Logger;
import divconq.util.KeyUtil;
import divconq.util.StringUtil;
import divconq.xml.XElement;
public class SslContextFactory {
protected SSLContext serverContext = null;
protected String sslclientauth = null;
protected List<String> keynames = new ArrayList<>();
private List<XElement> clientcerts = null;
public boolean keynameMatch(String name) {
for (String kname : this.keynames)
if (kname.equals(name))
return true;
int p = name.indexOf('.');
name = name.substring(p + 1);
for (String kname : this.keynames)
if (kname.endsWith(name))
return true;
return false;
}
public void init(XElement config, XElement sslconfig) {
if (config == null)
return;
this.clientcerts = config.selectAll("ClientCert");
WebTrustManager tm = new WebTrustManager();
tm.init(config);
TrustManager[] trustManagers = new TrustManager[] { tm };
this.sslclientauth = config.getAttribute("SslClientAuth", "None"); // Want, Need, None
this.init(sslconfig, null, trustManagers);
}
public void init(XElement sslconfig, String prepath, TrustManager[] trustManagers) {
if (sslconfig == null)
return;
String algorithm = sslconfig.getAttribute("Algorithm", "SunX509");
String protocol = sslconfig.getAttribute("Protocol", "TLSv1.2");
String jksfile = sslconfig.getAttribute("File");
if (StringUtil.isNotEmpty(prepath))
jksfile = prepath + jksfile;
String jkspass = sslconfig.getAttribute("Password");
// try to decrypt, if we succeed then we use it - if we don't then use plain text password
String jkspw = Hub.instance.getClock().getObfuscator().decryptHexToString(jkspass);
if (jkspw != null)
jkspass = jkspw;
if (StringUtil.isEmpty(jksfile))
return;
try {
// load keystore
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(jksfile), jkspass.toCharArray());
//if (Logger.isDebug()) {
Enumeration<String> aliases = ks.aliases();
Logger.debug("Certs and keys in web server key store:");
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
if (ks.isCertificateEntry(alias)) {
Entry e = ks.getEntry(alias, null);
KeyStore.TrustedCertificateEntry certentry = (KeyStore.TrustedCertificateEntry) e;
//Logger.info("Trusted Cert: " + alias + " " + certentry.getTrustedCertificate().getType());
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(certentry.getTrustedCertificate().getEncoded());
X509Certificate cert = (X509Certificate)certFactory.generateCertificate(in);
String subject = cert.getSubjectDN().toString();
String thumbprint = KeyUtil.getCertThumbprint(cert);
Logger.debug("Trusted Cert: " + alias + " Subject: " + subject + " Thumbprint: " + thumbprint);
}
else if (ks.isKeyEntry(alias)) {
Entry e = ks.getEntry(alias, new KeyStore.PasswordProtection(jkspass.toCharArray()));
KeyStore.PrivateKeyEntry keyentry = (KeyStore.PrivateKeyEntry) e;
//String thumb = HashUtil.getCertThumbprint(keyentry.getCertificate());
//Logger.info("Key: " + thumb + " / " + keyentry.getPrivateKey().getFormat() + " / " + keyentry.getPrivateKey().getAlgorithm());
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(keyentry.getCertificate().getEncoded());
X509Certificate cert = (X509Certificate)certFactory.generateCertificate(in);
String subject = cert.getSubjectDN().toString();
String thumbprint = KeyUtil.getCertThumbprint(cert);
X500Name x500name = new JcaX509CertificateHolder(cert).getSubject();
RDN cn = x500name.getRDNs(BCStyle.CN)[0];
String scn = IETFUtils.valueToString(cn.getFirst().getValue());
//Logger.info("Key: " + subject + " : " + thumbprint);
Logger.debug("Key: " + alias + " Subject: " + subject + " Thumbprint: " + thumbprint);
this.keynames.add(scn);
/*
if ((key instanceof PrivateKey) && "PKCS#8".equals(key.getFormat())) {
// Most PrivateKeys use this format, but check for safety.
try (FileOutputStream os = new FileOutputStream(alias + ".key")) {
os.write(key.getEncoded());
os.flush();
}
}
*/
}
}
//}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(ks, jkspass.toCharArray());
// init server context
SSLContext serverContext = SSLContext.getInstance(protocol);
serverContext.init(kmf.getKeyManagers(), trustManagers, null);
if (Logger.isTrace()) {
Logger.trace("TLS Provider: " + serverContext.getProvider().getName());
Logger.trace("TLS Protocol: " + serverContext.getProtocol());
SSLServerSocketFactory sfactory = serverContext.getServerSocketFactory();
Logger.trace("Default Suites");
for (String p : sfactory.getDefaultCipherSuites())
Logger.trace("Suite: " + p);
Logger.trace("Supported Suites");
for (String p : sfactory.getSupportedCipherSuites())
Logger.trace("Suite: " + p);
}
this.serverContext = serverContext;
Logger.info("OpenSSL in use (web): " + OpenSsl.isAvailable());
}
catch (Exception x) {
// TODO
throw new Error("Failed to initialize the SSLContext", x);
}
}
public SSLContext getServerContext() {
return this.serverContext;
}
public SSLEngine getServerEngine(String hostname) {
SSLEngine engine = this.serverContext.createSSLEngine();
engine.setUseClientMode(false);
if ("Want".equals(this.sslclientauth))
engine.setWantClientAuth(true);
if ("Need".equals(this.sslclientauth))
engine.setNeedClientAuth(true);
if (this.clientcerts != null) {
for (XElement ccel : this.clientcerts) {
if (hostname.endsWith(ccel.getAttribute("Names"))) {
String cauth = ccel.getAttribute("SslClientAuth", "None");
if ("Want".equals(cauth))
engine.setWantClientAuth(true);
if ("Need".equals(cauth))
engine.setNeedClientAuth(true);
break;
}
}
}
Hub.instance.getSecurityPolicy().hardenPublic(engine);
return engine;
}
}