How to change client TLS preferences in Java?
Asked Answered
E

3

7

I'm trying to make a POST request to an endpoint in Java, and when I try to send the request, I get the following error:

Caused by: javax.net.ssl.SSLHandshakeException: The server selected protocol version TLS10 is not accepted by client preferences [TLS13, TLS12]

This is what I have so far

Map<Object, Object> data = new HashMap<>();
data.put("username","foo");
data.put("password","bar");

String url = "https://google.com";

HttpRequest request = HttpRequest.newBuilder()
     .POST(buildFormDataFromMap(data))
     .uri(URI.create(url))
     .build();

try{
     HttpResponse<String> response =  httpClient.send(request, 
          HttpResponse.BodyHandlers.ofString());
     System.out.println(response.statusCode());
     System.out.println(response.body());
} catch (Exception e){
     e.printStackTrace();
}

Then when I run the code, the error gets thrown when sending the request/making the response object. My question is, if the TLS preferences are different for the server than the client, how can I change the preferences within Java so it can still make the request?

Ebneter answered 5/11, 2019 at 19:43 Comment(0)
R
5

To solve this problem in jdk 11, I had to create an javax.net.ssl.SSLParameters object to enable "TLSv1", etc:

SSLParameters sslParameters = new SSLParameters();
        sslParameters.setProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"});

Then create the HttpClient and add the sslParamters object:

HttpClient httpClient = HttpClient.newBuilder()
            .sslParameters(sslParameters)
            .build();

If you also want to disable hostname verification, add following code BEFORE HttpClient initialization;

final Properties props = System.getProperties(); 
props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());

Also you can add a new TrustManager to trust all certificates (self signed). To do so, add following code into your Class:

    TrustManager[] trustAllCerts = new TrustManager[] { 
        new X509TrustManager() {     
            public java.security.cert.X509Certificate[] getAcceptedIssuers() { 
                return new X509Certificate[0];
            } 
            public void checkClientTrusted( 
                java.security.cert.X509Certificate[] certs, String authType) {
                } 
            public void checkServerTrusted( 
                java.security.cert.X509Certificate[] certs, String authType) {
            }
        } 
    }; 

After this, you have to create an SSLContext object and add the TrustManger object:

SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

And finally alter the HttpClient initialization like this:

httpClient = HttpClient.newBuilder()
            .sslContext(sslContext)
            .sslParameters(sslParameters)
            .build()

Here is a complete Class example:

import java.net.http.HttpClient;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Properties;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class HttpSSLClient {

    private SSLContext sslContext;
    private SSLParameters sslParameters;
    private HttpClient httpClient;

    public HttpSSLClient() throws KeyManagementException, NoSuchAlgorithmException {

        sslParameters = new SSLParameters();
        sslParameters.setProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"});

        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

        final Properties props = System.getProperties(); 
        props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());

        httpClient = HttpClient.newBuilder()
                .sslContext(sslContext)
                .sslParameters(sslParameters)
                .build();
    }

    public HttpClient getHttplClient() {
        return httpClient;
    }

    TrustManager[] trustAllCerts = new TrustManager[] { 
            new X509TrustManager() {     
                public java.security.cert.X509Certificate[] getAcceptedIssuers() { 
                    return new X509Certificate[0];
                } 
                public void checkClientTrusted( 
                    java.security.cert.X509Certificate[] certs, String authType) {
                    } 
                public void checkServerTrusted( 
                    java.security.cert.X509Certificate[] certs, String authType) {
                }
            } 
        }; 
}

You can use the getHttplClient() function while calling your HttpRequest.

Reddin answered 15/5, 2020 at 13:49 Comment(4)
Extremely helpful! Thanks for sharing!Vogel
Please note that this answer includes code which changes much more than what the question asked for. Whoever is reading this might only need the .setProtocols(...) part and the code that uses it to build an HttpClient...Caruthers
I did all the above but I am still getting IOException: The server selected protocol version TLS10 is not accepted by client preferences [TLS13, TLS12] caused by: SSLHandshakeException: The server selected protocol version TLS10 is not accepted by client preferences [TLS13, TLS12]. Something must still be missing. This is on Java 11 (AdoptOpenJDK).Vogel
Just detected what was still missing: In file <JDK>/conf/security/java.security one needs to remove "TLSv1" and "TLSv1.1" from the disabledAlgorithms-list. One can also do this during runtime using code like: String disabledAlgorithms = Security.getProperty("jdk.tls.disabledAlgorithms"); Security.setProperty("jdk.tls.disabledAlgorithms", disabledAlgorithms .replace("TLSv1,", ""));Vogel
M
0

I had the same issue and this solution does not work for me. Instead I saw this answer Android Enable TLSv1.2 in OKHttp and I tried this code: ConnectionSpec spec = new ConnectionSpec .Builder(ConnectionSpec.MODERN_TLS) .tlsVersions(TlsVersion.TLS_1_2,TlsVersion.TLS_1_0,TlsVersion.TLS_1_1,TlsVersion.TLS_1_3).build(); client =client.newBuilder().connectionSpecs(Collections.singletonList(spec)).build(); And it worked for me:)

Mylohyoid answered 17/11, 2021 at 17:36 Comment(2)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Schmit
This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. To get notified when this question gets new answers, you can follow this question. Once you have enough reputation, you can also add a bounty to draw more attention to this question. - From ReviewDeclivity
B
-1

I think mmo's answer should be highlighted in bold. I had similar issue, but found out that the open-jdk jvm I was using has TLSv1 and TLSv1.1 as disabled in the jdk.tls.disabledAlgorithms line in java.security. So as soon as I removed it and restarted the JVM, I was able to connect usingthe older TLS protocols.

But please pay ATTENTION, This is not advisable in Production since it degrades the secure communication. So I'd say change it if you want at YOUR OWN RISK!!!

Barm answered 19/10, 2021 at 5:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.