How to check whether a server supports SSL with Java? - java

I need to check whether a server supports SSL and the ciphers from the web server.
I looked into SSLSocket in Java but I do not get it to work properly.
I used the method getSupportedProtocols() it always gives out the same protocols for each url. Moreover I do not get the ciphers that are given from the server. I guessed getEnabledCipherSuites() would be the correct method
try {
SSLContext ctx = SSLContext.getDefault();
ctx.getClientSessionContext().setSessionTimeout(5); // in seconds
SSLSocket socket = (SSLSocket) ctx.getSocketFactory().createSocket("host.com", 443);
socket.setSoTimeout(5000); // in millis
String[] result = socket.getEnabledCipherSuites();
Arrays.sort(result);
for (int i = 0; i < result.length; i++) {
System.out.println(result[i]);
}
} catch (IOException ex) {
System.out.println("Error!");
}
How can I check that the server uses SSL? What ciphers were returned from the server?

getSupportedProtocols()/CipherSuites() returns the lists of protocols and ciphers (respectively) that your side can support. getEnabledProtocols()/CipherSuites() returns the subset of these lists that are enabled on your side.
That won't tell you much about about what the server supports.
According to the TLS specification, the client sends the highest protocol version can use and the list of cipher suites it wants to use. The server then uses the highest protocol it supports within that list (if any) and selects the cipher suite it wants amongst that list (if any).
There's some flexibility regarding how the cipher suite selection is done (see SSLHonorCipherOrder directive in Apache Httpd, for example).
Essentially, if you want to see which cipher suites are actually supported by your server, you'll have to iterate through each cipher suite you support and enable them one by one to try a handshake.

used the method getSupportedProtocols() it always gives out the same protocols for each url.
It always give you the protocols supported by your end. That doesn't change per URL.
Moreover I do not get the ciphers that are given from the server. I guessed getEnabledCipherSuites() would be the correct method.
You guessed wrong. It gives you the ciphers supported by your end.
How can I check that the server uses SSL?
Call startHandshake() and catch the exception.
What ciphers were returned from the server?
You can't, but you can tell what cipher suite was negotiated, by looking in the SSLSession.

Related

SSL requirements on Android for Netty or Socket.io

