Why does Java's SSLSocket send a version 2 client hello? - java

The SSLSocket.getEnabledProtocols() method returns the following: [SSLv2Hello, SSLv3, TLSv1]. And indeed, when I call connect() and I have SSL debugging turned on, I see that a v2 client hello is used:
main, WRITE: TLSv1 Handshake, length = 81
main, WRITE: SSLv2 client hello message, length = 110
But I have found two (admittedly old) references that say JSSE does not support SSL version 2:
From Fundamental Networking in Java:
'SSLv2Hello' is a pseudo-protocol which allows Java to initiate the handshake with an SSLv2 'hello message'. This does not cause use of the SSLv2 protocol, which is not supported by Java at all.
And from the JSSE Reference Guide:
The JSSE implementation in the J2SDK 1.4 and later implements SSL 3.0 and TLS 1.0. It does not implement SSL 2.0.
Now, my understanding is that version 2.0 client hello should only be sent when the client does support SSL version 2.0. From RFC 2246:
TLS 1.0 clients that support SSL Version 2.0 servers must send SSL
Version 2.0 client hello messages [SSL2] ... Warning: The ability to send Version 2.0 client hello messages will be phased out with all due haste.
So why does Java use it?

Sun's JSSE doesn't support SSLv2 but it supports the SSlv2ClientHello, to support some SSL servers that require it. You can turn it off by removing it from the enabled protocols.
IBM's JSSE does support SSLv2 entirely.
From the JSSE Reference Guide:
For example, some older server
implementations speak only SSLv3 and
do not understand TLS. Ideally, these
implementations should negotiate to
SSLv3, but some simply hangup. For
backwards compatibility, some server
implementations (such as SunJSSE) send
SSLv3/TLS ClientHellos encapsulated in
a SSLv2 ClientHello packet. Some
servers do not accept this format, in
these cases use setEnabledProtocols to
disable the sending of encapsulated
SSLv2 ClientHellos.
I imagine 'server implementations' should read 'SSL implementations' above.
EDIT: thanks for citing my book!

I faced the same issue, we had this error on our RCP application, it was trying to access our nginx which only handle TLS protocols. but openjdk which we use has a bug with TLS which in some case will end in: javax.net.ssl.SSLException: Received fatal alert: bad_record_mac. So i was trying to find a way to use a different protocol to do the handshake like SSLv3. At first I though that SSLv2Hello is the handshake protocols! but it is not!
SSLv2 never implemented in sun jdk or openjdk, this SSLv2Hello is not the actual protocol for the handshake, it is there for backward compatibility(my guess is because of the probability of existence of some servers) and it will be used for negotiation on the available protocols that will be used for the actual handshake!
on this link search for the answer from Stoinov, he wrap the answer very well.

Related

Can I use TLSv1.3 ciphers in TLSv1.2 session?

Windows IIS Server 10 supported TLS_AES_256_GCM_SHA384 on TLS 1.3.
But my application max TLS version TLS 1.2 . I can't send request to server due to tls mismatch.
TLS_AES_256_GCM_SHA384 is not found in my app. How can I use this CipherSuite ?
Note : My android project language is Java.
The TLSv1.3 approach to ciphers isn't directly compatible with TLSv1.2, because the key exchange and signature is now treated separately (which means you can't just use a TLSv1.3 cipher in TLSv1.2 and conversely).
You can though pick equivalent ciphers for TLSv1.2 that match the functionality of TLSv1.3. All the options are listed on the IEEE site here.
If however all you are looking for is a strong set of options that support both TLSv1.3 and TLSv1.2, then I'd recommend taking a look at the Mozilla SSL configuration generator, which makes the process trivial to follow.

java 1.6 TLS1.2 support using proxy nginx/ squid solution issues

