How to specify the ciphersuite to be used in SSL session - java

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.

Related

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.

How to check whether a server supports SSL with 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.

I am trying to create sockets with different URLs. How can i handle failed connections?

I have a database in which there are different hosts on which i want to connect to through SSL. Because the hosts sometimes are random and often invalid i get an error at the point where the socket is created. I don't know how i can handle this problem
for(int i=connect.getMinId();i<connect.getMaxId();i++){
System.out.println(connect.getHost(i));
SSLSocket socket = (SSLSocket) factory.createSocket(URL, 443);
socket.startHandshake();
SSLSession session = socket.getSession();
}
Create an unconnected Socket with new Socket().
Connect it with a timeout using Socket.connect(address, timeout). You can choose your own timeout up to the platform default. Note that the Javadoc incorrect says that 0 means an infinite timeout. It doesn't: it means the platform default, which is around a minute, dependent on the, err, platform. This method throws SocketTimeoutException if the timeout expires.
Wrap it in an SSLSocket with SSLSocketFactory.createSocket(Socket, host, port, true). Note that you have to re-specify the host and port: this is for SSL session sharing purposes; it doesn't create a new connection.
Get the session, or start the handshake. Note that getting the session starts the handshake, you don't have to do both. If you're not interested in the server certificate you don't really need to do this step at all.
Catch all IOExceptions and close the socket.

handshake.getPeerCertificates() null pointers on a two way handshake

After my handshake is complete I call getPeerCertificates() on handshake (socket.getHandshakeSession()) I am using setNeedClientAuth(true) on the server socket so I know it must be getting the clients cert.
What could be going wrong? SSLPeerUnverifiedException is not thrown
ssf = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
serverSocket = (SSLServerSocket)ssf.createServerSocket(PORT);
serverSocket.setNeedClientAuth(true);
//added to after accept list and picked up by a worker thread.
SSLSocket socket = (SSLSocket) serverSocket.accept();
//worker thread.
SSLSession handshake = socket.getHandshakeSession();
Certificate[] certificates = handshake.getPeerCertificates(); // NULL
When you call socket.getHandshakeSession(), you haven't started the handshake yet, and you certainly haven't reached the point where the client certificate is exchanged. Although calling getSession() is one way to start the handshake (see introduction of the SSLSocket documentation, no such thing happens with getHandshakeSession() (as the documentation says: "Unlike getSession(), this method does not initiate the initial handshake and does not block until handshaking is complete.").
In most cases, you'll want to get the client certificate once the handshake has completed anyway. Calling socket.getSession() instead of socket.getHandshakeSession() (an addition in Java 7) is likely to solve your problem.

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