跳转到内容

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 证书:

Terminal window
# 方法 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 + openssl
echo "Q" | openssl s_client -connect xxx.com -showcerts 2>/dev/null | \
openssl x509 -out /tmp/elasticsearch-cert.pem

查看导出的证书:

Terminal window
cat /tmp/elasticsearch-cert.pem

输出示例:

-----BEGIN CERTIFICATE-----
MIIDcTCCAlmgAwIBAgIUCRpw+Jo0Abs5Kojji5D3s5zvhZgwDQYJKoZIhvcNAQEL
...
-----END CERTIFICATE-----

步骤 2:导入证书到 Java Truststore

使用 keytool 创建 truststore 并导入证书:

Terminal window
# 创建 truststore 并导入证书
keytool -import -alias elasticsearch \
-file /tmp/elasticsearch-cert.pem \
-keystore elasticsearch-truststore.jks \
-storepass changeit \
-noprompt

参数说明:

参数说明
-aliaselasticsearch证书的别名,可自定义
-file/tmp/elasticsearch-cert.pem证书文件路径
-keystoreelasticsearch-truststore.jks输出的 truststore 文件名
-storepasschangeittruststore 密码(建议修改)
-noprompt-自动确认,不提示用户

验证证书已导入:

Terminal window
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 放入项目资源目录

Terminal window
# 复制到项目的 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.enabledtrue启用 SSL 验证
truststoreclasspath:elasticsearch-truststore.jks从 classpath 加载 truststore
truststore-passwordchangeittruststore 密码

步骤 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;
@Configuration
public 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 路径

Terminal window
# 查看 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:导入证书

Terminal window
# Linux/macOS
sudo keytool -import -alias elasticsearch \
-file /tmp/elasticsearch-cert.pem \
-keystore $JAVA_HOME/lib/security/cacerts \
-storepass changeit \
-noprompt
# macOS Homebrew Java
sudo 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:验证导入

Terminal window
keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit | grep elasticsearch

优点: 所有 Java 应用自动信任该证书 缺点: 需要管理员权限;影响系统全局


方案三:运行时指定 JVM 参数

此方案无需修改代码,通过 JVM 参数指定 truststore。

命令行运行

Terminal window
# 指定 truststore 路径和密码
java -Djavax.net.ssl.trustStore=/path/to/elasticsearch-truststore.jks \
-Djavax.net.ssl.trustStorePassword=changeit \
-jar your-application.jar

Maven 运行

Terminal window
mvn spring-boot:run \
-Djavax.net.ssl.trustStore=/path/to/elasticsearch-truststore.jks \
-Djavax.net.ssl.trustStorePassword=changeit

Maven 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 运行

# Dockerfile
COPY 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:证书过期

Terminal window
# 检查证书有效期
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 时使用的密码一致。


安全最佳实践

  1. 生产环境:使用方案一(自定义 truststore)或方案二(系统 cacerts)
  2. 密码安全:修改默认密码 changeit 为强密码
  3. 证书管理:定期检查证书有效期,及时更新
  4. 权限控制:限制 truststore 文件的访问权限(chmod 600
  5. 环境隔离:不同环境使用不同的 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 ← 备份

相关资源