Reputation: 2977
It's appeared that we can't configure our java client to correctly handle SSL connection.
We can successfully connect to HTTPS URL using browser with specified client certificate and with curl tool but not with our java client or SOAP UI tool that is also java based.Even when we used the same *.p12 certificate in browser (which works fine) and in the SOAP UI tool (doesn't work).
So, We have following information:
We converted these pem certificates to jks and to p12 and used apache http client for connection. And as it was mentioned the same certificate is working for browser but doesn't work neither for custom client or for java based for SOAP UI.
First of all I guessed that we have following problem described there:
When the server requests a client certificate (as part of the TLS handshake), it will also provide a list of trusted CA's as part of the certificate request. When the client certificate you wish to present for authentication is not signed by one of these CA's, it won't be presented at all
But it seems if we had such problem It wouldn't resolve this certificate for browser.Because browser by default doesn't allow to use incorrect (with different issuer) certificate by default.
Client I created based on following tutorial:
So, basically I have to questions:
We tried to use openssl s_client tool. The output showed that server certificate is as we expected, but there are no CA for client certificate validation:
No client certificate CA names sent
The syntax for openssl s_client if using connection to 443 without specification of the schema (https://). So we decided to try our url in browser without the schema but with the 443 port. Server responded with:
The plain HTTP request was sent to HTTPS port
Next our step in this investigation was to try wireshark. First we got the correct session with browser and then we tried our application. We tried openssl s_client as well.
Difference is in the Server Key Exchange packet. While browser packet has a Certificate Request, but openssl and our application has no such field in that packet. Also if browser doesn't send any certificates this field is still present in the packet.
So only browser gets a request for certificate. Any thoughts about why this is happening are appreciated.
openssl s_client -connect your.server.name:443
we get a valid server certificate back and No client certificate CA names sent. Also we get verify error:num=2:unable to get issuer certificate.After some investigation and research we manage to find out the root cause of the problem. So we have SNI (Server Name Indication) enabled on our server and our application is using apache httpclient 4.2.1. This version of client doesn't support the SNI extension and doesn't send the server_name extension in Client Hello packet. After that server doesn't advertise client to authenticate with client certificate. This issue was fixed in 4.3.2 release.
Right now we are trying to check if the 4.3.2 version of httpclient is correctly sending server_name extension.
Upvotes: 3
Views: 6532
Reputation: 809
Finally the solution for this problem was found.
So preconditions for this problem to happen are:
Root cause of the problem is in the old version of httpclient was used. Clients before the version 4.3.2 are not capable of dealing with SNI (Server Name Indication) and don't send necessary information in Client Hello packet (field name: server_name). Here is a issue link.
Basically SNI is used to run multiple virtual domains on the same ip-address using different server certificates for each domain. When SNI enabled server is getting Client Hello packet without server_name then it can't determine what certificate it has to use and therefore is not advertising the client with CA list.
Solution is to use Java version >= 7 and apache httpclient version >= 4.3.2.
Also wireshark is really helpful for debugging such issues and the filter example to use with it are posted by juhraffe:
ssl && (ip.dst == ip_or_domain_of_server || ip.src == ip_or_domain_of_server)
Also while using it don't forget to make wireshark collect packets on correct interface.
Upvotes: 2
Reputation: 555
I'd suggest doing a packet capture (e.g., Wireshark or tcpdump) while sending your request. In Wireshark you can filter on "SSL" and the source and destination IP addresses (SSL && ip.src==X.X.X.X && ip.dst==X.X.X.X) to isolate the traffic of interest, and then look for the following sequence of SSL Handshake messages in the "Info" column (or something similar):
Client -> Server: Client Hello
Server -> Client: Server Hello, Certificate, Certificate Request, Server Hello Done
Client -> Server: Certificate, Certificate Verify, Client Key Exchange
The key things to look for are the Certificate Request (make sure the server is asking for the certificate, and check the list of trusted CAs), and then the Client -> Server Certificate message, which includes the client's certificate.
Other things you can check in the packet capture:
* The list of supported cipher suites by the client. If the server does not accept any of them the connection will fail.
* The TLS version. The client and server must support a common version for communication to take place. It appears you restricted to TLS v1.0, so check that this is supported on the other end.
* Other "Alert" messages, which indicate a fatal error or a warning.
Upvotes: 3