I have an in-house HTTP server written in Java; full source code at my disposal. The HTTP server can configure any number of web sites, each of which will have a separate listen socket created with:
skt=SSLServerSocketFactory.getDefault().createServerSocket(prt,bcklog,adr);
Using a standard key store created with the Java keytool, I cannot for the life of me work out how to get different certificates associated with different listen sockets so that each configured web site has it's own certificate.
I'm in a time pinch for this now, so some code samples that illustrate would be most appreciated. But as much I would appreciate any good overview on how JSSE hangs together in this regard (I have searched Sun's JSSE doco until my brain hurts (literally; though it might be as much caffeine withdrawal)).
Edit
Is there no simple way to use the alias to associate the server certificates in a key store with the listen sockets? So that:
I was getting the impression (earlier this afternoon) that I could write a simple KeyManager, with only chooseServerAlias(...)
returning non-null, that being the name of the alias I wanted - anyone have any thoughts on that line of reasoning?
Solution
The solution I used, built from slyvarking's answer was to create a temporary key store and populate it with the desired key/cert extracted from the singular external key store. Code follows for any who are interested (svrctfals is my "server certificate alias" value):
SSLServerSocketFactory ssf; // server socket factory
SSLServerSocket skt; // server socket
// LOAD EXTERNAL KEY STORE
KeyStore mstkst;
try {
String kstfil=GlobalSettings.getString("javax.net.ssl.keyStore" ,System.getProperty("javax.net.ssl.keyStore" ,""));
String ksttyp=GlobalSettings.getString("javax.net.ssl.keyStoreType" ,System.getProperty("javax.net.ssl.keyStoreType" ,"jks"));
char[] kstpwd=GlobalSettings.getString("javax.net.ssl.keyStorePassword",System.getProperty("javax.net.ssl.keyStorePassword","")).toCharArray();
mstkst=KeyStore.getInstance(ksttyp);
mstkst.load(new FileInputStream(kstfil),kstpwd);
}
catch(java.security.GeneralSecurityException thr) {
throw new IOException("Cannot load keystore ("+thr+")");
}
// CREATE EPHEMERAL KEYSTORE FOR THIS SOCKET USING DESIRED CERTIFICATE
try {
SSLContext ctx=SSLContext.getInstance("TLS");
KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore sktkst;
char[] blkpwd=new char[0];
sktkst=KeyStore.getInstance("jks");
sktkst.load(null,blkpwd);
sktkst.setKeyEntry(svrctfals,mstkst.getKey(svrctfals,blkpwd),blkpwd,mstkst.getCertificateChain(svrctfals));
kmf.init(sktkst,blkpwd);
ctx.init(kmf.getKeyManagers(),null,null);
ssf=ctx.getServerSocketFactory();
}
catch(java.security.GeneralSecurityException thr) {
throw new IOException("Cannot create secure socket ("+thr+")");
}
// CREATE AND INITIALIZE SERVER SOCKET
skt=(SSLServerSocket)ssf.createServerSocket(prt,bcklog,adr);
...
return skt;
A lot of people want to know whether you can install multiple SSL certificates on a single domain. The answer is yes.
In most cases, you'll only need one SSL certificate for your domain. But in certain cases, you might need two certificates for a single domain.
IIS 8 supports the TLS Server Name Indication (SNI) extension. From a single IP address and port, you can use multiple SSL certificates to secure various websites on a single domain (e.g., www.yourdomain.com, site2.yourdomain.com) or across multiple domains (e.g., www.domain1.com, www.domain2.com).
The easiest way to do this is to use a single certificate for all your domain names. Put all other site names in SAN (Subject Alternative Name).
If you prefer one certificate for each domain name, you can write your own key manager and use alias to identify the domain so you can use a single keystore. In our system, we make a convention that keystore alias always equals the CN in the certificate. So we can do something like this,
SSLContext sctx1 = SSLContext.getInstance("SSLv3");
sctx1.init(new X509KeyManager[] {
new MyKeyManager("/config/master.jks","changeme".toCharArray(),"site1.example.com")
},null, null);
SSLServerSocketFactory ssf = (SSLServerSocketFactory) sctx1.getServerSocketFactory();
ServerSocket ss1 = ssf.createServerSocket(1234);
...
SSLContext sctx2 = SSLContext.getInstance("SSLv3");
sctx2.init(new X509KeyManager[] {
new MyKeyManager("/config/master.jks","changeme".toCharArray(),"site2.example.com")
},null, null);
ssf = (SSLServerSocketFactory) sctx2.getServerSocketFactory();
ServerSocket ss2 = ssf.createServerSocket(5678);
...
public static class MyKeyManager implements X509KeyManager {
private KeyStore keyStore;
private String alias;
private char[] password;
MyKeyManager(String keyStoreFile, char[] password, String alias)
throws IOException, GeneralSecurityException
{
this.alias = alias;
this.password = password;
InputStream stream = new FileInputStream(keyStoreFile);
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(stream, password);
}
public PrivateKey getPrivateKey(String alias) {
try {
return (PrivateKey) keyStore.getKey(alias, password);
} catch (Exception e) {
return null;
}
}
public X509Certificate[] getCertificateChain(String alias) {
try {
java.security.cert.Certificate[] certs = keyStore.getCertificateChain(alias);
if (certs == null || certs.length == 0)
return null;
X509Certificate[] x509 = new X509Certificate[certs.length];
for (int i = 0; i < certs.length; i++)
x509[i] = (X509Certificate)certs[i];
return x509;
} catch (Exception e) {
return null;
}
}
public String chooseServerAlias(String keyType, Principal[] issuers,
Socket socket) {
return alias;
}
public String[] getClientAliases(String parm1, Principal[] parm2) {
throw new UnsupportedOperationException("Method getClientAliases() not yet implemented.");
}
public String chooseClientAlias(String keyTypes[], Principal[] issuers, Socket socket) {
throw new UnsupportedOperationException("Method chooseClientAlias() not yet implemented.");
}
public String[] getServerAliases(String parm1, Principal[] parm2) {
return new String[] { alias };
}
public String chooseServerAlias(String parm1, Principal[] parm2) {
return alias;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With