Note that there are some explanatory texts on larger screens.

plurals
  1. POUsing two private keys (keystore) and two public keys (truststore) in one SSL Socket Connection
    primarykey
    data
    text
    <p><em>I need to use to key-pair in one socket ssl connection without change nothing in clients.</em></p> <p>Why?</p> <p>Because one client use a CN attribute in trust store for connection handshake and other clients using another value in the same attribute to process the same task in the same way.</p> <p>So I need to use <strong>two key store</strong> (private) <em>with distinct CN attributes</em> and also aliases and <em>share two different trust store</em> (public key) with distinct CN attributes and also aliases too.</p> <p>Describing bellow:</p> <p><strong>keyStore1</strong></p> <p>Keystore type: JKS</p> <p>Keystore provider: SUN</p> <p>Alias name: <strong>identity1</strong></p> <p>Owner: <strong>CN=app1</strong> ...</p> <p>Issuer: CN=app1 ...</p> <p><strong>trustStore1</strong></p> <p>Alias name: <strong>identity1</strong></p> <p>Owner: <strong>CN=app1</strong> ...</p> <p>Issuer: CN=app1 ...</p> <p><strong>keyStore2</strong></p> <p>Alias name: <strong>identity2</strong></p> <p>Owner: CN=app2 ...</p> <p>Issuer: CN=app2 ...</p> <p><strong>trustStore2</strong></p> <p>Alias name: <strong>identity2</strong></p> <p>Owner: <strong>CN=app2</strong> ...</p> <p>Issuer: CN=app2 ...</p> <p>I tried to implement this feature in this way:</p> <pre><code> KeyStore KeyStore1; try { String keyStoreFile1 = "privatekey1"; String keyStoreType1 = "jks"; char[] keyStorePwd1 = "password".toCharArray(); keyStore1 = KeyStore.getInstance(keyStoreType1); keyStore1.load(new FileInputStream(keyStoreFile1), keyStorePwd1); } catch (java.security.GeneralSecurityException thr) { throw new IOException("Cannot load keystore (" + thr + ")"); } KeyStore trustStore1; try { String trustStoreFile1 = "publickey1"; String trustStoreType1 = "jks"; char[] trustStorePwd1 = "password".toCharArray(); trustStore1 = KeyStore.getInstance(trustStoreType1); trustStore.load(new FileInputStream(trustStoreFile1), trustStorePwd1); } catch (java.security.GeneralSecurityException thr) { throw new IOException("Cannot load truststore (" + thr + ")"); } KeyStore keyStore2; try { String keyStoreFile2 = "privatekey2"; String keyStoreType2 = "jks"; char[] keyStorePwd2 = "anotherpass".toCharArray(); keyStore2 = KeyStore.getInstance(key2StoreType); keyStore2.load(new FileInputStream(keyStoreFile2), keyStorePwd2); } catch (java.security.GeneralSecurityException thr) { throw new IOException("Cannot load keystore (" + thr + ")"); } KeyStore trustStore2; try { String trustStoreFile2 = "publickey2"; String trustStoreType2 = "jks"; char[] trustStorePwd2 = "anotherpass".toCharArray(); trustStore2 = KeyStore.getInstance(trustStoreType2); trustStore2.load(new FileInputStream(trustStoreFile2), trustStorePwd2); } catch (java.security.GeneralSecurityException thr) { throw new IOException("Cannot load truststore (" + thr + ")"); } KeyManagerFactory kmfkey1 = KeyManagerFactory .getInstance(KeyManagerFactory.getkey1Algorithm()); kmfkey1.init(keyStore1, "password".toCharArray()); TrustManagerFactory tmfkey1 = TrustManagerFactory.getInstance(TrustManagerFactory.getkey1Algorithm()); tmfkey1.init(trustStore1); SSLContext ctx = SSLContext.getInstance("SSL"); ctx.init(kmfkey1.getKeyManagers(), tmfkey1.getTrustManagers(), null); KeyManagerFactory kmfkey2 = KeyManagerFactory. getInstance(KeyManagerFactory.getkey1Algorithm()); kmfkey2.init(keyStore2, "password".toCharArray()); TrustManagerFactory tmfkey2 = TrustManagerFactory .getInstance(TrustManagerFactory.getkey1Algorithm()); tmfkey2.init(trustStore2); SSLContext ctxkey2 = SSLContext.getInstance("SSL"); ctxkey2.init(kmfkey2.getKeyManagers(), tmfkey2.getTrustManagers(), null); SSLServerSocketFactory sslSrvSockFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); serverSocket = sslSrvSockFact.createServerSocket(port); </code></pre> <p>... But I received this error message</p> <p>... </p> <p><em>java.security.KeyStoreException: Uninitialized keystore at java.security.KeyStore.aliases(KeyStore.java:941) at com.sun.net.ssl.internal.ssl.SunX509KeyManagerImpl.(SunX509KeyManagerImpl.java:106) at com.sun.net.ssl.internal.ssl.KeyManagerFactoryImpl$SunX509.engineInit(KeyManagerFactoryImpl.java:41) at javax.net.ssl.KeyManagerFactory.init(KeyManagerFactory.java:192)</em></p> <p>...</p> <p>So I really want to know if is possible to use to key-pairs in one socket connection or solve this in a different way that I can't see or deal with. </p> <p><strong>EDIT 1</strong></p> <p>Bruno, could you give me an entire or complete example please?</p> <p>Because is not clear for me....</p> <p>I tried two things:</p> <p>One solutions is put the two keys inside a private keystore following a previous suggestion... but doesn't work and I received the message bellow:</p> <p><em>Exception in thread "main" java.lang.NoSuchMethodError: javax.net.ssl.SSLContext.setDefault(Ljavax/net/ssl/SSLContext;) at ...initialiseManager(499)</em></p> <p>You was right ... I still need to choose one context or </p> <p><em>javax.net.ssl.SSLException: No available certificate or key corresponds to the SSL cipher suites which are enabled. while trying send a message to the server...</em></p> <p>Second I followed your suggestion...but SSL Socket connection doesn't start</p> <p>I implemented this with help of many other guys that showed their code here in this site...thanks for all </p> <pre><code>private KeyManager[] getKeyManagers() throws IOException, GeneralSecurityException { // First, get the default KeyManagerFactory. String alg = KeyManagerFactory.getDefaultAlgorithm(); KeyManagerFactory kmFact = KeyManagerFactory.getInstance(alg); // Next, set up the KeyStore to use. We need to load the file into // a KeyStore instance. File keyFile = new File("privatekey1"); FileInputStream fis = new FileInputStream(keyFile); LogManager.log("Loaded keystore privatekey1 " + keyFile.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL); KeyStore ks = KeyStore.getInstance("jks"); String keyStorePassword = "password"; ks.load(fis, keyStorePassword.toCharArray()); fis.close(); // Now we initialise the KeyManagerFactory with this KeyStore kmFact.init(ks, keyStorePassword.toCharArray()); KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg); File keyFileTwo = new File("privatekey2"); FileInputStream fisTwo = new FileInputStream(keyFileTwo); LogManager.log("Loaded keystore privatekey2 " + keyFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL); KeyStore ksTwo = KeyStore.getInstance("jks"); String keyStorePasswordTwo = "password"; ksTwo.load(fisTwo, keyStorePasswordTwo.toCharArray()); fisTwo.close(); // Now we initialise the KeyManagerFactory with this KeyStore dkmFact.init(ksTwo, keyStorePasswordTwo.toCharArray()); // default //KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg); //dkmFact.init(null, null); // Get the first X509KeyManager in the list X509KeyManager customX509KeyManager = getX509KeyManager(alg, kmFact); X509KeyManager jvmX509KeyManager = getX509KeyManager(alg, dkmFact); KeyManager[] km = {new MultiKeyStoreManager(jvmX509KeyManager, customX509KeyManager)}; LogManager.log("Number of key managers registered:" + km.length, LogManager.LOG_LOWEST_LEVEL); return km; } /** * Find a X509 Key Manager compatible with a particular algorithm * @param algorithm * @param kmFact * @return * @throws NoSuchAlgorithmException */ private X509KeyManager getX509KeyManager(String algorithm, KeyManagerFactory kmFact) throws NoSuchAlgorithmException { KeyManager[] keyManagers = kmFact.getKeyManagers(); if (keyManagers == null || keyManagers.length == 0) { throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " produced no key managers"); } X509KeyManager x509KeyManager = null; for (int i = 0; i &lt; keyManagers.length; i++) { if (keyManagers[i] instanceof X509KeyManager) { x509KeyManager = (X509KeyManager) keyManagers[i]; break; } } if (x509KeyManager == null) { throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " did not produce a X509 Key manager"); } return x509KeyManager; } private void initialiseManager(int iPort) throws IOException, GeneralSecurityException { // Next construct and initialise a SSLContext with the KeyStore and // the TrustStore. We use the default SecureRandom. // load your key store as a stream and initialize a KeyStore File trustFile = new File("publicKey"); LogManager.log("Trust File Loaded " + trustFile.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL); InputStream trustStream = new FileInputStream(trustFile); KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); // if your store is password protected then declare it (it can be null however) char[] trustPassword = "password".toCharArray(); // load the stream to your store trustStore.load(trustStream, trustPassword); File trustFileTwo = new File("publicKeyTwo"); LogManager.log("Trust File Loaded " + trustFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL); InputStream trustStreamTwo = new FileInputStream(trustFileTwo); KeyStore trustStoreTwo = KeyStore.getInstance(KeyStore.getDefaultType()); // if your store is password protected then declare it (it can be null however) char[] trustPasswordTwo = "password".toCharArray(); // load the stream to your store trustStoreTwo.load(trustStreamTwo, trustPasswordTwo); // initialize a trust manager factory with the trusted store TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustFactory.init(trustStore); trustFactory.init(trustStoreTwo); // get the trust managers from the factory TrustManager[] managers = trustFactory.getTrustManagers(); SSLContext context = SSLContext.getInstance("SSL"); context.init(getKeyManagers(), managers, new SecureRandom()); SSLContext.setDefault(context); SSLServerSocketFactory sslSrvFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); serverSocket = sslSrvFact.createServerSocket(iPort); // this method didn't create a Socket Connection correctly } class MultiKeyStoreManager implements X509KeyManager { private final X509KeyManager jvmKeyManager; private final X509KeyManager customKeyManager; public MultiKeyStoreManager(X509KeyManager jvmKeyManager, X509KeyManager customKeyManager) { this.jvmKeyManager = jvmKeyManager; this.customKeyManager = customKeyManager; } @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { // try the first key manager String alias = customKeyManager.chooseClientAlias(keyType, issuers, socket); if (alias == null) { alias = jvmKeyManager.chooseClientAlias(keyType, issuers, socket); LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); } return alias; } @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { // try the first key manager String alias = customKeyManager.chooseServerAlias(keyType, issuers, socket); if (alias == null) { alias = jvmKeyManager.chooseServerAlias(keyType, issuers, socket); LogManager.log("Reverting to JVM Server alias : " + alias ,LogManager.LOG_LOWEST_LEVEL); } return alias; } @Override public String[] getClientAliases(String keyType, Principal[] issuers) { String[] cAliases = customKeyManager.getClientAliases(keyType, issuers); String[] jAliases = jvmKeyManager.getClientAliases(keyType, issuers); LogManager.log("Supported Client Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length, LogManager.LOG_LOWEST_LEVEL); return (String[]) ArrayUtils.addAll(cAliases, jAliases); } @Override public PrivateKey getPrivateKey(String alias) { PrivateKey key = customKeyManager.getPrivateKey(alias); if (key == null) { System.out.println("Reverting to JVM Key : " + alias); return jvmKeyManager.getPrivateKey(alias); } else { return key; } } @Override public String[] getServerAliases(String keyType, Principal[] issuers) { String[] cAliases = customKeyManager.getServerAliases(keyType, issuers); String[] jAliases = jvmKeyManager.getServerAliases(keyType, issuers); LogManager.log("Supported Server Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length, LogManager.LOG_LOWEST_LEVEL); return (String[]) ArrayUtils.addAll(cAliases, jAliases); } @Override public java.security.cert.X509Certificate[] getCertificateChain(String string) { java.security.cert.X509Certificate[] chain = customKeyManager.getCertificateChain("alias_key1"); if (chain == null || chain.length == 0) { LogManager.log("Reverting to JVM Chain : " + string, LogManager.LOG_LOWEST_LEVEL); return jvmKeyManager.getCertificateChain("alias_key2"); } else { return chain; } } } and this gave me this status </code></pre> <p>*2012.02.09 18:47:00 Activating an SSL connection</p> <p>2012.02.09 18:47:00 [... ::run]</p> <p>2012.02.09 18:47:00 Trust File Loaded publicKey</p> <p>2012.02.09 18:47:00 Trust File Loaded publicKeyTwo</p> <p>2012.02.09 18:47:00 Loaded keystore privateKey privateKey</p> <p>2012.02.09 18:47:00 Loaded keystore privateKey2 privateKeyTwo</p> <p>2012.02.09 18:47:00 Number of key managers registered:1*</p> <p>But nothing happened when I tried to send a message for the server... </p> <p>Is being hard to find an example that really works in this case. </p> <p><strong>EDIT 2</strong></p> <p>Hi Bruno</p> <p>Really you have advanced knowledge about this subject, java and ssh and I appreciate your help. All this information is help me understand better this issue while is showing for me the way... Thx a lot</p> <p>And again you are right...I'm using code for Java 6 on a Java 5 JRE</p> <p>But following again your recipe I got this code :</p> <pre><code> // Load the key store: change store type if needed KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); FileInputStream fis = new FileInputStream("keyStore1"); char[] keyPass = "passw".toCharArray(); try { ks.load(fis, keyPass); } finally { if (fis != null) { fis.close(); } } // Get the default Key Manager KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, keyPass); final X509KeyManager origKm = (X509KeyManager) kmf.getKeyManagers()[0]; X509KeyManager km = new X509KeyManager() { public String chooseServerAlias(String[] keyType, Principal[] issuers, Socket socket) { String alias; InetAddress remoteAddress = socket.getInetAddress(); if (remoteAddress.getHostAddress().equalsIgnoreCase("11.111.111.11")) { alias = "alias1"; LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); } else { alias = "alias2"; LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); } return alias; } public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { // try this key manager String alias = origKm.chooseClientAlias(keyType, issuers, socket); LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); return alias; } public String[] getClientAliases(String keyType, Principal[] issues) { String[] cAliases = origKm.getClientAliases(keyType, issues); LogManager.log("Supported Client Aliases : " + cAliases.length, LogManager.LOG_LOWEST_LEVEL); return cAliases; } public String[] getServerAliases(String keyType, Principal[] issues) { String[] sAliases = origKm.getServerAliases(keyType, issues); LogManager.log("Supported Server Aliases: " + sAliases.length, LogManager.LOG_LOWEST_LEVEL); return sAliases; } public String chooseServerAlias(String keyType, Principal[] issues, Socket socket) { // try this key manager String alias = origKm.chooseServerAlias(keyType, issues, socket); LogManager.log("Reverting to JVM Server alias : " + alias, LogManager.LOG_LOWEST_LEVEL); return alias; } public X509Certificate[] getCertificateChain(String keyType) { // here I could specify my other keystore, keystore2 how I could do this? // I'm thinking in the righ way when I implemented this code to get the correct private key? X509Certificate[] chain = origKm.getCertificateChain("alias1"); if (chain == null || chain.length == 0) { LogManager.log("Reverting to JVM Chain : " + keyType, LogManager.LOG_LOWEST_LEVEL); return origKm.getCertificateChain("alias2"); } else { return chain; } } public PrivateKey getPrivateKey(String alias) { PrivateKey key = origKm.getPrivateKey(alias); // here I could get my other keystore according the alias, for example // keystore2 how I could do this? LogManager.log("Reverting to JVM Key : " + alias, LogManager.LOG_LOWEST_LEVEL); return key; } }; SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(new KeyManager[]{km}, null, null); SSLServerSocketFactory sslSrvFact = sslContext.getServerSocketFactory(); objServerSocket = sslSrvFact.createServerSocket(iPort); </code></pre> <p>Is exactly this I need to achieve my objective?</p> <p><strong>EDIT 3</strong></p> <p>Using this approach a got a handshake between client and server using the second keystore with alias2 using public trust store2 in client </p> <p>...but I still got error when I tried to use trust store1 in client</p> <p>...received a message from [addr=/11.111.111.11] Reverting to JVM Server alias : alias2 Reverting to JVM Key : alias2 Error in retriving: javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown</p> <p>My code not change the server to use the private key1 with alias1 ... when the client is using the public truststore1 with cert alias1...</p> <p>What more is necessary to do for solve this...I believe is near the final solution...</p> <p>thx again! </p> <p><strong>EDIT 4</strong></p> <p>Bruno , I changed the getCertificateChain method by following your suggestion showed bellow</p> <pre><code>public X509Certificate[] getCertificateChain(String alias) { X509Certificate[] chain = origKm.getCertificateChain(alias); return chain; } </code></pre> <p>and the method </p> <pre><code>public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { String alias; </code></pre> <p>also I removed the duplicated method...</p> <p>And in the addiction the clients that use the old trust store don't verify the hostname </p> <pre><code>private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; </code></pre> <p>But for the client that need to use the second trust store do this verification and it's a reason because I need to deal with to key-pair certificates... </p> <p><strong>EDIT5</strong></p> <p>I'd like to know how I can implement a server socket to let it able to identify and use correct certificate according with the certificate being used by client to proceed handshake communication with the server.</p> <p>Explaining better, in the server side is:</p> <p><strong>AppServerSideSocket.jar</strong></p> <ul> <li>private keystore: <strong>privateKeyApp</strong> (type JKS, generated with keytool)</li> <li>public keystore : <strong>publicKeyApp</strong> (type JKS, shared with all clients)</li> </ul> <p>And in the client side ...</p> <p><strong>AppClientSideSocket.jar</strong> - public keystore : <strong>publicKeyApp</strong> </p> <p>The <strong>AppServerSideSocket.jar</strong> listening clients requests and once received process information sent by clients</p> <p>The <strong>AppClientSideSocket.jar</strong> connect with the server using SSL using <strong>publicKeyApp</strong> <em>without verify server hostname</em> and after handshake send information for the AppServerSideSocket application.</p> <p>Now I've another client application, <strong>AppClientSideSocketNEW.jar</strong>, and this verify server hostname to make communication with the server. In this case, the CN used in the public certificate on the client side must be match with the hostname where <strong>AppServerSideSocket.jar</strong> are.</p> <p>Originally the connection was configured in this way on the server side:</p> <pre><code>if (usingSSLConn) { System.setProperty("javax.net.ssl.keyStore", "privateKeyApp"); System.setProperty("javax.net.ssl.keyStorePassword", "privateKeyAppPassword"); SSLServerSocketFactory sslServerSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); ServerSocket serverSocketApp = sslServerSocketFactory.createServerSocket(Port); } else serverSocketApp = new ServerSocket(Port); } </code></pre> <p>All the clients received the same publicKeyApp and connect with the server without verify hostname, so doesn't matter if the server where server socket application (<strong>AppServerSideSocket.jar</strong>) is installed in the server with hostname is badServer1.com and the CN of key in privateKeyApp and publicKeyApp is setted with goodServer1.com, because all the clients don't verify hostname or the CN attribute of the key.</p> <p>Bellow is showed a piece of this kind of connection</p> <pre><code> private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; System.setProperty("javax.net.ssl.trustStore", publicKey1); System.getProperties().setProperty("java.protocol.handler.pkgs", "javax.net.ssl.internal.www.protocol"); HttpsURLConnection.setDefaultHostnameVerifier(DO_NOT_VERIFY); ... SOAPConnectionFactory soapConn = SOAPConnectionFactory.newInstance(); SOAPConnection connection = soapConn.createConnection(); ... URL endpoint = new URL(hostname + ":" + port); </code></pre> <p>But the new client (<strong>AppClientSideSocketNEW.jar</strong>) do this verification obligatorily, now is necessary provide a new certificate for this client with new value for CN attribute reflecting the correct hostname CN where the server socket is.</p> <p>I don't have access to second client and I'm sure that it do hostname verification. </p> <p>So I created two new key-pair certificates (<strong>privateKeyAppNew and publicKeyAppNew</strong>) and apparently the communications happened with success between the server using this new key-pair and new client using this new public publicKeyAppNew key, after configured the server to use this new key-pair of course.</p> <p>But I need continue to use the old key-pair for old clients. I'd like to know how can I deal with this. </p> <p>Using a keymanager let me able to verify the client certificate on the server app when client try to connect and choose the appropriate and do the handshake using the correct certificate?</p> <p>Or I need distinct ssl socket connection in different ports for which kind of clients?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload