First of all, I premise that I am fully aware that Java 7 is EOL, vulnerable, extremely old and should not be used in 2023, but I need to mantain a legacy application which requires Java 7. I cannot upgrade it, so, please, read the full question (or jump to the next one) instead of commenting immediately with "Java 7 is old, upgrade it" just because there is "Java 7" in the title. Thank you for your understanding.
I have a legacy Java 7 application which sends some HTTPS requests to a server. Recently, that server changed its supported TLS ciphers so now all HTTP requests done from my Java application throw an exception.
This is a sample of the code I am using to send the HTTP requests:
URL url = new URL("https://precision.epayworldwide.com/up-interface");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String responseLine = reader.readLine();
while (responseLine != null)
{
System.out.printf("%s\n", responseLine);
responseLine = reader.readLine();
}
and this is the exception I get:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1943)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1059)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1294)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1321)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1305)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:523)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1296)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
at Main.main(Main.java:17)
To try to fix this exception I have tried the following steps (as suggested by this answer):
- I have donwloaded
bcprov-jdk15to18-175.jar
from BouncyCastle and I have added this JAR in myjdk/jre/lib/ext
directory. - I have edited the
jdk/jre/lib/security/java.security
file placingorg.bouncycastle.jce.provider.BouncyCastleProvider
at the top of the list of providers:
#
# List of providers and their preference orders (see above):
#
security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider
security.provider.2=sun.security.provider.Sun
security.provider.3=sun.security.rsa.SunRsaSign
security.provider.4=sun.security.ec.SunEC
security.provider.5=com.sun.net.ssl.internal.ssl.Provider
security.provider.6=com.sun.crypto.provider.SunJCE
security.provider.7=sun.security.jgss.SunProvider
security.provider.8=com.sun.security.sasl.Provider
security.provider.9=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.10=sun.security.smartcardio.SunPCSC
security.provider.11=sun.security.mscapi.SunMSCAPI
But I still get the handshake failure alert from the server.
I have also tried to add the bcutil-jdk15to18-175.jar
to my jdk/jre/lib/ext
directory (as suggested by a comment in the linked answer), but I still get the exception.
I have enabled TLS logging by adding the JVM argument -Djavax.net.debug=ssl:handshake:verbose
and this is what I get:
trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
main, setSoTimeout(0) called
%% No cached client session
*** ClientHello, TLSv1.2
RandomCookie: GMT: 1670579945 bytes = { 154, 22, 251, 69, 138, 101, 239, 72, 131, 212, 72, 151, 61, 82, 28, 179, 11, 15, 212, 118, 164, 38, 59, 49, 139, 50, 122, 48 }
Session ID: {}
Cipher Suites: [TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_RC4_128_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_RC4_128_MD5, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA]
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
Extension server_name, server_name: [host_name: precision.epayworldwide.com]
***
main, WRITE: TLSv1.2 Handshake, length = 229
main, READ: TLSv1.2 Alert, length = 2
main, RECV TLSv1 ALERT: fatal, handshake_failure
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
It seems that the HttpURLConnection
is not using the AES-GCM cipher (e.g. TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
), even though it should be implemented by the JARs I have added.
For the records, I have tested the server I am connecting to with this script and these are the supported ciphers:
Testing ECDHE-RSA-AES256-GCM-SHA384...YES
Testing DHE-RSA-AES256-GCM-SHA384...YES
Testing ECDHE-RSA-AES128-GCM-SHA256...YES
Testing DHE-RSA-AES128-GCM-SHA256...YES
Testing ECDHE-RSA-AES256-SHA384...YES
Testing ECDHE-RSA-AES128-SHA256...YES
Testing AES256-GCM-SHA384...YES
I am trying to support AES-GCM on my client because it's one of the ciphers supported by the server.
If possible, I would like to resolve this problem without changing the code (e.g. by explicitly setting the ciphers the HttpURLConnection
should use).