How can I get multiple SSL certificates for Java server

I have my own HTTP server written in Java; the complete source code is at my disposal. An HTTP server can configure any number of websites, each of which will have a separate listening socket created using

skt=SSLServerSocketFactory.getDefault().createServerSocket(prt,bcklog,adr); 

Using the standard keystore created using Java keytool, I have not been able to figure out all my life how to get different certificates associated with different sockets for listening, so that each configured website has its own certificate.

I am in this position right now, so some code examples that illustrate will be most appreciated. But I would really appreciate a good overview of how JSSE hangs together in this regard (I looked for Sun JSSE doco until my brain hurts (literally, although it could be like giving up caffeine)).

Edit

Is there an easy way to use an alias to bind server certificates in a keystore using listening sockets? So that:

  • The client has one keystore for managing all certificates and
  • No need to mess around with multiple keystores, etc.

I got the impression (earlier today) that I could write a simple KeyManager, and only chooseServerAlias(...) returns a non-zero value, which is the name of the alias that I wanted - someone has thoughts along this line of reasoning

Decision

The solution I used built from slyvarking was to create a temporary key store and populate it with the desired key / certificate extracted from the unique foreign key store. The code follows whoever is interested (svrctfals is my "server certificate alias"):

  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; 
+12
java certificate ssl
Nov 24 '09 at 5:38
source share
3 answers

The easiest way to do this is to use one certificate for all of your domain names. Put all other site names in the SAN (alternate topic name).

If you prefer one certificate for each domain name, you can write your own key manager and use an alias to identify the domain so you can use one keystore. In our system, we make an agreement that the keystore alias is always 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; } } 
+8
Nov 25 '09 at 1:34
source share

You cannot use the default SSLServerSocketFactory .

Instead of initializing a different SSLContext for each site, each of which uses KeyManagerFactory configured with a keystore containing a key entry with the correct server certificate. (After initializing the KeyManagerFactory pass the key managers the init method SSLContext .)

After initializing the SSLContext get the SSLServerSocketFactory and use this to create your listener.

 KeyStore identity = KeyStore.getInstance(KeyStore.getDefaultType()); /* Load the keystore (a different one for each site). */ ... SSLContext ctx = SSLContext.getInstance("TLS"); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(identity, password); ctx.init(kmf.getKeyManagers(), null, null); SSLServerSocketFactory factory = ctx.getServerSocketFactory(); ServerSocket server = factory.createSocket(port); 
+5
Nov 24 '09 at 5:42
source share

I recently encountered a similar situation. I have my own built-in Java web server that can host any number of websites. Each website has its own domain name. Each website / domain is assigned a unique IP address on the server. A socket listener is created for each IP address on port 80.

For sites that have SSL certificates, I imported the keys and certificates into one KeyStore. I have assigned a certificate alias for each domain SSL certificate so that it matches the domain name. Each domain / website that has an SSL certificate is assigned a new socket listener on port 443.

By default, the standard Java X509KeyManager and the SunX509 implementation will select the first aliases that it finds, for which there is a private key and a key of the desired type for the selected cipher suite (usually RSA) . Unfortunately, the selected alias does not necessarily correspond to the requested domain, therefore you receive certificate errors.

To get around this, I used the ZZ Coder suggestion and implemented a custom X509KeyManager. Actually for my server I need X509ExtendedKeyManager, which has an extra selectEngineServerAlias ​​() method.

My custom KeyManager relies on a hash file of hostnames and their corresponding IP addresses. When a new SSL request is made, it checks the incoming IP address and finds the corresponding host name. He then tries to find an alias in the keystore that matches the host name.

 private class MyKeyManager extends X509ExtendedKeyManager implements X509KeyManager { private KeyStore keyStore; private char[] password; private java.util.HashMap<InetAddress, String> hosts; public MyKeyManager(KeyStore keystore, char[] password, java.util.HashMap<InetAddress, String> hosts) throws IOException, GeneralSecurityException { this.keyStore = keystore; this.password = password; this.hosts = hosts; } public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { try{ return hosts.get(InetAddress.getByName(engine.getPeerHost())); } catch(Exception e){ return null; } } public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { return hosts.get(socket.getLocalAddress()); } 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) { e.printStackTrace(); return null; } } public String[] getServerAliases(String keyType, Principal[] issuers) { throw new UnsupportedOperationException("Method getServerAliases() not yet implemented."); } public String[] getClientAliases(String keyType, Principal[] issuers) { 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 chooseEngineClientAlias(String[] strings, Principal[] prncpls, SSLEngine ssle) { throw new UnsupportedOperationException("Method chooseEngineClientAlias() not yet implemented."); } } 

The custom KeyManager is used to initialize the SSLContext. The best part is that you only need to initialize one SSLContext.

 javax.net.ssl.KeyManager[] kms = new javax.net.ssl.KeyManager[]{ new MyKeyManager(keystore, keypass, hosts) }; TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(keystore); javax.net.ssl.TrustManager[] tms = tmf.getTrustManagers(); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kms, tms, null); 
+2
Mar 07 '17 at 18:25
source share



All Articles