I have a legacy java web application that makes calls to an external webservice. The provider of that service is turning off TLS1.0 support. So, I am trying to see how the application can continue to work with the service.
The options I have seen are a) use BouncyCastle JCE instead of Java JCE http://boredwookie.net/index.php/blog/how-to-use-bouncy-castle-lightweight-api-s-tlsclient/, which I guess requires code change/ recompile (we don't have the luxury of doing it) or 2) use proxy servers https://www.reddit.com/r/sysadmin/comments/48gzbi/proxy_solution_to_bump_tls_10_connection_to_tls_12/
I have tried nginx proxy - it doesn't seem to handle the switch between TLS1.0 incoming and TLS1.2 that the end server expects.
server { listen 443 ssl; server_name proxy.mydomain.com;
ssl_certificate D:/apps/openssl/proxy.mydomain.com.cert;
ssl_certificate_key D:/apps/openssl/proxy.mydomain.com.private;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP;
ssl_prefer_server_ciphers on;
location / {
proxy_pass https://fancyssl.hboeck.de/;
}
This fails with a 502/ bad gateway error since https://fancyssl.hboeck.de only support TLS1.2 but works with https://www.google.com that supports TLS1.0.
I am doing this on Windows.
It's not TLSv1.2, it's lack of SNI leading to renegotiation.
First, I set up nginx (1.8.1/Windows) with a config like yours except using my own key&cert and proxying to my own test server. It worked fine, connecting from Java6 requester with TLSv1.0 and to server with TLSv1.2 (and even ECDHE-RSA-AES256GCM-SHA384, one of the 'best' ciphersuites) and returned pages just fine. I tried fancyssl.hboek.de and got 502 like you.
With wireshark I saw that nginx does not send SNI (by default) and at least using the IPv4 address 46.4.40.249 (I don't have IPv6) that server apparently hosts more than one domain because without SNI it provides a different (and expired!) certificate, for *.schokokeks.org, and after the first application data (the request) it sends an encrypted handshake (a renegotiation request -- which nginx does not honor). Testing with openssl s_client confirms that the server with SNI immediately sends the page but without it renegotiates first; repointing nginx to openssl s_server confirms that if the server requests renegotiation, receives no response, and closes nginx treats that as 502.
I would guess that Apache is renegotiating because it realizes the requested Host is not covered by the certificate -- except that it again uses the 'wrong' certificate. I haven't tried to track that part down.
Google does support TLSv1.2 (and ECDHE-RSA-AESGCM) when I connect, but even without SNI doesn't renegotiate, presumably because it's such high volume nothing else runs on www.google.com servers and there's no ambiguity. My test server doesn't have vhosts so didn't need SNI.
The nginx documentation reveals a directive proxy_ssl_server_name which can be set on to enable SNI, and then proxying to this server works.
FYI: several of the statements on that webpage are wrong, although its conclusion (if possible use TLSv1.2 with ECDHE or DHE and AES-GCM) is good.
Also, most of your ssl_ciphers string is useless, but you didn't ask about that.
ssl_ciphers HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP
HIGH is an excellent start.
SEED is useless in a server used (only) by Java/JSSE client, because it's not implemented on the Java side. Even outside of Java it was pretty much used only in South Korea, where it was created as an alternative to DES or IDEA, and even there it is mostly obsoleted by ARIA which is an alternative to AES -- but is not implemented by OpenSSL and hence nginx.
aNULL is probably unneeded because JSSE disables 'anonymous' suites by default, but here it's worth it as defense in depth.
!eNULL does nothing; no eNULL suites are in HIGH, or DEFAULT, or even ALL. You can only get them explicitly or with the bizarre COMPLEMENTOFALL -- which you shouldn't.
!EXPORT !DES !RC4 do nothing; none of them are in HIGH. If instead you started from DEFAULT on older versions of OpenSSL, or from ALL, then they would be good.
!PSK is unneeded; nginx doesn't appear to configure for PSK and JSSE doesn't implement it anyway.
!RSAPSK is ignored because OpenSSL doesn't implement that keyexchange, and if it did those suites are already covered as above.
!aDH !aECDH are covered by !aNULL and thus do nothing.
!EDH-DSS-DES-CBC3-SHA is silly; there's no reason to exclude this one suite when you keep other DHE_DSS and 3DES suites.
!KRB5-DES-CBC3-SHA is ignored because OpenSSL doesn't implement Kerberos, and if it did nginx wouldn't be configured for it plus again it would be silly to exclude one suite while keeping similars.
!SRP is unneeded; like PSK nginx apparently doesn't configure and JSSE doesn't implement.
So: HIGH:!aNULL is all you need.

Modifiying Weblogic Server to use TLS

I have a VM running a Weblogic Server (running version 10.3.6) with 2 nodes. I also have a Tomcat server running on my host machine which runs an SSL web service, that the Weblogic Server has to connect to. I added the two startup parameters to the "Arguments" text area under startup:
-Dweblogic.security.SSL.protocolVersion=TLSv1.1
-Dweblogic.security.SSL.minimumProtocolVersion=TLSv1.1
I added these since the nodes were trying to connect using SSLv2 before, and causing a handshake error with Tomcat.
After adding these parameters, I still see the nodes trying to connect to Tomcat using SSLv2. I'm trying to get it to use TLS. What else can I do to get it to use TLS?
You're probably not using SSLv2, but an SSLv3 or TLS1.x ClientHello wrapped into an SSLv2 ClientHello. See "Why does Java's SSLSocket send a version 2 client hello?" or "How to initiate ssl connection using SSLv2".
Note that the latest JSSE Reference Guide (JDK 8) says:
Note: As part of disabling SSLv3, some servers have also disabled SSLv2Hello, which means communications with SSLv2Hello-active clients (e.g. JDK 1.5/6) will fail. Starting with JDK 7, SSLv2Hello default to disabled on clients, enabled on servers.
The Java 7 release notes also say:
SSLv2Hello disabled by default on the client: In Java SE 7, SSLv2Hello is removed from the default enabled protocol list on the client.
It's possible that you're using an older JRE or that for whatever reason, SSLv2Hello was explicitly enabled on your clients.
The protocolVersion value should be TLS1 instead of TLSv1.1.
https://docs.oracle.com/middleware/1213/wls/SECMG/ssl_version.htm#SECMG636
Setting -Dweblogic.security.SSL.minimumProtocolVersion=TLSv0 as java option, will set the minimum protocol to SSLV3 and will eliminate the use of SSLV2.
This worked for me.
TLSv0 is invalid, and WebLogic 12.1.3 will set SSLV3 as minimum.
What is the Java version that you're using with WebLogic?
TLS 1.1 is available at Java 1.6 Update 111. That might be why it is not working. That using TLSv1 as value

Google Authentication errors when accessing token endpoint from JVM

We run a web application with a JVM backend (Java 7 update 75; code is in Scala, but I don't believe this is relevant). We use Google for authentication via Oauth.
There have been a handful of days over the last couple of months on which we have been intermittently unable to authenticate users.
The redirect to and from Google is successful, but when we try to call the token_endpoint at https://www.googleapis.com/oauth2/v4/token to validate the authentication we sometimes get the following exception: javax.net.ssl.SSLHandshakeException: server certificate change is restrictedduring renegotiation.
This comment on another question led me to find a JDK bug that can manifest as this exception (What means "javax.net.ssl.SSLHandshakeException: server certificate change is restrictedduring renegotiation" and how to prevent it?).
My working hypothesis is:
The bug (https://bugs.openjdk.java.net/browse/JDK-8072385) means that only the first entry in the Subject Alternative Name (SAN) list is checked. The exception above is thrown when the hostname being verified is in the SAN list, but not at the top of the list.
Yesterday (27th May 2015 from ) we saw two different certificates being intermittently served from www.googleapis.com. The first (serial 67:1a:d6:10:cd:1a:06:cc) had an SAN list of DNS:*.googleapis.com, DNS:*.clients6.google.com, DNS:*.cloudendpointsapis.com, DNS:cloudendpointsapis.com, DNS:googleapis.com whilst the second (serial 61:db:c8:52:b4:77:cf:78) had a SAN list of: DNS:*.storage.googleapis.com, DNS:*.commondatastorage.googleapis.com, DNS:*.googleapis.com.
Due to the bug in the JVM, we can validate the first certificate, but the exception is thrown with the second (despite being perfectly valid) as *.googleapis.com is not the first entry in the SAN list.
The fix is in the yet to be released 7u85 (no mention of when this will be available).
I've downgraded a single node of our cluster to 7u65, but the certificate seemed to be reverted at around the time we did this (last error we saw was 22:20GMT) so it's hard to pin down an affirmative fix.
Has anyone else experienced this or something similar and have any other workaround other than downgrading (downgrading removes various other SSL/TLS checks)?
I am not really sure that your problem is related to a JVM bug.
There is a fix in Java 6 and above for CVE-2014-6457, "Triple Handshake attack against TLS/SSL connections (JSSE, 8037066)", prevents peer certificates changing during renegotiation.
Problem explanation:
A security vulnerability in all versions of the Transport Layer
Security (TLS) protocol (including the older Secure Socket Layer
(SSLv3)) can allow Man-In-The-Middle (MITM) type attacks where chosen
plain text is injected as a prefix to a TLS connection. This
vulnerability does not allow an attacker to decrypt or modify the
intercepted network communication once the client and server have
successfully negotiated a session between themselves.
However, if the potentially changed certificate is for the same identity as the last seen certificate then the connection is allowed.
Two identities are considered equal in this case:
There is a subject alternative name specified in both certificates which is an IP address and the IP address in both certificates is the same.
There is a subject alternative name specified in both certificates which is a DNS name and the DNS name in both certificates is the same.
The subject and issuer fields are present in both certificates and contain identical subject and issuer values.
In other conditions (the identity of the certificate has changed) then a javax.net.ssl.SSLHandshakeException: server certificate change is restricted during renegotiation exception is raised.
Workaround:
Disable renegotiation (not recommended) applying the following JVM argument: -Djdk.tls.allowUnsafeServerCertChange=true it disables the unsafe server certificate protection.
Disable SSLv3 in outgoing HTTPS connections, Java 7 supports TLSv1.1 and TLSv1.2 in client mode but defaults to using TLSv1 in the TLS handshake. We should use TLSv1.1 and TLSv1.2 in client mode TLS in java 7 as well. Java 8 enables TLSv1.1 and TLSv1.2 in client mode(in addition to SSLv3 and TLSv1) and uses TLSv1.2 by default in TLS handshake. If you are creating the connection programatically and setting a socket factory use TLS instead of SSL.
Anyway, update your post with your google oauth client code before calling the token_endpoint to validate the authentication to see what might be happening.

getting "javax.net.ssl.SSLException: MAC data does not match" while creating a TLS connection

![enter image description here][1]
![enter image description here][2]
![enter image description here][3]
I am trying to create a secure connection between client and server.
Client is supporting TLSv1.2 and Server is supporting TLSv1.0.
But getting "javax.net.ssl.SSLException: MAC data does not match" exception.
here is the printstacktrace :
javax.net.ssl.SSLException: MAC data does not match.
at com.rsa.sslj.x.aJ.b(Unknown Source)
at com.rsa.sslj.x.aJ.a(Unknown Source)
at com.rsa.sslj.x.aJ.a(Unknown Source)
at com.rsa.sslj.x.aU.c(Unknown Source)
at com.rsa.sslj.x.aU.a(Unknown Source)
at com.rsa.sslj.x.aU.h(Unknown Source)
at com.rsa.sslj.x.cI.startHandshake(Unknown Source)
at com.sun.jndi.ldap.ext.StartTlsResponseImpl.startHandshake(StartTlsResponseImpl.java:361)
at com.sun.jndi.ldap.ext.StartTlsResponseImpl.negotiate(StartTlsResponseImpl.java:225)
at com.cisco.ccm.admin.utilities.LdapUtil.validateHost(LdapUtil.java:383)
at com.cisco.ccm.admin.utilities.LdapUtil.validateHost(LdapUtil.java:289)
I checked in my code the enabled protcols are SSLv3, TLSv1 , TLSv1.1 and TLSv1.2 using getEnabledProtocols() method.
I have read in the TLSv1.2 rfc that if the server is supporting the lower version of TLS then client, client should negotiate with lower version of TLS.
But in my case, i am getting the above exception.(why ??... that shouldnot happen and negotiate with the TLSv1).
NOTE : Also, if i set the enabled protocol as TLSv1 only, then i am able to create a TLS connection.
Please help me out.
Thanks
[1]: http://i.stack.imgur.com/RC4Ha.jpg
[2]: http://i.stack.imgur.com/rjXjq.jpg
[3]: http://i.stack.imgur.com/WIRvF.jpg
It is definitely a problem of version incompatible. One thing for sure is that TLS is the new name for SSL. Namely, SSL protocol got to version 3.0; TLS 1.0 is "SSL 3.1". TLS versions currently defined include TLS 1.1 and 1.2.
Also, the following wiki link (http://en.wikipedia.org/wiki/Transport_Layer_Security) shows that
All TLS versions were further refined in RFC 6176 in March 2011 removing their backward compatibility with SSL such that TLS sessions will never negotiate the use of Secure Sockets Layer (SSL) version 2.0.
Also between TLS and SSL, the MAC/HMAC differs (TLS uses HMAC whereas SSL uses an earlier version of HMAC). I am suspecting that somehow your code still might be picking us SSL. Could you please disable SSL and try again?

Categories