How to disable certificate validations in the Java HTTP Client

发布时间:2024年01月12日

Java 11 introduced the?HTTP Client, an API that made it easier to send HTTP requests with vanilla Java.

By default, it throws an exception if there are certificate path or hostname verification errors in the request.

Let’s see how to bypass certificate validations for cases where this is really necessary.

Disabling all certificate verifications for a specific client

To ignore both certificate path and hostname verifications, create an?X509ExtendedTrustManager?extension that doesn't do any verification and use it to init an?SSLContext?for an?HttpClient:

var trustManager = new X509ExtendedTrustManager() {
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[]{};
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) {
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) {
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {
    }
};
var sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());

var client = HttpClient.newBuilder()
        .sslContext(sslContext)
        .build();

With this solution, only that client with that custom?SSLContext?specified will allow insecure requests. So in many cases this is the best option.

You can use the example URLs?https://expired.badssl.com/?and?https://wrong.host.badssl.com/?to test:

var expiredRequest = HttpRequest.newBuilder()
        .uri(URI.create("https://expired.badssl.com/"))
        .build();

var wrongHostRequest = HttpRequest.newBuilder()
        .uri(URI.create("https://wrong.host.badssl.com/"))
        .build();

client.send(expiredRequest, BodyHandlers.discarding());
client.send(wrongHostRequest, BodyHandlers.discarding());

Errors you would get

Without disabling verification, this error would occur for an expired?SSL/TLS certificate:

javax.net.ssl.SSLHandshakeException: PKIX path validation failed: java.security.cert.CertPathValidatorException: validity check failed

...

Caused by: java.security.cert.CertificateExpiredException: NotAfter: Sun Apr 12 20:59:59 BRT 2015

And for a wrong hostname:

javax.net.ssl.SSLHandshakeException: No subject alternative DNS name matching wrong.host.badssl.com found.

...

Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching wrong.host.badssl.com found.

Disabling hostname verification by system property

You can set the?jdk.internal.httpclient.disableHostnameVerification?system property to?"true"?to disable only hostname verification, as shown in the?Javadoc.

This solution isn’t applied to certificate path verification, so an expired certificate would still cause an exception. And it has the disadvantage of disabling hostname verification for requests from all clients.

Disabling only certificate path verification

If you create an?X509TrustManager?implementation (instead of an?X509ExtendedTrustManager?extension) that doesn't do verifications and use it on a client, it will ignore only the certificate path verification:

var sslContext = SSLContext.getInstance("TLS");
var trustManager = new X509TrustManager() {
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[]{};
    }

    @Override
    public void checkClientTrusted(X509Certificate[] certs, String authType) {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] certs, String authType) {
    }
};
sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());

var client = HttpClient.newBuilder()
        .sslContext(sslContext)
        .build();
var request = HttpRequest.newBuilder()
        .uri(URI.create("https://expired.badssl.com/"))
        .build();
client.send(request, BodyHandlers.discarding());

So this solution isn’t applied to hostname verification.

Conclusion

To disable certificate verification, the best option in most cases is to use an?X509ExtendedTrustManager?extension that doesn't do any verification, as this will bypass both certificate path and hostname verifications and will only apply to the specified client.

文章来源:https://blog.csdn.net/mmoooodd/article/details/135549503
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。