Springboot 信任 Elasticsearch SSL 证书配置指南
Java Springboot 信任 Elasticsearch SSL 证书配置指南
本文档详细说明如何配置 Java 应用程序信任使用 HTTPS + 自签名证书的 Elasticsearch。
问题背景
当 Elasticsearch 使用 HTTPS 协议且配置了自签名证书时,Java 客户端默认不信任该证书,会抛出以下错误:
javax.net.ssl.SSLHandshakeException: PKIX path building failed:sun.security.provider.certpath.SunCertPathBuilderException:unable to find valid certification path to requested target解决方案概览
| 方案 | 安全性 | 适用场景 | 难度 |
|---|---|---|---|
| 方案一:自定义 Truststore | ✅ 高 | 生产环境推荐 | 中等 |
| 方案二:系统 cacerts | ✅ 高 | 多应用共享同一 ES | 简单 |
| 方案三:JVM 参数 | ✅ 高 | 不修改代码的临时方案 | 简单 |
| 方案四:信任所有证书 | ❌ 低 | 仅开发/测试环境 | 简单 |
方案一:使用自定义 Truststore(推荐生产环境)
步骤 1:导出 Elasticsearch 证书
使用 openssl 命令导出 Elasticsearch 的 SSL 证书:
# 方法 A:使用 openssl(推荐)xxx.com为域名信息或id地址openssl s_client -connect xxx.com -showcerts </dev/null 2>/dev/null | \ openssl x509 -out /tmp/elasticsearch-cert.pem
# 方法 B:使用 echo + opensslecho "Q" | openssl s_client -connect xxx.com -showcerts 2>/dev/null | \ openssl x509 -out /tmp/elasticsearch-cert.pem查看导出的证书:
cat /tmp/elasticsearch-cert.pem输出示例:
-----BEGIN CERTIFICATE-----MIIDcTCCAlmgAwIBAgIUCRpw+Jo0Abs5Kojji5D3s5zvhZgwDQYJKoZIhvcNAQEL...-----END CERTIFICATE-----步骤 2:导入证书到 Java Truststore
使用 keytool 创建 truststore 并导入证书:
# 创建 truststore 并导入证书keytool -import -alias elasticsearch \ -file /tmp/elasticsearch-cert.pem \ -keystore elasticsearch-truststore.jks \ -storepass changeit \ -noprompt参数说明:
| 参数 | 值 | 说明 |
|---|---|---|
-alias | elasticsearch | 证书的别名,可自定义 |
-file | /tmp/elasticsearch-cert.pem | 证书文件路径 |
-keystore | elasticsearch-truststore.jks | 输出的 truststore 文件名 |
-storepass | changeit | truststore 密码(建议修改) |
-noprompt | - | 自动确认,不提示用户 |
验证证书已导入:
keytool -list -keystore elasticsearch-truststore.jks -storepass changeit输出示例:
密钥库类型: PKCS12密钥库提供方: SUN
您的密钥库包含 1 个条目
elasticsearch, 2026年1月18日, trustedCertEntry,证书指纹 (SHA-256): 6A:82:DF:26:32:B9:67:6D:22:76:FF:43:F8:A9:2F:FF:E7:CD:1E:8E...步骤 3:将 truststore 放入项目资源目录
# 复制到项目的 resources 目录cp elasticsearch-truststore.jks src/main/resources/项目结构:
elastic-demon/├── src/│ └── main/│ └── resources/│ ├── application.yml│ └── elasticsearch-truststore.jks ← 信任库文件步骤 4:配置 application.yml
spring: elasticsearch: uris: https://elasticsearch服务地址 connection-timeout: 10s socket-timeout: 30s username: ${ELASTICSEARCH_USERNAME:elastic} password: ${ELASTICSEARCH_PASSWORD:your-password} # SSL 配置 ssl: enabled: true truststore: classpath:elasticsearch-truststore.jks truststore-password: changeit配置说明:
| 配置项 | 值 | 说明 |
|---|---|---|
ssl.enabled | true | 启用 SSL 验证 |
truststore | classpath:elasticsearch-truststore.jks | 从 classpath 加载 truststore |
truststore-password | changeit | truststore 密码 |
步骤 5:ElasticsearchConfig 配置
完整的 ElasticsearchConfig.java 实现:
package com.elasticdemon.config;
import co.elastic.clients.elasticsearch.ElasticsearchClient;import co.elastic.clients.json.jackson.JacksonJsonpMapper;import co.elastic.clients.transport.rest_client.RestClientTransport;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.ssl.SSLContextBuilder;import org.elasticsearch.client.RestClient;import org.elasticsearch.client.RestClientBuilder;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;
import javax.net.ssl.SSLContext;import java.io.File;import java.io.InputStream;import java.nio.file.Files;import java.nio.file.Paths;import java.nio.file.StandardCopyOption;
@Configurationpublic class ElasticsearchConfig {
@Value("${spring.elasticsearch.uris}") private String elasticsearchUrl;
@Value("${spring.elasticsearch.username:}") private String username;
@Value("${spring.elasticsearch.password:}") private String password;
@Value("${spring.elasticsearch.ssl.enabled:false}") private boolean sslEnabled;
@Value("${spring.elasticsearch.ssl.truststore:}") private String truststorePath;
@Value("${spring.elasticsearch.ssl.truststore-password:changeit}") private String truststorePassword;
@Bean public ElasticsearchClient elasticsearchClient() { RestClientBuilder builder = RestClient.builder( HttpHost.create(elasticsearchUrl) );
// 配置 SSL if (sslEnabled && !truststorePath.isEmpty()) { try { File truststoreFile;
if (truststorePath.startsWith("classpath:")) { // 从 classpath 加载 String resourcePath = truststorePath.substring("classpath:".length()); ClassPathResource resource = new ClassPathResource(resourcePath);
if (resource.exists()) { // 创建临时文件 truststoreFile = File.createTempFile("elasticsearch-truststore", ".jks"); truststoreFile.deleteOnExit();
try (InputStream is = resource.getInputStream()) { Files.copy(is, truststoreFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } } else { throw new RuntimeException("Truststore file not found: " + resourcePath); } } else { // 从文件系统加载 truststoreFile = Paths.get(truststorePath).toFile(); }
// 使用 File 加载 KeyStore SSLContext sslContext = SSLContextBuilder.create() .loadTrustMaterial(truststoreFile, truststorePassword.toCharArray()) .build();
builder.setHttpClientConfigCallback(httpClientBuilder -> { httpClientBuilder.setSSLContext(sslContext);
// 配置认证 if (username != null && !username.isEmpty()) { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials(username, password) ); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); }
return httpClientBuilder; });
} catch (Exception e) { throw new RuntimeException("Failed to configure SSL context", e); } } else { // Fallback:信任所有证书(仅开发环境) try { SSLContext sslContext = SSLContextBuilder.create() .loadTrustMaterial(null, (chains, authType) -> true) .build();
builder.setHttpClientConfigCallback(httpClientBuilder -> { httpClientBuilder.setSSLContext(sslContext); httpClientBuilder.setSSLHostnameVerifier((hostname, session) -> true);
if (username != null && !username.isEmpty()) { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials(username, password) ); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); }
return httpClientBuilder; }); } catch (Exception e) { throw new RuntimeException("Failed to configure SSL context", e); } }
RestClientTransport transport = new RestClientTransport( builder.build(), new JacksonJsonpMapper() );
return new ElasticsearchClient(transport); }}方案二:导入到系统默认 Truststore(全局生效)
此方案将证书导入到 Java 默认的 cacerts 文件,对所有 Java 应用生效。
步骤 1:导出证书
同方案一步骤 1。
步骤 2:查找 Java cacerts 路径
# 查看 Java 路径echo $JAVA_HOME
# 或使用java -version常见路径:
| 系统 | 路径 |
|---|---|
| Linux | $JAVA_HOME/lib/security/cacerts |
| macOS (Homebrew) | /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home/lib/security/cacerts |
| Windows | %JAVA_HOME%\lib\security\acerts |
步骤 3:导入证书
# Linux/macOSsudo keytool -import -alias elasticsearch \ -file /tmp/elasticsearch-cert.pem \ -keystore $JAVA_HOME/lib/security/cacerts \ -storepass changeit \ -noprompt
# macOS Homebrew Javasudo keytool -import -alias elasticsearch \ -file /tmp/elasticsearch-cert.pem \ -keystore /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home/lib/security/cacerts \ -storepass changeit \ -noprompt
# Windows(以管理员身份运行 CMD)keytool -import -alias elasticsearch \ -file C:\temp\elasticsearch-cert.pem \ -keystore "%JAVA_HOME%\lib\security\cacerts" \ -storepass changeit \ -noprompt步骤 4:验证导入
keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit | grep elasticsearch优点: 所有 Java 应用自动信任该证书 缺点: 需要管理员权限;影响系统全局
方案三:运行时指定 JVM 参数
此方案无需修改代码,通过 JVM 参数指定 truststore。
命令行运行
# 指定 truststore 路径和密码java -Djavax.net.ssl.trustStore=/path/to/elasticsearch-truststore.jks \ -Djavax.net.ssl.trustStorePassword=changeit \ -jar your-application.jarMaven 运行
mvn spring-boot:run \ -Djavax.net.ssl.trustStore=/path/to/elasticsearch-truststore.jks \ -Djavax.net.ssl.trustStorePassword=changeitMaven pom.xml 配置
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <jvmArguments> -Djavax.net.ssl.trustStore=${project.basedir}/src/main/resources/elasticsearch-truststore.jks -Djavax.net.ssl.trustStorePassword=changeit </jvmArguments> </configuration></plugin>Docker 运行
# DockerfileCOPY elasticsearch-truststore.jks /app/
ENV JAVA_OPTS="-Djavax.net.ssl.trustStore=/app/elasticsearch-truststore.jks -Djavax.net.ssl.trustStorePassword=changeit"
CMD ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"]方案四:信任所有证书(仅开发环境)
⚠️ 警告:此方案存在安全风险,仅用于开发/测试环境,严禁用于生产环境!
SSLContext sslContext = SSLContextBuilder.create() .loadTrustMaterial(null, (chains, authType) -> true) // 信任所有证书 .build();
builder.setHttpClientConfigCallback(httpClientBuilder -> { httpClientBuilder.setSSLContext(sslContext); httpClientBuilder.setSSLHostnameVerifier((hostname, session) -> true); // 跳过主机名验证 return httpClientBuilder;});常用命令参考
keytool 命令
| 命令 | 说明 |
|---|---|
keytool -import -alias <name> -file <cert> -keystore <jks> | 导入证书 |
keytool -list -keystore <jks> | 列出证书 |
keytool -delete -alias <name> -keystore <jks> | 删除证书 |
keytool -export -alias <name> -keystore <jks> -file <cert> | 导出证书 |
openssl 命令
| 命令 | 说明 |
|---|---|
openssl s_client -connect <host>:<port> -showcerts | 获取证书 |
openssl x509 -in <cert> -text -noout | 查看证书详情 |
openssl x509 -in <pem> -out <der> -outform der | 转换证书格式 |
故障排查
问题 1:证书过期
# 检查证书有效期openssl x509 -in elasticsearch-cert.pem -noout -dates
# 输出示例:# notBefore=Jan 17 08:36:16 2026 GMT# notAfter=Jan 17 08:36:16 2031 GMT解决: 重新导出当前有效的证书。
问题 2:主机名不匹配
错误信息:Certificate hostname verification failed
解决: 确保证书的 CN (Common Name) 或 SAN (Subject Alternative Name) 包含访问的主机名。
问题 3:找不到 truststore 文件
错误信息:Truststore file not found
解决:
- 检查路径是否正确
- 确认文件已复制到
src/main/resources/ - 重新编译项目:
mvn clean package
问题 4:密码错误
错误信息:Keystore was tampered with, or password was incorrect
解决: 确认 truststore-password 与创建 truststore 时使用的密码一致。
安全最佳实践
- 生产环境:使用方案一(自定义 truststore)或方案二(系统 cacerts)
- 密码安全:修改默认密码
changeit为强密码 - 证书管理:定期检查证书有效期,及时更新
- 权限控制:限制 truststore 文件的访问权限(
chmod 600) - 环境隔离:不同环境使用不同的 truststore
附录:完整项目结构
elastic-demon/├── pom.xml├── src/│ ├── main/│ │ ├── java/│ │ │ └── com/elasticdemon/│ │ │ ├── config/│ │ │ │ └── ElasticsearchConfig.java ← SSL 配置│ │ │ ├── controller/│ │ │ ├── model/│ │ │ └── service/│ │ └── resources/│ │ ├── application.yml ← ES 配置│ │ └── elasticsearch-truststore.jks ← 信任库│ └── test/└── elasticsearch-truststore.jks ← 备份