How can I reduce the waiting time for ssl handshake? - java

I have the following code from http://sanjaal.com/java/tag/get-public-key-of-ssl-certificate-in-java/ that creates a socket and initiate ssl handshake.
SSLSocketFactory factory = HttpsURLConnection
.getDefaultSSLSocketFactory();
System.out.println("Creating a SSL Socket For "+hostname+" on port "+port);
SSLSocket socket = (SSLSocket) factory.createSocket(hostname, port);
socket.startHandshake();
System.out.println("Handshaking Complete");
The problem is that if the handshake could not establish, there is a long waiting time before I see the exception: Connection timed out
How can I set a maximum waiting time ? and what is the suitable minimum amount of time that ensures me that ssl handshake is not going to be successful.

'Connection timeout' has nothing to do with the handshake, or even SSL. It doesn't happen when you call startHandshake(): it happens when you create the socket. Your stack trace would have told you that.
To reduce it, create a Socket() using the default constructor; call connect(SocketAddress address, long timeout); then if that succeeds call SSLSocketFactory.createSocket(Socket, ...) to wrap it in an SSLSocket.
NB the blog you cited is very poor quality indeed. Some of the problems with it:
An SSL certificate does not 'enable encryption'. It is there primarily for authentication. It also plays a minor, inessential role in the handshake leading to the session key.
TLS offers a lot more than just 'prevent[ing] eavesdropping, tampering, and message forgery': it also prevents replay and man-in-the-middle attacks too, for example; and it offers many more encryption possibilities than just RSA with 1024 or 2048 byte keys.
The suggestion that 'At the browser level, it only means that the browser has validated the server’s certificate', and that for security the user/application must 'cipher something using the public key contained in the certificate and assure that the server can understand it' is complete nonsense. The browser has already checked that the server owns that certificate, via the digital-signature mechanism, during the SSL handshake.
All this: 'the “locked padlock” icon has no relationship to the URL, DNS name or IP address of the server – thinking otherwise is a common misconception. Such a binding can only be securely established if the URL, name or address is specified in the server’s certificate itself. ' is completely incorrect. HTTPS includes checking the hostname against what is embedded in the certificate: and what is embedded in the certificate is put there and checked by the signing authority.
Don't rely on unrefereed unreviewed sites like that. Use the official product documentation.

Before performing sslSocket.startHandshake(); use sslSocket.setSoTimeout(timeout);
setSoTimeout is an read timeout so the timeout value which you provide in setSoTimeout will work with handshake also.

Related

Apache HttpClient for Java Specific GET instead of CONNECT

