3

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?

Christopher Schultz
  • 20,221
  • 9
  • 60
  • 77
  • `.getHandshakeSession()` only works _during_ a handshake; after that the results are in `.getSession()` as you found. `SSLSession` contains the peer certificate and thus does or would contain a static-DH key, which as Maarten says nobody uses; an ephemeral-DH key is by definition not certified, and is not saved in the `SSLSession`. – dave_thompson_085 Jan 03 '17 at 17:33
  • **Instead of warning** you can cause JSSE to **fail** a connection with too-small DH, or RSA or DSA, with security property `jdk.tls.disabledAlgorithms` set in code or `JRE/lib/security/java.security` q.v. for doc. That's (much?!) less convenient for users, of course. – dave_thompson_085 Jan 03 '17 at 17:42
  • @dave_thompson_085 I don't believe any thresholds can be set for ephemeral keys. All that can be done is set the (one and only) ephemeral key size for the entire JVM. I haven't tried to see what happens if you try to change the ephemeral key size after one connection is closed and another is opened. It may be possible to "detect" that a server is allowing the use of a weak ephemeral DH key size (e.g. 768-bits) but it doesn't appear to be possible to detect the key size *currently* being used (other than by process of elimination). – Christopher Schultz Jan 03 '17 at 17:42
  • 'DH keySize<1024' for example does apply to DHE for me in current Java, although it doesn't work in some older versions I still have to hand; the cutoff appears to be 8u51 or thereabouts. Changing _within_ a process may indeed be an issue; my test programs don't exercise that, and I have seen other JDK code 'freeze' config, e.g. JSSE default truststore can't be changed after first use. – dave_thompson_085 Jan 03 '17 at 18:37

2 Answers2

1

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.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • I'm actually trying to *write* a sniffing tool, not use one. :) So I was wondering if the Java API provided any way to get those values from an established (or currently-handshaking) connection. Using `-Djavax.net.debug` I think does show the information I'd like (I posted this some time ago and haven't gone back recently to investigate), but parsing `stdout` seems like a terrible way to get information from an API. – Christopher Schultz Jan 03 '17 at 16:53
  • Using JSSE to implement a sniffer seems like a terrible way to get information from a TLS connection. – Maarten Bodewes Jan 03 '17 at 17:06
  • The Java code is making the connection itself. I just figured it would be reasonable to be able to get these details of the connection if I can also get details of which cipher suite is in use, etc. Using an external sniffer to determine what is happening with the connection established by *my code* seems silly if I can just modify the code to fetch that information. I'm looking for something I may have missed in the Java API to provide this information from an established connection over which I have complete control. I'm not trying to implement Wireshark in Java. – Christopher Schultz Jan 03 '17 at 17:15
  • 1
    Note the `ephemeralDHKeySize` setting is for JSSE (Java) _server_ only; it is ignored for client, and this Q appears to be about the client. – dave_thompson_085 Jan 03 '17 at 17:39
  • Hmm, interesting stuff. I'll sure keep a good look at how this progresses - if only to aid a possible implementation of TLS 1.3 (or whatever the name will be). – Maarten Bodewes Jan 03 '17 at 18:23
0

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.

Community
  • 1
  • 1
Guenther
  • 2,035
  • 2
  • 15
  • 20
  • The key size of the _data_ cipher is determined by the ciphersuite; the sizes (and values) of DH, ECDH, and RSA/DSA/ECDSA keys used for key exchange and authentication are not, except that the way-old obsolete weak and broken 'export' suites no one should use or allow today set an _upper limit_ on DHE or kRSA size. _Server_ can configure DH key size(s) but Q appears to be for client. – dave_thompson_085 Jan 03 '17 at 17:30