允许 Java JDK 11 HttpClient 的不安全 HTTPS 连接

允许 Java JDK 11 HttpClient 的不安全 HTTPS 连接 - Stack Overflow

如前所述,您需要一个忽略错误证书的 SSLContext。在问题中的一个链接中获取 SSLContext 的确切代码应该基本上通过创建一个不查看证书的 null TrustManager 来工作:

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

public static  void main (String[] args) throws Exception {
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustAllCerts, new SecureRandom());

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

改用 sslContext.init(null, null, new SecureRandom())。将 trustAllCerts 参数替换为 null,然后可以忽略 TrustManager 数组的整个创建。

elasticsearch - 禁用 Elastic search 的 SSL 验证Restclient 在 Java 中不起作用 - Stack Overflow

sslcontext-kickstart/sslcontext-kickstart/src/main/java/nl/altindag/ssl/trustmanager/UnsafeX509ExtendedTrustManager.java at master · Hakky54/sslcontext-kickstart

选项 1

public final class UnsafeX509ExtendedTrustManager extends X509ExtendedTrustManager {

    private static final X509ExtendedTrustManager INSTANCE = new UnsafeX509ExtendedTrustManager();
    private static final X509Certificate[] EMPTY_CERTIFICATES = new X509Certificate[0];

    private UnsafeX509ExtendedTrustManager() {}

    public static X509ExtendedTrustManager getInstance() {
        return INSTANCE;
    }

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

    }

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

    @Override
    public void checkClientTrusted(X509Certificate[] certificates, String authType, SSLEngine sslEngine) {

    }

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

    }

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

    }

    @Override
    public void checkServerTrusted(X509Certificate[] certificates, String authType, SSLEngine sslEngine) {

    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return EMPTY_CERTIFICATES;
    }

}

上述 trustmanager 可以通过以下代码片段提供给 RestHighLevelClient:

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{ UnsafeX509ExtendedTrustManager.INSTANCE }, null);

RestClient restClient = RestClient
        .builder(new HttpHost("localhost", 9200, "https"))
        .setHttpClientConfigCallback(httpClientBuilder -> 
                httpClientBuilder.setSSLContext(sslContext)
                                 .setSSLHostnameVerifier((host, session) -> true))
        .build();

顺便说一句,我不建议你或其他任何人使用 UnsafeX509ExtendedTrustManager。它不安全,根本不应在生产中使用。

选项 2

如果您不想将自定义代码添加到您的代码库中,而只想轻松禁用 ssl 验证,则可能需要尝试以下代码段。它是一个轻松生成 SSLContext 或其他 ssl 材料的库,并且可以选择禁用 ssl 验证。

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart</artifactId>
    <version>8.3.5</version>
</dependency>

用法

SSLFactory sslFactory = SSLFactory.builder()
          .withUnsafeTrustMaterial()
          .withUnsafeHostnameVerifier()
          .build();

RestClient restClient = RestClient
        .builder(new HttpHost("localhost", 9200, "https"))
        .setHttpClientConfigCallback(httpClientBuilder -> 
                httpClientBuilder.setSSLContext(sslFactory.getSslContext())
                                 .setSSLHostnameVerifier(sslFactory.getHostnameVerifier())
        .build();

 

    @Bean
        public RestHighLevelClient createSimpleElasticClient() throws Exception {
            try {
                SSLContextBuilder sslBuilder = SSLContexts.custom()
                        .loadTrustMaterial(null, (x509Certificates, s) -> true);
                        final SSLContext sslContext = sslBuilder.build();
                RestHighLevelClient client = new RestHighLevelClient(RestClient
                        .builder(new HttpHost(hostNameOrLoadbalancerURL, 443, "https")) 
//port number is given as 443 since its https schema
                        .setHttpClientConfigCallback(new HttpClientConfigCallback() {
                            @Override
                            public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                                return httpClientBuilder
                                         .setSSLContext(sslContext)
                                         .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
                            }
                        })
                        .setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
                            @Override
                            public RequestConfig.Builder customizeRequestConfig(
                                    RequestConfig.Builder requestConfigBuilder) {
                                return requestConfigBuilder.setConnectTimeout(5000)
                                        .setSocketTimeout(120000);
                            }
                        }));
                System.out.println("elasticsearch client created");
                return client;
            } catch (Exception e) {
                System.out.println(e);
                throw new Exception("Could not create an elasticsearch client!!");
            }
        }