I've been researching all over the internet for 3 weeks now for how to make Netty enable TLS (any version of it) on Android (at first, I took it on as a challenge). But as of now, I couldn't get it to work, not even once. I have read a LOT of articles, gists and docs about how TLS works, and certificates (inc. self-signed certificates and two-way authentification), key stores, the handshake itself, and even opportunistic TLS, but this is all confusing to me I don't know where else to ask. I need guidance to piece things together.
I just can't put all the pieces together. Here's what I have:
A Twisted server (Python), which doesn't belong to me, will present his certificates to the client. The certificates are issued by Let's Encrypt's global CAs (If you don't know Let's Encrypt, that's fine, all you have to know is that they're globally trusted and also compatible with Android although I don't know what they meant exactly with that). The server does not support self-signed certificates
And a Netty client that establishes bare unencrypted connection with the server (Which works just fine because the server will accept the bare connection as is if opportunistic TLS isn't triggered. The client code (More precisely, the bootstrapping initialization part, which matters the most) is here:
viewModelScope.launch(Dispatchers.IO) {
val group: EventLoopGroup = NioEventLoopGroup()
val b = Bootstrap()
b.group(group) /* Assigning the event loop group to the bootstrap */
.channel(NioSocketChannel::class.java) /* We want a NIO Socket Channel */
.handler(object : ChannelInitializer<SocketChannel>() {
override fun initChannel(ch: SocketChannel) {
val p: ChannelPipeline = ch.pipeline()
/** Adding TLS stuff ? There's literally no good doc on this */
val engine: SSLEngine? = null
engine?.useClientMode = true
p.addLast("ssl", SslHandler(engine))
p.addLast(
"framer",
DelimiterBasedFrameDecoder(8192, *Delimiters.lineDelimiter())
)
p.addLast(StringDecoder())
p.addLast(StringEncoder())
p.addLast(Reader())
}
})
/** The following line works for non-TLS, but for TLS, nothing happens */
val f: ChannelFuture = b.connect(serverHost, serverPort)
}
How exactly do certificates work on Android? Should I create a key store ? or a certificate ? or both ? I don't really understand how to get this done. I heard that Okhttp has SSL built-in, so how does it actually initialize that SSL connection ?

Determine Diffie-Hellman "Parameters" Length for a TLS handshake in Java

I'd like to make an HTTPS connection to a server and, if I'm using
non-ephemeral DH key exchange, I'd like to know what the parameters
are for that connection. Actually, I don't really care if it's
ephemeral or not.
What I'm looking for is the ability to make a connection and then warn
if the connection is using "weak" DH parameters. Is that something I
can check at connection-time? Or is the set of DH parameters (or, more
specifically, the length of those parameters, in bits) defined by
the cipher suite itself?
For example, the Qualys community thread has an illustration of the
cipher suites that SSLLabs considers "weak" (well, everyone considers
them weak... they just have a public tool which complains about them):
https://community.qualys.com/thread/14821
They specifically mention e.g. TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
which is cipher suite 0x9f and mention the DH parameters. Are those
parameters' parameters baked-into the cipher suite (meaning they are
always 1024-bit) or is this a configuration of the server that makes
those cipher suites weak due to the specific DH parameter choice?
In either case, I'd like to be able to sniff that information from the
connection if at all possible. Does anyone know if this can be done,
and how?
I've written some code to attempt to get this information about the handshake, but I keep getting null for the object I was hoping would contain this data.
SSLSocketFactory sf = ...;
Socket sock = new Socket();
sock.connect(address, timeout);
SSLSocket socket = (SSLSocket)sf.createSocket(sock, host, port, true);
socket.startHandshake();
SSLSession sess = socket.getHandshakeSession();
I was hoping that sess at this point would contain some interesting information about the handshake, but it's null. The javadoc for startHandshake indicates that it will notify an event listener when the handshake is completed. So I tried this:
SSLSocketFactory sf = ...;
Socket sock = new Socket();
sock.connect(address, timeout);
SSLSocket socket = (SSLSocket)sf.createSocket(sock, host, port, true);
socket.startHandshake();
// SSLSession sess = socket.getHandshakeSession();
SSLSession sess = socket.getSession(); // This forces the handshake to complete
sess = socket.getHandshakeSession();
... but sess is still null at this point. The "real" SSLSession does exist and gives me information about the connection, but the "handshake session" seems to always be null.
So I tried writing an HandshakeCompletedListener, and I do in fact get an SSLSession, but it appears to be the same one that I can get from the SSLSocket already, so the "handshake" session seems to be unhelpful.
How can I get those parameters from the SSLSession?
Are those parameters' parameters baked-into the cipher suite (meaning they are always 1024-bit) or is this a configuration of the server that makes those cipher suites weak due to the specific DH parameter choice?
No, this is a configuration parameter for the protocol. There is a default of 1024 bits for Java but that may be changed globally for JSSE (the Java TLS implementation) using a system property: jdk.tls.ephemeralDHKeySize. Best set this during startup with a -D option for the Java VM.
For static DH key pairs (that are used for authentication) you would have to look into the DH certificate. But I don't think you'll find any, everybody uses RSA for authentication.
In either case, I'd like to be able to sniff that information from the connection if at all possible. Does anyone know if this can be done, and how?
Well, for sniffing tools such as WireShark would suffice. Undoubtedly you can parse things like DH parameters from a TLS connection (if they are used in the first place of course).
You can also debug connections using -Djavax.net.debug
For Java applications / libraries you could look up the cipher suite and then, if it contains DHE_ look up the aforementioned system property (keeping in mind its default values).
The Java JSSE API was not written with deep packet inspection in mind. It's (literally) a service oriented implementation for servers and client applications. Although you could of course use the OpenJDK code itself (it's GPL'ed, right?) you are better off using a separate implementation, possibly with an even more permissive license.
For a sniffer however I would rather use C/C++ (or at least a C/C++ frontend) than Java.
For most cipher algorithms, the length is determined by the name cypher name, as also mentioned here How to get the actual block cipher key size for Java SSL connection _in code_? .
Instead of trying to warn people when they are using unsecure cyphers, I'd recommend to disable those ciphers by selecting only the cyphers you want to support. You can do this on a jvm level or on the SSLSocket, e.g.
String pickedCipher[] ={"TLS_RSA_WITH_AES_128_CBC_SHA"};
socket.setEnabledCipherSuites(pickedCipher);
You can also set the desired key size, see here
https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#customizing_dh_keys
You can see defaults and classes used in Java security here https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html
If you are curious and want to investigate this in more detail, I'd recommend to turn on ssl logging, as described here.

