why doesn't java send the client certificate during SSL handshake?

时光总嘲笑我的痴心妄想 提交于 2019-11-26 00:18:13

问题


I\'m trying to connect to a secure webservice.

I was getting a handshake failure even though my keystore and truststore have been set correctly.

After several days of frustration, endless googling and asking everyone around I found out that the only problem was that java chose not to send the client certificate to the server during the handshake.

Specifically:

  1. Server requested a client certificate (CN=RootCA) - i.e. \"give me a cert that is signed by the root CA\"
  2. Java looked into the keystore and only found my client certificate which is signed by the \"SubCA\", which in turn is issued by the \"RootCA\". It didn\'t bother to look into the truststore...duh OK I guess
  3. Sadly when I tried to add the \"SubCA\" certificate to the keystore, that didn\'t help at all. I did check if the certificates get loaded into the keystore. They do, but the KeyManager ignores all certificates except the client one.
  4. All of the above leads to the fact that java decides it doesn\'t have any certificates that satisfy the server\'s request and sends nothing...tadaaa handshake failure :-(

My questions:

  1. Is it possible that I added the \"SubCA\" certificate to the keystore in a manner that \"broke the certificate chain\" or something so that the KeyManager only loads the client certificate and ignores the rest? (Chrome and openssl manage to figure that out so why can\'t java? - note that the \"SubCA\" cert is always presented separately as the trusted authority so Chrome apparently correctly packs it along with the client cert during handshake)
  2. Is this a formal \"configuration issue\" on the server side? The server is a third party. I would expect the server to request a certificate signed by the \"SubCA\" authority since that\'s what they provided us with. I suspect that the fact that this works in Chrome and openssl is because they are \"less restrictive\" and java just goes \"by the book\" and fails.

I did manage to put together a dirty workaround for this, but I\'m not very happy about it so I\'ll be glad if anyone can clarify this one for me.


回答1:


It's possible that you may have imported the intermediate CA certificate into the keystore without associating it with the entry where you have your client certificate and its private key. You should be able to see this using keytool -v -list -keystore store.jks. If you only get one certificate per alias entry, they're not together.

You would need to import your certificate and its chain together into the keystore alias that has your private key.

To find out which keystore alias has the private key, use keytool -list -keystore store.jks (I'm assuming JKS store type here). This will tell you something like this:

Your keystore contains 1 entry

myalias, Feb 15, 2012, PrivateKeyEntry, 
Certificate fingerprint (MD5): xxxxxxxx

Here, the alias is myalias. If you use -v in addition to this, you should see Alias Name: myalias.

If you don't have it separately already, export your client certificate from the keystore:

keytool -exportcert -rfc -file clientcert.pem -keystore store.jks -alias myalias

This should give you a PEM file.

Using a text editor (or cat), prepare file (let's call it bundle.pem) with that client certificate and the intermediate CA certificate (and possibly the root CA certificate itself if you want), so that the client-certificate is at the beginning and its issuer cert is just under.

This should look like:

-----BEGIN CERTIFICATE-----
MIICajCCAdOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJVSzEa
....
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICkjCCAfugAwIBAgIJAKm5bDEMxZd7MA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNV
....
-----END CERTIFICATE-----

Now, import this bundle back together into the alias where your private key is:

keytool -importcert -keystore store.jks -alias myalias -file bundle.pem



回答2:


As an add here, you can use %> openssl s_client -connect host.example.com:443 and see the dump and check that all the main cert is valid against the client. You are looking for this at the bottom of the output. Verify return code: 0 (ok)

If you add -showcerts it will dump all the info of the keychain that was sent along with the host certificate, which is what you loaded into your keychain.




回答3:


The thing is when using client cert signed by an intermediate one you need to include the intermediate one in your trustore so java can find it. Either solo or as a bundle with the root issuing ca.



来源:https://stackoverflow.com/questions/9299133/why-doesnt-java-send-the-client-certificate-during-ssl-handshake

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!