java - 忽略 SSL 验证HttpClient / Rest API - Groovy - 堆栈溢出

1

您几乎已正确配置它,但它略有偏差。您需要做的是从 url 本身创建一个 URL 对象并打开连接,将其转换为 HttpsUrlConnection 并将 ssl 配置传递给它。示例代码片段如下:

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.InputStream;
import java.net.Socket;
import java.net.URL;
import java.security.cert.X509Certificate;

public class App {

    public static void main(String[] args) throws Exception {
        UnsafeTrustManager unsafeTrustManager = new UnsafeTrustManager();
        HostnameVerifier hostnameVerifier = (hostname, sslSession) -> true;

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{unsafeTrustManager}, null);

        URL url = new URL("https://stackoverflow.com");
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        connection.setHostnameVerifier(hostnameVerifier);
        connection.setSSLSocketFactory(sslContext.getSocketFactory());
        connection.setRequestMethod("GET");

        InputStream inputStream = connection.getInputStream();
        int responseCode = connection.getResponseCode();

        System.out.println(responseCode);
    }

    private static class UnsafeTrustManager extends X509ExtendedTrustManager {
        public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) {}
        public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) {}
        public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {}
        public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {}
        public void checkClientTrusted(X509Certificate[] chain, String authType) {}
        public void checkServerTrusted(X509Certificate[] chain, String authType) {}
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }

}

上面的示例适用于 httpsurlconnection,但是在您的堆栈跟踪中,我看到 Apache http 客户端 5,它需要以不同的方式配置,请参阅下面的示例:

import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.net.Socket;
import java.security.cert.X509Certificate;

public class App {

    public static void main(String[] args) throws Exception {
        UnsafeTrustManager unsafeTrustManager = new UnsafeTrustManager();
        HostnameVerifier hostnameVerifier = (hostname, sslSession) -> true;

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{unsafeTrustManager}, null);

        LayeredConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        var connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
                .setSSLSocketFactory(socketFactory)
                .build();

        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .build();
    }

    private static class UnsafeTrustManager extends X509ExtendedTrustManager {
        public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) {}
        public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) {}
        public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {}
        public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {}
        public void checkClientTrusted(X509Certificate[] chain, String authType) {}
        public void checkServerTrusted(X509Certificate[] chain, String authType) {}
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }

}

ssl - Java 禁用证书验证仍然获取 PKIX 路径构建失败 - Stack Overflow

通过 https 使用 Httpclient 信任所有证书 |Better Stack 社区

通过 https 使用 httpclient 信任所有证书

更好的堆栈团队
更新于 2024 年 10 月 7 日

由于存在重大安全风险,通常不建议在使用 HTTPS 请求时信任所有证书。但是,出于开发或测试目的,您可能需要绕过 SSL 证书验证。以下是针对各种平台和语言执行此作的方法:HttpClient

1. Python:通过 urllib3 使用请求

对于 Python 的库,您可以通过配置自定义适配器或将参数设置为 .requestsverifyFalse

 
import requests

# Bypass SSL verification
response = requests.get('<https://example.com>', verify=False)
print(response.text)

选项 2:使用自定义适配器

您可以创建自定义适配器以忽略 SSL 验证:

 
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.poolmanager import PoolManager
import ssl

class SSLAdapter(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        kwargs['ssl_context'] = ssl._create_unverified_context()
        return super().init_poolmanager(*args, **kwargs)

session = requests.Session()
session.mount('https://', SSLAdapter())

response = session.get('<https://example.com>')
print(response.text)

2. Java:使用 HttpClient

在 Java 中,您可以配置为信任所有证书。这涉及设置接受所有证书的自定义。HttpClientTrustManager

选项 1:信任所有证书(用于开发/测试)

 
import javax.net.ssl.*;
import java.security.cert.X509Certificate;
import java.security.KeyStore;
import java.net.HttpURLConnection;
import java.net.URL;

public class TrustAllCertificates {

    public static void main(String[] args) throws Exception {
        // Create a TrustManager that trusts all certificates
        TrustManager[] trustAll = new TrustManager[]{
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

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

                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                }
            }
        };

        // Install the all-trusting trust manager
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAll, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // Create an all-trusting host verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };

        // Install the all-trusting host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

        // Make a request
        URL url = new URL("<https://example.com>");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.connect();

        // Read the response
        System.out.println("Response Code: " + connection.getResponseCode());
    }
}