OkHttp 2.0 manually verifying cipher suite in use

I am trying to work out a way to check the Cipher Suite (CS) that has been chosen during the TLS handshake. I want to be able to manually kick out of the handshake, or at least stop the body of the http message being sent if the CS fails some manual check.
I am requesting a subset of CSs using a custom SocketFactory but this is not always honoured by the server.
I have played with doing this inside the HandshakeCompletedListener callback but this is proving difficult. Throwing any RuntimeException class will not do anything due to the catch all in the calling method. Closing the socket wont make any difference either, which i thought may internally throw an exception, which would not be a great solution anyway.
Any help would be great.
EDIT: after #jesse-wilsons answer below I am using 2.1-RC1 and the ConnectionSpec class to specify what Cipher Suites (CS) I want to use - however there are cases when an existing SSL session can be reused with a differnt cipher suite. I have included a custom HostNameVerifier to catch any cases where an SSL connection is trying to be setup with a differnt than requested CS. This is a little bit of a shoehorn but I cant see another way to hook in after the SSL handshake has taken place.
public class HostnameAndCipherSuiteVerifier implements HostnameVerifier
{
private final String mHostname, mCipherSuite;
public HostnameAndCipherSuiteVerifier(String hostname, String cipherSuite)
{
mHostname = hostname;
mCipherSuite = cipherSuite;
}
#Override
public boolean verify(String hostname, SSLSession session)
{
//delegate to original hostname verifier
if(!OkHostnameVerifier.INSTANCE.verify(hostname, session))
{
XLog.w("delegate hostname check failed");
return false;
}
//hardcode check for domain
if(!hostname.equals(mHostname))
{
XLog.w("Hard-coding hostname fail");
return false;
}
//handshake cipher check
if(!session.getCipherSuite().equals(mCipherSuite))
{
XLog.w("cipher check failed");
return false;
}
else
return true;
}
}
Grab OkHttp 2.1-RC1 and use the new ConnectionSpec class to limit the cipher suites to those you like.

How to specify the ciphersuite to be used in SSL session