Is there a way to specify the connection method when using a proxy?
I'm using the below code which sends an HTTP CONNECT. That is not supported by my load balancer. A GET request would terminate the TLS connection between the proxy and the website. The CONNECT method creates a TLS connection end to end between the end user and website. Essentially I need to inspect the traffic at the proxy.
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
clientBuilder
.setConnectionManager(connectionManager)
.setProxy(proxy)
.setDefaultRequestConfig(config);
Below is what the connection looks like:
Hypertext Transfer Protocol
CONNECT xyz.com:443 HTTP/1.1\r\n
Host: xyz.com\r\n
User-Agent: Apache-HttpClient/4.5.13 (Java/19.0.1)\r\n
\r\n
For HTTPS communications through a proxy, the client will use a CONNECT request to create a connection through the proxy to the remote trusted site.
With HTTPS, the client will only communicate with a server that it trusts. The trust usually requires that the server presents an X.509 certificate that is signed by a trusted authority (not self-signed), not expired, and has a subject that matches the site the client is trying to reach.
When communicating with a proxy, the proxy is typically unable to match that criteria. However, there are proxies that can match the above criteria by generating a new cert on the fly that has a matching subject to that expected by the client.
In that case, once the client has established a TLS connection with the proxy, the proxy can then decrypt the payload of the HTTPS traffic.
For the proxy to create a certificate trusted by the client, it must be signed by a CA (certificate authority) that is trusted by the client. Now, by default, the client will not trust the cert since it will not recognize the CA. However, you may modify the client by placing a cert for the CA on the client. Once the client trusts the CA, it will trust all of the certs generated by the proxy.
That being said, here's configuration for an HTTPS proxy (written by Cisco) that provides instructions for just that:
https://www.cisco.com/c/en/us/support/docs/unified-communications/jabber/212044-Configure-Charles-Proxy-to-Capture-HTTPS.html
The configuration action you'll need for Apache (if Apache supports this) will be similar - you'll need to get a cert from the Apache proxy to install on your client.
I'm currently searching the web for similar proxies. I'm essentially looking for a key word that describes this technique of enabling your HTTPS proxy to do man-in-the-middle attacks with a dynamically generated cert matching the request. If I find the terms, I'll update the post.
EDIT: Update for squid
I found an article about configuring squid for inspecting HTTPS traffic: https://scubarda.com/2020/03/23/configure-squid-proxy-for-ssl-tls-inspection-https-interception/
In the article, the author indicates that you would have to install the X.509 Cyber Saiyan certificate in your browser (client's) certificate/authorities section (but this is just for this specific configuration).
Some good keywords to search for now are HTTPS inspection...
If this is a temporary debug solution, you can use one of the Man In The Middle (MITM) proxies that are designed just for this:
Burp
Charles
Zap
Once installed somewhere, you'll just need to import the proxy CA cert into the server making the connections (each of the above proxies has a how-to guide for this), and set the proxy address to the MITM.

In Google App Engine is it possible for getScheme() on a ServletRequest to return "https" when isSecure() returns false?"

In Google App Engine is it possible for getScheme() on a ServletRequest to return "https" when isSecure() returns false?"
This is a follow up question to this.
I think this question is missing a little of the context you added on the other question, since your concern seems to be certificate errors I think an appropriate answer would be:
Yes getScheme() will return https even in the case of certificate errors, but only (of course) if the client opted to accept a server certificate that had errors (untrusted, expired, etc) during the SSL handshake. For instance, the client may have used the curl -k flag.
In your original question, you mentioned:
[if getScheme() returns "https"] is it guaranteed that a HTTPS connection has been established and there were no certificate errors?
I think there's a flawed assumption in your question, that a certificate error means that an HTTPS connection is not established. In the case of certificate errors (untrusted issuer, expired certificate, etc), there are two possibilities:
The client terminates the SSL handshake (no request will arrive to be processed by your application).
The client has explicitly opted to trust this certificate, or it opted to ignore expired certs and other errors (curl -k). In this case, an https connection is established and the request will proceed. Your app will of course see "https" returned by getScheme() because an https connection was established. From the point of view of the client though, it may not be a trustworthy connection.

Connect to FTPS through proxy

The initial problem we encountered was that a regular FTPs download started failing due to an untrusted server certificate. This prompted us to wonder whether the certificate had been updated without the counterparty notifying us so we wanted to download the current certificate and compare it to the one we have in our keystore.
This seems to be a trickier problem than we had anticipated. The usual suspects (firefox, filezilla, ...) did not seem up to the task of connecting to an FTPs server through an FTP proxy so out of curiosity I started playing around with a more low level java approach. I can not for the life of me get it to work though.
First (overly simplistic) java attempt:
// create proxy connection
SocketFactory plainFactory = SocketFactory.getDefault();
Socket proxy = plainFactory.createSocket(proxyServer, proxyPort);
// create ssl connection on top of it?
SSLSocketFactory sslFactory = getSocketFactory();
SSLSocket socket = (SSLSocket) sslFactory.createSocket(proxy, server, port, true);
This approach obviously does not work.
Next I started playing around with ftp4j (http://www.sauronsoftware.it/projects/ftp4j/) it seems to have a clean and accessible codebase:
FTPClient client = new FTPClient();
client.setConnector(new FTPProxyConnector(proxyHost, proxyPort));
client.getConnector().setConnectionTimeout(0);
client.getConnector().setReadTimeout(0);
client.setSSLSocketFactory(getSocketFactory());
// also tried SECURITY_FTPS
client.setSecurity(FTPClient.SECURITY_FTPES);
client.connect(server, port);
This outputs:
REPLY: 220 Blue Coat FTP Service
SEND: USER anonymous
REPLY: 530-User Access denied.
REPLY: 530-
REPLY: 530-Usage: USER username#hostname
REPLY: 331 PASS userpassword
Exception in thread "main" java.io.IOException: Invalid proxy response
The proxy server has optional authentication and on our development servers we generally use "user#host" without proxy authentication. As such I assume the username, hostname and password are those of the remote server?
So I tried adding the remote parameters, this does not work:
REPLY: 220 Blue Coat FTP Service
SEND: USER test#ftps.example.com
Exception in thread "main" java.io.IOException: FTPConnection closed
Adding the proxy user to match the bluecoat format does not seem to work either:
USER %u#%h %s
PASS %p
ACCT %w
Any help with either of these two problems would be most welcome:
how to retrieve the server certificate from an ftps server through an ftp proxy
how to connect to an ftps server through an ftp proxy in java
You might want to try Apache Net commons libs.
Here is a similar thread that uses that Net Commons library
Net commons also has a fully functional FTP Client Example so you can test with something you know works.

The SSL Cert is bound to the actual host name. If you have an SSL cert for "qa.example.com", it won't work on your machine named "dev.example.com"

I want to understand the below line
"The SSL Cert is bound to the actual host name. If you have an SSL cert for "qa.example.com", it won't work on your machine named "dev.example.com".
Questions:
How is possible to create a cert bound to a hostname?
I have a client and server model. The server is not validating the client during communication. So far I had ONE set of key and certs which is installed on multiple servers and the communication works fine.
Now we have new set of certs which is bound to a hostname. Each server has a set of certs.
When swapped the communication does not work. "Handshake failure" obtained.
What's happening here? Who validates the hostname?
The client validates the certificate returned by the server. Among the various verifications performed the client checks that the hostname embedded in the certificate matches the hostname of the server. If the hostname doesn't match the handshake fails.
This means that if your certificate was issued for qa.example.com, it won't work on dev.example.com. For the same certificate to work for both hostnames you'll need a wildcard certificate (issued for *.example.com).
The hostname is in the certificates, you need to generate new certificates with different hostnames. The certificate names is validates by the client when it attempts to establish a secure connection.

Question on ssl handshake and behavior in java

I am using https to connect to an https server.
Specifically I am using apache httpclient and I configure the ssl context to use my keystore and truststore.
The https server I am using is IIS7 and is configured to require client authentication.
I think I have set it up properly.
Anyway, if I configure the httpClent's ssl context with a keystore (i.e. with client certificates) valid for IIS then there is no problem connecting.
Now my problem is the following:
If I do not configure the ssl context with any client certificate to send to IIS, there is no connection with the server. What makes me think though, is the fact that I was expecting to see some java exception in the code as a result of a hanshake failure alert.
Monitoring what is happening with wireshark, I could not see a certificate request from IIS to my application, but I noticed that after ServerHelloDone everything was encrypted.
I did not expect that. I think the handshake is usually in cleartext.
I used private key to decrypt traces and i saw a certificate request from IIS but after many starting and opening of new connections.
My app send back as a response a certificate of length 0 and IIS replies with a TLSv1 Finished.
After that the packets stop (i.e. seems that the communication ends).
I was expecting a handshake alert.
My question is, is this how it is supposed to work or at least how IIS works?
Or if I do not see the alert something is wrong with my use case?
Thanks
It sounds like IIS is only requiring client certificates for certain URLs (ie, for example.com/foo, but not example.com/bar).
In the initial handshake, it does not know which url you are requesting, so it does not require a certificate. When it sees that you are requesting a restricted resource (/foo), it then rehandshakes, requiring a certificate.
However, I would still expect a handshake_failure to occur.
As I was saying in an answer to this question, as far as I remember, IIS uses re-negotiation to get the client certificate. You should be able to change this behaviour using netsh and clientcertnegotiate=enable (depending on the version of IIS you're using).
You might also be interest in this similar question.
Failing to supply a certificate in response to a CertificateRequest isn't an SSL protocol error, so there is no handshake_error. 'Requiring' instead of just 'needing' client certificates is added-in by SSL libraries, and all they can do if you don't send one is just close the connection.

Categories

Resources