3. JavaScript:将 axios 与 Node.js 一起使用

对于使用 Node.js 应用程序,您可以将其配置为忽略 SSL 证书验证错误。axios

选项 1:绕过 SSL 验证

 
const axios = require('axios');
const https = require('https');

// Create an instance of axios with HTTPS agent configured to ignore SSL errors
const instance = axios.create({
    httpsAgent: new https.Agent({ rejectUnauthorized: false })
});

instance.get('<https://example.com>')
    .then(response => {
        console.log(response.data);
    })
    .catch(error => {
        console.error('Error:', error);
    });

安全注意事项

  • 风险:信任所有证书或禁用 SSL 验证会使您的应用程序面临各种安全风险,包括中间人 (MITM) 攻击和数据泄露。
  • 用例:此类配置应仅用于不考虑安全性的开发或测试环境。
  • 生产:对于生产环境,请始终使用有效、可信的 SSL 证书,并确保进行适当的 SSL/TLS 验证。

总结

绕过 SSL 证书验证可以在各种编程环境中完成,以用于开发或测试目的。但是,在生产环境中,使用有效证书并确保采取适当的 SSL/TLS 安全措施来防范潜在的安全威胁至关重要。

在 Java 中使用自定义 TrustStore |贝尔东

1. 引言

在本教程中,我们将了解如何在 Java 中使用自定义 TrustStore。我们首先要覆盖默认的 TrustStore,然后探索组合来自多个 TrustStore 的证书的方法。我们还将了解已知的问题和挑战是什么,以及我们如何超越它们。

2. 覆盖自定义 TrustStore

因此,首先,让我们覆盖默认的 TrustStore。最有可能的是,对于 JDK 9 及更高版本,它将是位于 lib/security/cacerts 中的 cacerts 文件。对于版本 9 以下的 JDK,cacerts 位于 jre/lib/security/cacerts 下。要覆盖它,我们需要传递一个 VM 参数 -Djavax.net.ssl.trustStore,其中包含要用作值的 TrustStore 的绝对路径。例如,如果我们像这样启动 JVM:

java -Djavax.net.ssl.trustStore=/path/to/another/truststore.p12 app.jarCopy

然后,Java 将使用 /path/to/another/truststore.p12 作为 TrustStore,而不是 cacerts

但是,这种方法有一个小问题。当我们覆盖 TrustStore 的位置时,将不再考虑默认的 cacerts TrustStore。这意味着,预装 JDK 的所有受信任 CA 证书现在将不再可用。

3. 组合多个 TrustStore

因此,要解决上面列出的问题,我们可以做以下两件事之一:

 
  • 将所有默认 cacerts 证书包含在我们要使用的新 TrustStore 中
  • 尝试以编程方式要求 Java 在解析实体的信任期间查看这两个 TrustStore

我们将在下面回顾这两种方法,因为它们各有利弊。

4. 合并 TrustStores

第一种方法是解决问题的相对简单的方法。在这种情况下,我们可以从默认 TrustStore 创建新的 TrustStore。通过这样做,我们确保新的 TrustStore 将包含所有初始 CA 证书:

keytool -importkeystore -srckeystore cacerts -destkeystore new_trustStore.p12 -srcstoretype PKCS12 -deststoretype PKCS12Copy

然后,我们将所需的证书导入到新创建的 TrustStore 中:

keytool -import -alias SomeSelfSignedCertificate -keystore new_trustStore.p12 -file /path/to/certificate/to/addCopy

我们可以修改初始 TrustStore(即 cacerts 本身),这可能是一个可行的选择。唯一需要考虑的是依赖于此确切 JDK 安装的其他应用程序。他们也将在默认 cacerts 中接收这些新添加的证书。这可能没问题,也可能不可以,这取决于要求。

5. 以编程方式考虑两个 TrustStore

这种方法比我们描述的要复杂一些。挑战在于,在 JDK 中,没有内置方法可以要求 TrustManager(决定信任某人的那个)考虑多个 TrustStore。所以我们必须自己实施它。

 

首先要做的是获取 TrustManagerFactory 的实例。当我们拥有它时,我们将能够获得我们需要的 TrustManager

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
Copy

所以在这里,我们得到默认的 TrustManagerFactory,然后我们用 null 作为参数初始化它。init 方法使用给定的 TrustStore 初始化 TrustManagerFactory。顺便说一句,Java KeyStore 和 TrustStore 都由 KeyStore Java 类表示。因此,当我们将 null 作为参数传递时,TrustManagerFactory 将使用默认的 TrustStore (cacerts) 初始化自身。

