如何为camel-https4配置trustStore并解决SSL握手异常
解决Apache Camel HTTPS4组件的PKIX SSL握手错误
问题回顾
你配置了Apache Camel的HTTPS4路由,代码如下:
@Component public class MyRouteDefinition extends RouteBuilder { @Override public void configure() throws Exception { KeyStoreParameters ksp = new KeyStoreParameters(); ksp.setType("pkcs12"); ksp.setResource(PushRoutesDefinition.class.getResource("/keystore.p12").getPath()); ksp.setPassword("password"); KeyManagersParameters kmp = new KeyManagersParameters(); kmp.setKeyStore(ksp); kmp.setKeyPassword("password"); SSLContextParameters scp = new SSLContextParameters(); scp.setKeyManagers(kmp); HttpComponent httpComponent = getContext().getComponent("https4", HttpComponent.class); httpComponent.setSslContextParameters(scp); //TO HTTPS from(...) .to("https4://localhost:8080/load") .process(exchange -> { Integer responseCode = exchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class); log.debug("The response code is: {}", responseCode); }); } }
但遇到了以下SSL握手错误:
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 ...(省略后续堆栈信息)
错误原因
这个错误的核心是:你的应用程序的Java信任库中没有包含目标服务器(localhost:8080)的SSL证书。你当前的配置只设置了客户端用于身份验证的KeyManagers,但没有配置TrustManagers来信任服务器的证书,导致Java无法验证服务器的身份,从而抛出PKIX路径构建失败的异常。
解决方案
下面提供几种不同场景下的解决方法,你可以根据实际情况选择:
方法1:将服务器证书导入Java默认信任库(推荐生产环境)
这是最标准的做法,把服务器的SSL证书添加到Java的全局信任库中:
- 先获取服务器的证书:
openssl s_client -connect localhost:8080 </dev/null | openssl x509 -outform PEM > server.crt - 使用
keytool将证书导入到Java默认信任库(默认路径是$JAVA_HOME/jre/lib/security/cacerts,默认密码是changeit):keytool -importcert -file server.crt -alias my-local-server -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit - 重启你的应用程序,让新的信任库配置生效。
方法2:在Camel路由中配置自定义信任库(灵活,适合多环境)
如果不想修改全局信任库,可以创建一个自定义的信任库,然后在Camel的SSL配置中指定它:
- 先创建包含服务器证书的PKCS12格式信任库:
keytool -importcert -file server.crt -alias my-local-server -keystore truststore.p12 -storetype pkcs12 -storepass trust-password - 修改你的Camel路由代码,添加信任库配置:
@Component public class MyRouteDefinition extends RouteBuilder { @Override public void configure() throws Exception { // 原有的客户端证书配置 KeyStoreParameters ksp = new KeyStoreParameters(); ksp.setType("pkcs12"); ksp.setResource(PushRoutesDefinition.class.getResource("/keystore.p12").getPath()); ksp.setPassword("password"); KeyManagersParameters kmp = new KeyManagersParameters(); kmp.setKeyStore(ksp); kmp.setKeyPassword("password"); // 添加信任库配置 KeyStoreParameters trustKsp = new KeyStoreParameters(); trustKsp.setType("pkcs12"); trustKsp.setResource(MyRouteDefinition.class.getResource("/truststore.p12").getPath()); trustKsp.setPassword("trust-password"); TrustManagersParameters tmp = new TrustManagersParameters(); tmp.setKeyStore(trustKsp); // 同时设置KeyManagers和TrustManagers SSLContextParameters scp = new SSLContextParameters(); scp.setKeyManagers(kmp); scp.setTrustManagers(tmp); HttpComponent httpComponent = getContext().getComponent("https4", HttpComponent.class); httpComponent.setSslContextParameters(scp); //TO HTTPS from(...) .to("https4://localhost:8080/load") .process(exchange -> { Integer responseCode = exchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class); log.debug("The response code is: {}", responseCode); }); } } - 将生成的
truststore.p12放到项目的资源目录下,重启应用即可。
方法3:禁用证书验证(仅测试环境使用,生产绝对禁止)
如果是本地测试环境,临时需要绕过证书验证,可以用这种方法,但生产环境严禁使用,会带来严重的安全风险:
@Component public class MyRouteDefinition extends RouteBuilder { @Override public void configure() throws Exception { // 创建忽略证书验证的SSLContext SSLContext sslContext = SSLContexts.custom() // 加载客户端证书 .loadKeyMaterial( KeyStore.getInstance(new File(PushRoutesDefinition.class.getResource("/keystore.p12").getPath()), "password".toCharArray()), "password".toCharArray()) // 信任所有证书 .loadTrustMaterial((chain, authType) -> true) .build(); HttpComponent httpComponent = getContext().getComponent("https4", HttpComponent.class); httpComponent.setSslContext(sslContext); // 同时禁用主机名验证 httpComponent.setX509HostnameVerifier(new AllowAllHostnameVerifier()); //TO HTTPS from(...) .to("https4://localhost:8080/load") .process(exchange -> { Integer responseCode = exchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class); log.debug("The response code is: {}", responseCode); }); } }
内容的提问来源于stack exchange,提问作者gstackoverflow
相关产品推荐
相关产品推荐