I have created a socket on port 443 as in the following line:
socket = (SSLSocket) factory.createSocket(hostName, port);
Then, I wanted to see the enabled ciphersuites in this socket, I used:
String[] enCiphersuite=socket.getEnabledCipherSuites();
System.out.println("Enabled ciphersuites are: "+Arrays.toString(enCiphersuite));
Then, I want to pick only one ciphersuite that I want my application to use when creating handshake with the remote server. I did the following:
String pickedCipher[] ={"TLS_RSA_WITH_AES_128_CBC_SHA"};
socket.setEnabledCipherSuites(pickedCipher);
System.out.println("ciphersuite set to: "+Arrays.toString(pickedCipher));
Then I made the handshake, and checked the session ciphersuite:
socket.startHandshake();
System.out.println("Session ciphersuite is"+socket.getSession().getCipherSuite() );
But I found that the name of the cipher printed in the previous printout statement after the handshake (as I understand, this is the actually used cipher in the session) is not what I set earlier using setEnabledCipherSuites()
Why am I still not see my chosen ciphersuite is the used one ? and also, I also tried to getEnabledCipherSuites() and print it out after I setEnabledCipherSuites and found the list has not changed to what I have set. I am not sure when I print the enabled ciphersuite, is this list of ciphersuites depends on Java and always the same list, or depends on the client or on the server? Can any body explain ?
EDIT:
Before the handshake I only have the following lines:
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
SSLSocket socket=null;
try {
socket = (SSLSocket) factory.createSocket(hostName, port);
socket.setSoTimeout(15000);
socket.startHandshake(); //handshake
.
.
I found out that I added socket.getsession() before the
setEnableCipherSuite() in order to print out the enabled cipheres
before setting them. When I removed it, the cipher has been set. why
is that ?
As documented in the SSLSocket JavaDoc:
The initial handshake on this connection can be initiated in one of
three ways:
calling startHandshake which explicitly begins handshakes, or
any attempt to read or write application data on this socket causes an implicit handshake, or
a call to getSession tries to set up a session if there is no currently valid session, and an implicit handshake is done.
If you call getSession() before calling setEnabledCipherSuite(), the handshake has already been done when you try to set the enabled cipher suites, so this session's cipher suite has already been selected.

Failure in SSL library on using cipher

I have created a ssl socket connection with my server. The server sends me the RC4 key and i use the key to create a cipher and bind it to the input and output streams of the existing socket. I am getting the following error on trying to read from the input stream:
javax.net.ssl.SSLProtocolException: Read error: ssl=0x32fbf8: Failure in SSL library, usually a protocol error
Is it possible that the decryption is not working properly or the RC4 key cipher is not correct. what are the reasons for such an error. I am doing it in an app on android 2.3.3.
One more query, does android 2.3.3 support sslv23(openssl) . If so how can instantiate the same ?(In windows client, i set the session context with the rc4 key and it works perfectly fine)
I am new to java and android and come from VC++ background.
Experts and programmers please enlighten me on my queries. My code is as follows:
sslContext = SSLContext.getInstance("TLS");
/* some code to initialize ssl context */
SSLSocketFactory sslSocketfactory = sslContext.getSocketFactory();
sock = (SSLSocket) sslSocketfactory.createSocket(host,port1);
sock.setSoTimeout(12000);
is = new DataInputStream(new BufferedInputStream(sock.getInputStream(),
16384));
os = sock.getOutputStream();
/* some more code using above mentioned is and os to recieve rc4 key and
write it into a byte array */
SecretKey key2 = new SecretKeySpec(reverserRc4InBytes, 0, reverserRc4InBytes.length, "RC4");
Cipher cipherOS = Cipher.getInstance("RC4");
cipherOS.init(Cipher.ENCRYPT_MODE, key2);
Cipher cipherIS = Cipher.getInstance("RC4");
cipherIS.init(Cipher.DECRYPT_MODE, key2);
cos = new CipherOutputStream(os,cipherOS);
cis = new CipherInputStream(is,cipherIS);
From what I understand, you're first making an SSL/TLS connection to exchange the RC4 key somehow, and then you're using it to encipher and decipher the result, instead of letting the SSL/TLS stack do it all for you. (It's clearly unnecessarily convoluted, and it's not clear how secure this is, since SSL/TLS provides you with more than encryption, in particular with integrity.)
The server is implemented in C and using an existing SSL stack ..
sslv23 to be specific.
SSLv23 isn't really a "stack" (by "stack" I mean an implementation: JSSE, OpenSSL, .Net's Security API, ...). SSLv23 usually refers to SSLv3 where the initial Client Hello message is wrapped in SSLv2 format. This wrapping happens on the client side, for clients that supports both SSLv3 and SSLv3/TLSv1.x. Which versions the server support should be fixed, in particular, you shouldn't need to use that trick if your server supports SSLv3 and above. (Note that the JSSE supports the v2 wrapped Client Hello format, but doesn't actually support SSLv2. I guess this is also the case for Android.)
javax.net.ssl.SSLProtocolException: Read error: ssl=0x32fbf8: Failure
in SSL library, usually a protocol error
This indicates that something incompatible is happening between your server and the client. There can be a few reasons for this:
Your server only supports SSLv2 (not 3). Let's assume your server supports at least SSLv3 (you can check whether the handshake completes using Wireshark, for example.)
There's a more general problem with the SSL/TLS implementation on the server. (You could attempt to connect to it using other tools, such as openssl s_client, at least to see if the connection can be established.)
Your key exchange protocol expects the SSL/TLS connection to end there, while leaving your manual handling of the connection on a plain TCP socket afterwards. You may have to use your Socket as an SSLSocket only during the section where SSL/TLS is used and revert back to the plain Socket afterwards. ()
You can try to establish a plain socket to your server, upgrade it to an SSLSocket using createSocket (Socket s, String host, int port, boolean autoClose) with autoClose=false and get the I/O from the plain socket to do your manual encryption.
I would assume that this should have caused other problems on the SSLSocket side, especially when the server closes its side of the SSL/TLS connection, though. This is just a guess, this approach might work.
Anyway, I don't think the problems you're seeing have anything to do with your using Cipher manually from your SSLSocket's I/O streams, since the exception happens at the underlying layer, which should be hidden as far as the data you read/write there is concerned.
I don't think you have understood your assignment properly. Either you should be using SSL or you need to implement this home-grown cipher.
Or else you should look for a saner job ;-)

Categories