JDK8连接Elasticsearch 8 HTTPS集群的PEM证书解决方案 1. 项目概述当JDK8遇上Elasticsearch 8的HTTPS如果你还在用JDK8并且最近想把应用升级到Elasticsearch 8.x特别是8.2.0这个版本那你大概率已经踩过或者即将踩进一个“大坑”用Java Client死活连不上开启了HTTPS的ES集群。控制台报错信息五花八门什么SSLHandshakeException、PKIX path building failed、unable to find valid certification path to requested target看着就让人头疼。这问题我最近在帮一个老系统做技术栈升级时遇到了折腾了大半天才彻底搞明白。核心矛盾点就在于Elasticsearch从7.x版本开始就加强了对安全传输层TLS的要求到了8.x更是默认就开启了HTTPS和认证而JDK8自带的那个“古老”的TLS实现和证书信任机制跟ES 8.x生成的证书格式尤其是默认的PKCS#12有点“水土不服”。简单来说ES 8.2.0集群默认或推荐生成的节点证书是PKCS#12格式.p12或.pfx后缀这种格式包含了私钥、公钥证书和完整的证书链本身很完善。但JDK8里的SSLContext在初始化时对于处理这种包含完整链的P12文件特别是当中间证书的某些属性比如basicConstraints扩展不符合JDK8较严格的默认校验规则时就容易出问题。更常见的情况是我们手里只有PEM格式的证书文件.crt, .key, .ca-bundle而用JDK8原生的方式去加载这一堆零散的文件步骤繁琐且容易出错。所以这个项目的目标非常明确在不升级JDK版本坚守JDK8的前提下找到一种可靠、清晰的方法让我们的Java应用程序能够成功连接到开启了HTTPS的Elasticsearch 8.2.0集群。解决方案的核心就是绕开JDK8里那些“挑食”的API直接使用更通用、更底层的PEM格式证书文件并通过一个强大的第三方库——org.apache.httpcomponents:httpclient配合org.apache.httpcomponents.client5:httpclient5的扩展能力——来构建我们自己的、兼容性极强的RestClient。下面我就把完整的踩坑过程、原理分析和一行行可落地的代码分享给你。2. 核心问题拆解为什么JDK8连不上ES 8.2.0 HTTPS在动手写代码之前我们必须先把问题根源挖清楚。这不仅仅是“证书不对”这么简单而是涉及到TLS协议版本、证书格式、Java安全库以及Elasticsearch默认配置等多个层面的兼容性问题。2.1 TLS协议版本与密码套件的不匹配Elasticsearch 8.x为了提升安全性默认启用了更现代的TLSv1.2和TLSv1.3并且禁用了一些老旧、不安全的密码套件。而JDK8特别是早期版本如8u161之前默认支持的TLS版本和密码套件列表可能比较保守。如果ES服务器端只启用了JDK8客户端不支持的协议或套件握手就会失败。不过这个问题相对好解决通常升级到JDK8的最新更新版本如8u381就能获得更好的TLSv1.2支持。但我们的项目前提是“不升级JDK”所以我们需要确保客户端代码能明确指定使用双方都支持的协议比如TLSv1.2。2.2 证书格式与信任链构建的差异这是最核心、最棘手的问题。Elasticsearch提供了elasticsearch-certutil工具来生成证书默认输出是PKCS#12格式。这个格式的文件是一个二进制容器里面打包了节点的私钥、公钥证书以及从根CA到该证书的完整证书链。JDK8的KeyStore与TrustStore加载P12的“坑”在Java中我们通常用KeyManagerFactory和TrustManagerFactory来管理SSL/TLS连接所需的密钥和信任材料。它们的数据源是KeyStore对象。使用KeyStore.getInstance(“PKCS12”)加载一个.p12文件时JDK8会尝试解析并验证其中的整个证书链。问题常出现在这里CA证书的basicConstraints扩展一个合格的CA证书其basicConstraints扩展必须明确标识CA:TRUE。如果ES工具生成的CA证书里这个属性设置不标准或者JDK8的默认校验器PKIX算法对此特别严格就会在构建信任链时抛出CertificateException。证书链的完整性有时.p12文件里可能只包含了叶节点证书缺少中间CA或根CA证书。JDK8在尝试构建一条从叶证书到其信任库中某个受信锚点的路径时如果链不完整也会失败。PEM格式的优势PEM格式Privacy-Enhanced Mail是Base64编码的文本文件常见后缀有.crt证书、.key私钥、.pem通用。它的优势在于“解耦”和“透明”。.crt文件通常只包含公钥证书可能是叶证书也可能是CA证书。.ca-bundle或.chain.pem文件按顺序包含从中间CA到根CA的证书链。.key文件包含PKCS#8格式的私钥。 我们可以分别获取这些文件并精确地控制将哪些证书加载到信任库TrustStore将哪些密钥和证书加载到密钥库KeyStore。这种灵活性让我们可以绕过JDK8对完整P12文件链验证的严格检查。2.3 Elasticsearch 8.2.0的默认安全配置从8.0开始Elasticsearch安全功能包括TLS和用户认证是默认开启的。这意味着即使你在单机开发环境localhost:9200也是一个HTTPS端点。它的默认证书是自签名的Java客户端自然不会信任它。因此我们必须将ES服务器使用的CA证书也就是签署了节点证书的那个根证书导入到客户端的信任库中告诉JDK“我信任这个CA颁发的一切证书”。3. 准备工作获取PEM格式的证书文件工欲善其事必先利其器。我们首先需要准备好关键的PEM文件。假设你已经有一个运行在HTTPS模式下的Elasticsearch 8.2.0集群。3.1 从Elasticsearch集群导出PEM证书如果你拥有ES集群的配置目录访问权限证书文件通常位于config/certs/目录下。你需要找到以下文件http_ca.crt这是Elasticsearch HTTP层使用的CA证书。这就是我们需要信任的根证书。把它拷贝出来重命名为es-root-ca.crt。节点的证书和私钥可能是http.p12或类似名称。我们需要从中提取PEM格式的私钥和证书。这需要用到openssl命令。# 假设你的节点证书文件是 http.p12密码是空字符串或你知道的密码 # 1. 提取私钥 (PKCS#8 格式) openssl pkcs12 -in config/certs/http.p12 -nocerts -nodes | openssl pkcs8 -topk8 -nocrypt -out client.key # 2. 提取证书链 openssl pkcs12 -in config/certs/http.p12 -clcerts -nokeys -out client.crt注意如果你的http.p12有密码需要在openssl pkcs12命令后添加-passin pass:your_password。-nodes参数表示不加密输出的私钥这在开发环境可以接受生产环境请妥善保管私钥。执行后你会得到client.key私钥和client.crt包含节点证书和可能的CA证书链。有时client.crt里已经包含了完整的链但为了清晰我们更希望使用独立的CA证书文件即上一步的es-root-ca.crt。3.2 使用自建CA签发PEM证书备选方案如果你没有现成的PEM文件或者想在测试环境从头构建可以自己扮演CA为ES节点签发证书。这能让你完全控制证书的格式和属性。# 1. 生成自签名根CA证书和私钥 openssl req -x509 -sha256 -days 3650 -newkey rsa:4096 -keyout rootCA.key -out rootCA.crt -subj /CNMyRootCA -nodes # 2. 为Elasticsearch节点生成私钥和证书签名请求(CSR) openssl req -new -newkey rsa:2048 -keyout es-node.key -out es-node.csr -subj /CNyour-es-hostname -nodes # 3. 使用根CA签署节点CSR生成节点证书 openssl x509 -req -in es-node.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out es-node.crt -days 365 -sha256 -extfile (printf subjectAltNameDNS:your-es-hostname,DNS:localhost,IP:127.0.0.1)这样你就得到了rootCA.crt信任的根证书、es-node.key节点私钥和es-node.crt节点证书。将rootCA.crt导入ES节点的信任链配置中并用es-node.key和es-node.crt配置ES的HTTP TLS。客户端则信任rootCA.crt即可。4. 构建兼容JDK8的Elasticsearch Java Client现在进入实战环节。我们将使用Elasticsearch官方推荐的Java High Level REST Client虽然已标记为Deprecated但在8.x版本中仍广泛使用且与Transport Client相比更现代并通过自定义RestClientBuilder来注入我们基于PEM证书的SSL配置。4.1 项目依赖配置Maven首先确保你的pom.xml包含了必要的依赖。关键点是引入httpclient5及其用于处理PEM的扩展模块。properties elasticsearch.version8.2.0/elasticsearch.version /properties dependencies !-- Elasticsearch Java Client -- dependency groupIdorg.elasticsearch.client/groupId artifactIdelasticsearch-rest-high-level-client/artifactId version${elasticsearch.version}/version /dependency !-- Apache HttpClient 5 (用于自定义SSL) -- dependency groupIdorg.apache.httpcomponents.client5/groupId artifactIdhttpclient5/artifactId version5.2.1/version /dependency !-- HttpClient5的SSL扩展支持PEM -- dependency groupIdorg.apache.httpcomponents.core5/groupId artifactIdhttpcore5/artifactId version5.2/version /dependency !-- 这个库能方便地加载PEM文件到KeyStore -- dependency groupIdorg.apache.httpcomponents/groupId artifactIdhttpclient/artifactId version4.5.13/version !-- 注意我们主要用其SSLContextBuilder的便捷方法 -- /dependency !-- 日志框架可选但建议有 -- dependency groupIdorg.slf4j/groupId artifactIdslf4j-api/artifactId version1.7.36/version /dependency dependency groupIdch.qos.logback/groupId artifactIdlogback-classic/artifactId version1.2.11/version /dependency /dependencies实操心得这里混合使用了httpclient4.x和httpclient5的依赖看起来有点怪但这是有原因的。httpclient4.5.13版本里的SSLContextBuilder类提供了非常方便的loadTrustMaterial(File, char[])和loadKeyMaterial(File, char[], char[])方法可以直接从PEM文件加载证书和私钥省去了我们手动解析PEM、构建KeyStore的复杂步骤。而Elasticsearch client底层最终会使用我们通过httpclient5配置的CloseableHttpClient。这是一种实用的“嫁接”方案。4.2 核心工具类PEM证书加载与SSLContext构建这是整个解决方案的心脏。我们将创建一个工具类专门负责从PEM文件构建出JDK的SSLContext对象。import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.SSLContexts; import javax.net.ssl.SSLContext; import java.io.File; import java.io.FileInputStream; import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.interfaces.RSAPrivateKey; import java.util.Collection; public class PEMBasedSSLContextBuilder { /** * 构建一个信任指定CA证书的SSLContext。 * 用于客户端验证服务器证书单向TLS。 * * param caCertificatePemFile PEM格式的CA证书文件 * return 配置好的SSLContext * throws Exception 如果文件读取或SSLContext创建失败 */ public static SSLContext buildTrustingSSLContext(File caCertificatePemFile) throws Exception { SSLContextBuilder sslBuilder SSLContexts.custom(); // 关键方法直接从PEM文件加载信任材料 sslBuilder.loadTrustMaterial(caCertificatePemFile, null); // null表示无密码保护 return sslBuilder.build(); } /** * 构建一个既信任指定CA又使用客户端证书进行双向TLS认证的SSLContext。 * 适用于需要客户端证书认证的场景。 * * param caCertificatePemFile PEM格式的CA证书文件用于信任服务器 * param clientCertificatePemFile PEM格式的客户端证书文件 * param clientPrivateKeyPemFile PEM格式的客户端私钥文件 * param clientKeyPassword 客户端私钥的密码如果没有加密则为null或空字符数组 * return 配置好的SSLContext * throws Exception 如果文件读取或SSLContext创建失败 */ public static SSLContext buildMutualAuthSSLContext(File caCertificatePemFile, File clientCertificatePemFile, File clientPrivateKeyPemFile, char[] clientKeyPassword) throws Exception { SSLContextBuilder sslBuilder SSLContexts.custom(); // 加载信任的CA sslBuilder.loadTrustMaterial(caCertificatePemFile, null); // 加载客户端证书和私钥 sslBuilder.loadKeyMaterial(clientCertificatePemFile, clientKeyPassword, clientPrivateKeyPemFile); return sslBuilder.build(); } /** * 备选方案手动加载PEM到KeyStore提供更精细的控制。 * 当上述简便方法因证书格式问题失效时可以尝试此方法。 */ public static SSLContext buildSSLContextManually(File caCertFile, File clientCertFile, File clientKeyFile) throws Exception { // 1. 创建空的KeyStore作为TrustStore KeyStore trustStore KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); // 加载CA证书 CertificateFactory cf CertificateFactory.getInstance(X.509); try (FileInputStream fis new FileInputStream(caCertFile)) { Collection? extends Certificate caCerts cf.generateCertificates(fis); int i 0; for (Certificate cert : caCerts) { trustStore.setCertificateEntry(ca- i, cert); } } // 2. 创建KeyStore作为KeyStore (存放客户端证书和私钥) // 这里需要解析PKCS#8私钥过程较复杂通常使用BouncyCastle库更简单。 // 鉴于篇幅此处省略详细代码。强烈建议优先使用上面的loadKeyMaterial方法。 // KeyStore keyStore ...; // 3. 使用手动创建的KeyStore初始化SSLContext // SSLContext sslContext SSLContexts.custom() // .loadTrustMaterial(trustStore, null) // .loadKeyMaterial(keyStore, password) // .build(); // return sslContext; throw new UnsupportedOperationException(手动解析PEM私钥较复杂建议使用BouncyCastle。本例优先使用简便方法。); } }注意事项SSLContexts.custom()来自org.apache.httpcomponents:httpclient:4.5.13它封装了复杂的KeyStore操作。loadTrustMaterial(File, char[])方法内部会读取PEM文件解析出X.509证书并将其添加到信任库。loadKeyMaterial(File, char[], File)方法更强大它能自动识别PEM文件中的私钥PKCS#8或PKCS#1格式和证书并将其组装成一个可用的密钥条目。这完美解决了我们手动处理PEM的难题。私钥密码如果你的.key文件是加密的有ENCRYPTED PRIVATE KEY头则需要提供密码。如果是-----BEGIN PRIVATE KEY-----无加密则密码传null或空数组。4.3 配置并创建Elasticsearch RestHighLevelClient有了SSLContext我们就可以配置RestClientBuilder了。这里我们配置的是单向TLS即客户端验证服务器证书这是最常见的场景。import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestHighLevelClient; import javax.net.ssl.SSLContext; import java.io.File; public class ElasticsearchClientFactory { private static final String ES_HOST your-elasticsearch-host; private static final int ES_PORT 9200; private static final String ES_SCHEME https; // PEM文件路径根据你的实际情况修改 private static final String CA_CERT_PATH /path/to/your/es-root-ca.crt; // 如果需要双向认证才需要下面两个文件 // private static final String CLIENT_CERT_PATH /path/to/your/client.crt; // private static final String CLIENT_KEY_PATH /path/to/your/client.key; public static RestHighLevelClient createClient() throws Exception { // 1. 构建SSLContext (单向认证只信任CA) SSLContext sslContext PEMBasedSSLContextBuilder.buildTrustingSSLContext(new File(CA_CERT_PATH)); // 2. 配置CredentialsProvider如果ES开启了基础认证 final CredentialsProvider credentialsProvider new BasicCredentialsProvider(); // 如果你的ES设置了用户名密码请取消注释并填写 // credentialsProvider.setCredentials( // new AuthScope(ES_HOST, ES_PORT), // new UsernamePasswordCredentials(elastic, your-password) // ); // 3. 构建RestClientBuilder RestClientBuilder builder RestClient.builder(new HttpHost(ES_HOST, ES_PORT, ES_SCHEME)) .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { Override public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) { httpClientBuilder.setSSLContext(sslContext); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); // 可选禁用主机名验证仅用于测试自签名证书生产环境慎用 // httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); return httpClientBuilder; } }); // 4. 可选设置请求超时、失败重试等策略 builder.setRequestConfigCallback(requestConfigBuilder - { requestConfigBuilder.setConnectTimeout(5000); requestConfigBuilder.setSocketTimeout(60000); return requestConfigBuilder; }); builder.setMaxRetryTimeoutMillis(60000); // 5. 创建高级客户端 return new RestHighLevelClient(builder); } // 一个简单的测试连接的方法 public static void testConnection(RestHighLevelClient client) { try { org.elasticsearch.action.main.MainResponse response client.info(org.elasticsearch.client.RequestOptions.DEFAULT); System.out.println(Connected to Elasticsearch cluster: response.getClusterName()); System.out.println(Node name: response.getNodeName()); System.out.println(Elasticsearch Version: response.getVersion().toString()); } catch (Exception e) { System.err.println(Failed to connect to Elasticsearch: e.getMessage()); e.printStackTrace(); } } public static void main(String[] args) { try (RestHighLevelClient client createClient()) { testConnection(client); // 这里可以执行你的业务操作例如索引文档、搜索等 // IndexRequest request new IndexRequest(test-index).id(1).source(field, value); // client.index(request, RequestOptions.DEFAULT); } catch (Exception e) { e.printStackTrace(); } } }4.4 完整示例执行一个索引操作让我们写一个完整的、可运行的示例包含创建索引、索引文档和搜索。import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.builder.SearchSourceBuilder; import java.util.HashMap; import java.util.Map; public class ElasticsearchPEMDemo { public static void main(String[] args) { RestHighLevelClient client null; try { // 1. 创建客户端 client ElasticsearchClientFactory.createClient(); System.out.println(Client created successfully.); // 2. 准备文档数据 MapString, Object jsonMap new HashMap(); jsonMap.put(user, kimchy); jsonMap.put(postDate, new java.util.Date()); jsonMap.put(message, trying out Elasticsearch with JDK8 and PEM certs); // 3. 构建索引请求 IndexRequest request new IndexRequest(posts) .id(1) .source(jsonMap, XContentType.JSON); // 如果索引不存在ES会自动创建取决于动态映射设置 // 4. 执行索引请求 IndexResponse indexResponse client.index(request, RequestOptions.DEFAULT); System.out.println(Document indexed. Index: indexResponse.getIndex()); System.out.println(Document ID: indexResponse.getId()); System.out.println(Result: indexResponse.getResult()); // 5. 等待片刻让文档可被搜索近实时搜索 Thread.sleep(1000); // 6. 执行一个简单搜索 SearchRequest searchRequest new SearchRequest(posts); SearchSourceBuilder searchSourceBuilder new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchQuery(message, elasticsearch)); searchRequest.source(searchSourceBuilder); SearchResponse searchResponse client.search(searchRequest, RequestOptions.DEFAULT); System.out.println(Search hits: searchResponse.getHits().getTotalHits().value); searchResponse.getHits().forEach(hit - { System.out.println(Found document: hit.getSourceAsString()); }); } catch (Exception e) { System.err.println(Error during Elasticsearch operation: ); e.printStackTrace(); } finally { // 7. 关闭客户端 if (client ! null) { try { client.close(); System.out.println(Client closed.); } catch (Exception e) { e.printStackTrace(); } } } } }5. 常见问题排查与调试技巧实录即使按照上面的步骤操作你可能还是会遇到一些问题。下面是我在实战中遇到的一些典型错误及其解决方法。5.1 SSLHandshakeException: PKIX path building failed错误信息javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target原因分析这是最经典的错误意味着JDK的信任库里没有找到可以验证服务器证书的根证书。简单说就是客户端不信任服务器出示的证书。解决方案确认CA证书文件正确确保你传递给buildTrustingSSLContext方法的caCertificatePemFile确实是签署了Elasticsearch服务器证书的那个根CA证书。如果你用的是ES自签名的http_ca.crt那这个文件就是对的。检查证书链用openssl命令检查服务器证书的完整链。openssl s_client -connect your-es-host:9200 -showcerts /dev/null 2/dev/null | openssl x509 -text -noout查看输出中的Issuer字段。确保你的CA证书能匹配这个签发者。确保证书文件是PEM格式文件内容应该是-----BEGIN CERTIFICATE-----开头-----END CERTIFICATE-----结尾的文本。如果是二进制格式需要用openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM转换。5.2 IOException: Invalid keystore format错误信息在调用loadKeyMaterial或loadTrustMaterial时抛出。原因分析Apache HttpClient的SSLContextBuilder无法解析你提供的PEM文件。可能的原因文件路径错误。文件不是有效的PEM格式例如是二进制DER格式或者内容损坏。私钥文件不是PKCS#8格式。loadKeyMaterial方法期望私钥是-----BEGIN PRIVATE KEY-----PKCS#8格式。如果你的是-----BEGIN RSA PRIVATE KEY-----PKCS#1需要转换。解决方案# 将PKCS#1私钥转换为PKCS#8格式无加密 openssl pkcs8 -topk8 -in old_key.pem -out new_key.pem -nocrypt然后使用转换后的new_key.pem文件。5.3 连接超时或拒绝连接错误信息ConnectTimeoutException或Connection refused原因分析这不是SSL问题是网络层问题。Elasticsearch服务没有运行。防火墙阻止了9200端口。主机名或端口号配置错误。Elasticsearch配置绑定了127.0.0.1而不是0.0.0.0导致无法远程连接。解决方案先用curl -k https://localhost:9200在服务器本地测试ES是否正常响应。检查ES配置文件elasticsearch.yml中的network.host和http.port设置。确保客户端能通过网络访问到ES服务器的IP和端口。5.4 启用详细SSL日志进行调试当问题难以定位时开启JVM的SSL调试日志是终极武器。这能打印出TLS握手的所有细节。方法在启动Java程序时添加JVM参数-Djavax.net.debugssl:handshake:verbose或者更详细的-Djavax.net.debugall查看日志你会在控制台看到大量输出重点关注以下部分“trustStore is: ...”客户端使用的信任库是什么。“adding as trusted cert: ...”加载了哪些受信任的证书。“Server’s certificate chain:”服务器发送的证书链。“PKIX path building failed”具体在哪一步验证失败。通过对比服务器发送的证书链和客户端加载的信任证书就能精确找到问题所在。6. 生产环境进阶考量与优化上面的示例足以让你在开发和测试环境跑通。但如果要上生产还有几个关键点需要考虑。6.1 证书管理不要硬编码路径绝对不要在代码里硬编码证书文件的路径。推荐的做法环境变量通过环境变量指定证书路径。String caCertPath System.getenv(ES_CA_CERT_PATH);配置文件使用Spring Boot的ConfigurationProperties、Micronaut的EachProperty或简单的.properties/.yaml文件来配置。类路径资源将证书文件打包在JAR内的资源目录下通过ClassLoader.getResourceAsStream()读取。但要注意保护私钥的安全。密钥管理服务KMS在云环境或大型系统中使用AWS KMS、HashiCorp Vault等服务动态获取证书和私钥而不是存放在文件系统中。6.2 性能与连接池RestHighLevelClient底层使用Apache HttpClient的连接池。默认设置可能不适合高并发场景。最大连接数通过RestClientBuilder.setHttpClientConfigCallback配置PoolingHttpClientConnectionManager。存活时间设置连接存活时间避免使用陈旧的连接。超时设置我们已经设置了连接和Socket超时还可以根据业务需要设置连接请求超时。PoolingHttpClientConnectionManager connectionManager new PoolingHttpClientConnectionManager(); connectionManager.setMaxTotal(100); // 最大总连接数 connectionManager.setDefaultMaxPerRoute(50); // 每个路由目标主机的最大连接数 httpClientBuilder.setConnectionManager(connectionManager);6.3 兼容JDK 8u161之前的版本如果你被迫使用非常老的JDK8如8u60可能会因为缺少某些TLS相关的算法而导致握手失败。解决方案是使用更全面的安全提供者比如Bouncy Castle。添加Bouncy Castle依赖。dependency groupIdorg.bouncycastle/groupId artifactIdbcpkix-jdk15on/artifactId version1.70/version !-- 使用较新版本 -- /dependency在程序启动时或者在构建SSLContext之前动态添加Bouncy Castle提供者。import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.Security; Security.addProvider(new BouncyCastleProvider());然后在SSLContextBuilder创建时可以指定使用“BC”提供者或者让JDK自动选择最合适的。6.4 向新版本客户端迁移Elasticsearch官方正在推动向新的Java API Client基于elasticsearch-java库迁移。新客户端同样支持自定义HTTP客户端配置。其思路是类似的构建一个javax.net.ssl.SSLContext然后将其嵌入到org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder中最后通过RestClientTransport构建新的客户端。虽然API不同但处理PEM证书和SSLContext的核心逻辑完全通用。当你未来决定升级客户端时今天掌握的SSLContext构建方法依然是最宝贵的资产。整个过程下来核心思路就是绕过JDK8对特定证书格式的严格校验通过更灵活的PEM文件和强大的Apache HttpClient工具库自己动手构建一个兼容的SSL连接环境。这套方案不仅适用于Elasticsearch任何基于HTTP/HTTPS的、需要与JDK8兼容的Java客户端连接问题如某些老旧的WebService、API网关等都可以借鉴这个思路。希望这篇超详细的指南能帮你彻底告别JDK8下的HTTPS连接烦恼。