I am using below code to connect to https://api.twilio.com:8443.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
public class SampleApp {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, KeyManagementException {
URL url = new URL("https://api.twilio.com:8443/");
java.lang.System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
InputStream is = connection.getInputStream();
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null,null);
SSLContext.setDefault(sslContext);
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
}
When executing the above code with Java 1.7, it gives below exception:
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:1979)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1086)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1332)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1359)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1343)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1301)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
But when running the same code with Java 1.8, it gives expected output as:
<?xml version='1.0' encoding='UTF-8'?>
<TwilioResponse>
<Versions>
<Versions>
<Version>
<Name>2010-04-01</Name>
<Uri>/2010-04-01</Uri>
<SubresourceUris>
<Accounts>/2010-04-01/Accounts</Accounts>
</SubresourceUris>
</Version>
</Versions>
</Versions>
</TwilioResponse>
I am aware that JDK 8 will use TLS 1.2 as default. So, I have explicitly provided the http protocol in the code itself. Could anyone kindly explain me the reason for above behaviour and also how can I connect through Java 1.7?
CodePudding user response:
Twilio developer evangelist here.
I'm not a Java developer, so I don't have good insight on the differences between Java 1.7 and 1.8 but I can make some suggestions.
First up, Twilio does not support TLS below 1.2. Support for older versions of TLS was removed in June 2019. Twilio also only supports a specific list of cipher suites:
- ECDHE-RSA-AES128-GCM-SHA256
- ECDHE-ECDSA-AES128-SHA256
- ECDHE-RSA-AES128-SHA256
- ECDHE-ECDSA-AES256-GCM-SHA384
- ECDHE-RSA-AES256-GCM-SHA384
- ECDHE-ECDSA-AES256-SHA384
- ECDHE-RSA-AES256-SHA384
- AES128-GCM-SHA256
- AES128-SHA256
- AES256-GCM-SHA384
- AES256-SHA256
Since the same code works on Java 1.8 and not 1.7 I would check the cipher suites you have available in 1.7 and if any match the supported ones and whether that has changed in the move to Java 1.8.
Further, there are some tips on how to upgrade your system's support for TLS and the available cipher suites which I recommend you read and follow.
CodePudding user response:
You need to
either do
SSLContext.setDefault(tls12ctx)
before theURL.openConnection
or do
HttpsURLConnection.setDefaultSSLSocketFactory(tls12ctxfact)
before theURL.openConnection
(you didn't try this one)or do
connection.setSSLSocketFactory(tls12ctxfact)
before theconnection.getInputStream
(and with any of those you don't need any sysprops)
or to use sysprops, set both
https.protocols
to includeTLSv1.2
andhttps.cipherSuites
to be at least one of the suites identified by philnash, but using the RFC name(s), which Java follows, not the OpenSSL names he shows; see the openssl man page for ciphers(1) for the mapping (or the explanation in my answer at Map SSL/TLS cipher suites and their OpenSSL equivalents ). Personally I likeTLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
-- it's modern, fashionable and secure without being pretentious.(and with the sysprops you don't need any code change)
1 affects all other SSL/TLS connections in the same JVM; 2 and 4 affect all HTTPS connections using [Https]URLConnection
, but not connections using other software like Apache or other SSL/TLS protocols like secure mail, LDAPS, and many databases. 3 affects only this specific connection.
All four of those work for me on the last Oracle free version (7u80). I'm aware the paid-only versions since 2015 have had a number of SSL/TLS improvements retrofitted and at least some of them may work with no or less/different tweaking, but as I don't pay I can't help there.