一旦我们有了它,我们应该从 TrustManagerFactory 获取实际的 TrustManager。更具体地说,我们需要 X509TrustManager。此 TrustManager 负责确定是否应信任给定的 x509 证书:

X509TrustManager defaultX509CertificateTrustManager = null;
for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
    if (trustManager instanceof X509TrustManager x509TrustManager) {
        defaultX509CertificateTrustManager = x509TrustManager;
        break;
    }
}
Copy

因此,我们有默认的 JDK 的 X509TrustManager,它只知道默认的 cacerts。现在,我们需要加载我们自己的 TrustStore 并使用我们的这个新 TrustStore 初始化新的 TrustManagerFactory

try (FileInputStream myKeys = new FileInputStream("new_TrustStore.p12")) {
    KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    myTrustStore.load(myKeys, "new_TrustStore_pwd".toCharArray());
    trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(myTrustStore);

    X509TrustManager myTrustManager = null;
    for (TrustManager tm : trustManagerFactory.getTrustManagers()) {
        if (tm instanceof X509TrustManager x509TrustManager) {
            myTrustManager = x509TrustManager;
            break;
        }
    }
}
Copy

正如我们所看到的,我们已经使用给定的密码将 TrustStore 加载到一个新的 KeyStore 对象中。然后我们获取另一个默认的 TrustManagerFactory 实例(getInstance() 方法总是返回一个新对象)并使用我们的 TrustStore 初始化它。然后,以与上述相同的方式,我们找到 X509TrustManager,它现在考虑我们的 TrustStore。现在,剩下的唯一事情就是配置 SSLContext 以使用两个 X509TrustManager 实现 – 默认实现和我们的实现。

 

6. 重新配置 SSLContext

现在,我们需要教 SSLContext 使用我们的 2 个 X509TrustManagers。问题是我们不能将它们单独传递到 SSLContext 中。这是因为 SSLContext 出乎意料地只使用它找到的第一个 X509TrustManager,而忽略其余的。 为了克服这个问题,我们需要创建一个最终的X509TrustManager,它是我们两个 X509TrustManager 的包装器:

X509TrustManager finalDefaultTm = defaultX509CertificateTrustManager;
X509TrustManager finalMyTm = myTrustManager;

X509TrustManager wrapper = new X509TrustManager() {
    private X509Certificate[] mergeCertificates() {
        ArrayList<X509Certificate> resultingCerts = new ArrayList<>();
        resultingCerts.addAll(Arrays.asList(finalDefaultTm.getAcceptedIssuers()));
        resultingCerts.addAll(Arrays.asList(finalMyTm.getAcceptedIssuers()));
        return resultingCerts.toArray(new X509Certificate[resultingCerts.size()]);
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return mergeCertificates();
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        try {
            finalMyTm.checkServerTrusted(chain, authType);
        } catch (CertificateException e) {
            finalDefaultTm.checkServerTrusted(chain, authType);
        }
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        finalDefaultTm.checkClientTrusted(mergeCertificates(), authType);
    }
};
Copy

然后使用我们的包装器初始化 TLS SSLContext

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[] { wrapper }, null);
SSLContext.setDefault(context);
Copy

我们还将此 SSLContext 设置为默认 SSLContext。这只是为了以防万一,因为大多数想要建立安全连接的客户端都会使用 TLS SSLContext。不过,这用作备用选项。我们终于完成了。

7. 总结

在本文中,我们探讨了如何在一个 Java 应用程序中使用来自不同 TrustStore 的证书的方法。

遗憾的是,在 Java 中,如果我们从命令行指定 TrustStore 位置,这将指示 Java 仅使用指定的 TrustStore。因此,我们的选项是修改默认的 cacerts TrustStore 文件,或者创建一个全新的 TrustStore 文件,其中包含所有必需的 CA 证书条目。更复杂的方法是强制 SSLContext 以编程方式考虑这两个 TrustStores。

尽管如此,所有这些选项都可以使用,我们应该使用符合我们要求的那个。

 spring boot - 在服务器上实现信任库管理器以验证对等 X509 证书 - Stack Overflow

ssl - 替换正在运行的 java 应用程序中的证书 - Stack Overflow

 

 

posted @ 2025-05-07 11:24  CharyGao  阅读(220)  评论(0)    收藏  举报