如何配置reactor.netty.http.client.HttpClient接受自签名证书
问题说明
使用reactor.netty.http.client.HttpClient请求走HTTPS协议、使用自签名证书的对外服务时,程序抛出SSL握手异常,核心报错栈如下:
Caused by: javax.net.ssl.SSLHandshakeException: General OpenSslEngine problem at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.handshakeException(ReferenceCountedOpenSslEngine.java:1895) at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.wrap(ReferenceCountedOpenSslEngine.java:822) at javax.net.ssl.SSLEngine.wrap(SSLEngine.java:509) at io.netty.handler.ssl.SslHandler.wrap(SslHandler.java:1040) at io.netty.handler.ssl.SslHandler.wrapNonAppData(SslHandler.java:926) Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:397) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302) at sun.security.validator.Validator.validate(Validator.java:260) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:281) Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141) at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392) ... 36 more
当前使用的HttpClient配置代码如下:
HttpClient client = HttpClient.create(connectionProvider); if (isUseTLS) { Http11SslContextSpec http11SslContextSpec = Http11SslContextSpec.forClient(); client = client.secure(spec -> { spec.sslContext(http11SslContextSpec); }); }
需求为不通过keytool导入证书到密钥库的方式,纯代码配置HttpClient使其接受自签名证书。
解决方案
可以通过编码自定义SSL信任规则实现,按使用场景分为两类:
- 测试/非生产环境:配置信任所有证书(跳过证书校验),配置简单但存在安全风险,禁止用于生产环境
- 生产环境:加载指定的自签名证书作为信任根,仅信任目标服务的自签名证书,兼顾便利性和安全性
方案1:测试环境跳过所有证书校验
配置SslContext时指定信任所有证书、关闭主机名校验,代码示例:
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import reactor.netty.http.client.Http11SslContextSpec; // ... HttpClient client = HttpClient.create(connectionProvider); if (isUseTLS) { Http11SslContextSpec http11SslContextSpec = Http11SslContextSpec.forClient(); http11SslContextSpec.configure(sslContextBuilder -> { // 配置信任所有证书,不做证书链校验 sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE); }); client = client.secure(spec -> { spec.sslContext(http11SslContextSpec) // 关闭主机名校验 .handlerConfigurator(sslHandler -> sslHandler.setHostnameVerifier((hostname, session) -> true)); }); }
注意:该方案会完全跳过SSL证书校验逻辑,存在中间人攻击风险,仅限本地开发、测试环境临时使用。
方案2:生产环境加载指定自签名证书
将自签名证书文件(一般为crt/pem格式)放在项目资源目录,代码加载该证书作为信任锚,仅信任该自签名证书,不需要修改JDK密钥库配置,证书随项目部署,适合容器化、多环境部署场景,代码示例:
import java.io.InputStream; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import reactor.netty.http.client.Http11SslContextSpec; // ... HttpClient client = HttpClient.create(connectionProvider); if (isUseTLS) { // 从资源目录加载目标服务的自签名证书 try (InputStream certIs = getClass().getResourceAsStream("/your-self-signed-cert.crt")) { CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate trustedCert = (X509Certificate) cf.generateCertificate(certIs); Http11SslContextSpec http11SslContextSpec = Http11SslContextSpec.forClient(); http11SslContextSpec.configure(sslContextBuilder -> { // 仅信任加载的指定自签名证书 sslContextBuilder.trustManager(trustedCert); }); client = client.secure(spec -> spec.sslContext(http11SslContextSpec)); } catch (Exception e) { throw new RuntimeException("加载自签名证书失败", e); } }
内容的提问来源于stack exchange,提问作者wqliang
相关产品推荐
相关产品推荐

