My SSL client (Java) does not send the certificate back to the server in two-way SSL handshake

In the Java 1.7 application running on Windows 7, I am trying to do two-way SSL with the server (the smart card token provides my client certificates through openSC). The server certificate is accurately verified by the client, but the client does not respond to the server certificate request. I believe, because the client cannot make the chain from my certificate to one of those requested by the server (although such a chain exists).

Here's the debugging of the SSL server certificate request and the empty client request:

*** CertificateRequest Cert Types: RSA, DSS, ECDSA Cert Authorities: <CN=c4isuite-SDNI-DC02-CA, DC=c4isuite, DC=local> <CN=DoD Root CA 2, OU=PKI, OU=DoD, O=US Government, C=US> ... *** ServerHelloDone *** Certificate chain *** 

My client certificate is as follows:

 found key for : Certificate for PIV Authentication chain [0] = [ [ Version: V3 Subject: CN=<...>, OU=CONTRACTOR, OU=PKI, OU=DoD, O=US Government, C=US Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 Key: Sun RSA public key, 2048 bits Issuer: CN=DOD CA-30, OU=PKI, OU=DoD, O=US Government, C=US SerialNumber: [ 05bf13] 

Through a key tool, I also installed in truststore (the java cacerts file) what should be the connection between my cert certificate, DOD CA-30 and what the server is requesting, DoD Root CA 2.

SSL debugging:

 adding as trusted cert: Subject: CN=DOD CA-30, OU=PKI, OU=DoD, O=US Government, C=US Issuer: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=US Government, C=US Algorithm: RSA; Serial number: 0x1b5 Valid from Thu Sep 08 10:59:24 CDT 2011 until Fri Sep 08 10:59:24 CDT 2017 adding as trusted cert: Subject: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=US Government, C=US Issuer: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=US Government, C=US Algorithm: RSA; Serial number: 0x5 Valid from Mon Dec 13 09:00:10 CST 2004 until Wed Dec 05 09:00:10 CST 2029 

So, the question is why the client cannot make a certificate chain for the response? Here is the relevant code:

  // Create the keyStore from the SmartCard certs Provider provider = new sun.security.pkcs11.SunPKCS11(configName); Security.addProvider(provider); keyStore = KeyStore.getInstance("PKCS11", "SunPKCS11-SCR3310test"); char[] pin = PIN.toCharArray(); keyStore.load(null, pin); // Init the trustmanager TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); // Create the client key manager LOG.info("Installing keystore with pin"); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyManagerFactory.init(clientKeyStore, clientKeyPassword.toCharArray()); sslContext.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), null); // Init SSL context SSLSocketFactory socketFactory = sslContext.getSocketFactory(); URL url = new URL(urlString); URLConnection connection = url.openConnection(); if (connection instanceof HttpsURLConnection) { LOG.info("Connection is HTTPS"); ((HttpsURLConnection) connection).setSSLSocketFactory(socketFactory); } // Send the request. connection.connect(); InputStreamReader in = new InputStreamReader((InputStream) connection.getContent()); ... 

And the error I am returning is that the server returns 403. Most likely because the client did not send it a client certificate.

+7
source share
2 answers

Although it looks like you just copied the portion of the CA list sent by the server to this question, I will assume that CN=DOD CA-30, OU=PKI, OU=DoD, O=US Government, C=US in this list.

What seems to be missing from the chain is a certificate (which you mention later):

  Subject: CN=DOD CA-30, OU=PKI, OU=DoD, O=US Government, C=US Issuer: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=US Government, C=US Algorithm: RSA; Serial number: 0x1b5 Valid from Thu Sep 08 10:59:24 CDT 2011 until Fri Sep 08 10:59:24 CDT 2017 

Importing certificates to your client supermarket absolutely does not affect the certificate that the client sends. The client certificate (and its private key) must be configured in the client key store. In addition, if you want to send a chain of client certificates (which is required here if the server does not offer this intermediate CA certificate in its list), you need to associate the entire chain with this certificate entry. It is not enough to put other certificates in the keystore.

To fix this, you must configure your keystore entry in the client-certificate chain. This can be done as described in this answer . However, it is possible that the fact that this is a hardware token accessed through PKCS # 11 can make it a little more difficult (perhaps there is another certificate management tool that comes with the card, possibly Java-independent).

+3
source

Since I know which certificate I need to use for authentication on the server, I can force the client to send this specific certificate by extending the X509ExtendedKeyManager and overriding the selectClientAlias ​​() method to always return the alias of that certificate. The code:

 public class MyX509KeyManager extends X509ExtendedKeyManager { X509KeyManager defaultKeyManager; public MyX509KeyManager(X509KeyManager inKeyManager) { defaultKeyManager = inKeyManager; } public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) { return "<Alias of my cert>"; } @Override public String chooseClientAlias(String[] strings, Principal[] prncpls, Socket socket) { return "<Alias of my cert>"; } @Override public String[] getClientAliases(String string, Principal[] prncpls) { return defaultKeyManager.getClientAliases(string, prncpls); } @Override public String[] getServerAliases(String string, Principal[] prncpls) { return defaultKeyManager.getServerAliases(string, prncpls); } ... 

So, as you can see, I take in defaultKeyManager, which I set aside for anything other than what I want to override. Then, to use this in your sslContext, follow these steps:

 // clientKeyStore is initialized elsewhere from the SmartCard KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyManagerFactory.init(clientKeyStore, clientKeyPassword.toCharArray()); MyX509KeyManager customKeyManager = new MyX509KeyManager((X509KeyManager) keyManagerFactory.getKeyManagers()[0]); sslContext.init(new KeyManager[] {customKeyManager}, tmf.getTrustManagers(), null); 
+2
source

All Articles