将IBMMQ(队列和主题)REST API转为Java时遇ValidatorException异常
IBM MQ REST API Java调用SSL握手异常解决方案
问题本质
你遇到的SSLHandshakeException是因为JVM默认不信任IBM MQ REST服务使用的SSL证书(大概率是自签名证书),导致无法完成SSL握手。而CURL能正常运行,是因为它默认跳过了证书验证环节,或者已经配置了信任该证书。
解决方案
方案1:测试环境临时绕过验证(禁止生产环境使用)
通过自定义SSL信任管理器,让Java代码信任所有证书,快速解决测试阶段的问题。修改后的完整代码如下:
import javax.net.ssl.*; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; public class IbmmqQueue { // 初始化信任所有证书的SSL上下文 static { try { TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) {} public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); // 禁用主机名验证 HostnameVerifier allHostsValid = (hostname, session) -> true; HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); } catch (NoSuchAlgorithmException | KeyManagementException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { URL url = new URL("https://localhost:9443/ibmmq/rest/v1/admin/qmgr/QM1/queue"); HttpURLConnection http = (HttpURLConnection) url.openConnection(); http.setRequestMethod("POST"); http.setDoOutput(true); http.setRequestProperty("Content-Type", "application/json"); http.setRequestProperty("license", "accept"); http.setRequestProperty("Authorization", "Basic YWRtaW46cGFzc3cwcmQ="); http.setRequestProperty("ibm-mq-rest-csrf-token", ";"); String data = "{\n \"name\": \"fourthQueue1\",\n \"type\": \"local\",\n \n \"storage\" : {\n \"maximumDepth\" : 5000\n }\n}"; byte[] out = data.getBytes(StandardCharsets.UTF_8); OutputStream stream = http.getOutputStream(); stream.write(out); System.out.println(http.getResponseCode() + " " + http.getResponseMessage()); http.disconnect(); } }
方案2:生产环境正规处理(推荐)
将IBM MQ的SSL证书导入到JVM的信任证书库中,让JVM合法信任该证书:
导出MQ REST服务的证书
执行以下OpenSSL命令(需安装OpenSSL):openssl s_client -connect localhost:9443 < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mq-rest-cert.crt将证书导入JVM信任库
找到你的JRE安装目录下的lib/security/cacerts文件,执行以下keytool命令(默认密码为changeit):keytool -import -alias mq-rest-cert -file mq-rest-cert.crt -keystore $JAVA_HOME/jre/lib/security/cacerts执行过程中输入
yes确认信任该证书。重启Java应用
让JVM加载更新后的信任库,之后再运行代码即可正常建立SSL连接。
附:原问题代码与异常信息
原Java代码
import javax.net.ssl.*; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; public class IbmmqQueue { public static void main(String[] args) throws IOException { URL url = new URL("https://localhost:9443/ibmmq/rest/v1/admin/qmgr/QM1/queue"); HttpURLConnection http = (HttpURLConnection)url.openConnection(); http.setRequestMethod("POST"); http.setDoOutput(true); http.setRequestProperty("Content-Type", "application/json"); http.setRequestProperty("license", "accept"); http.setRequestProperty("Authorization", "Basic YWRtaW46cGFzc3cwcmQ="); http.setRequestProperty("ibm-mq-rest-csrf-token", ";"); String data = "{\n \"name\": \"fourthQueue1\",\n \"type\": \"local\",\n \n \"storage\" : {\n \"maximumDepth\" : 5000\n }\n}"; byte[] out = data.getBytes(StandardCharsets.UTF_8); OutputStream stream = http.getOutputStream(); stream.write(out); System.out.println(http.getResponseCode() + " " + http.getResponseMessage()); http.disconnect(); } }
异常信息
Exception in thread "main" 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 at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1639) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037) at sun.security.ssl.Handshaker.process_record(Handshaker.java:965) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185) at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1334) at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1309) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:259) at IbmmqQueue.main(IbmmqQueue.java:30) 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:262) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1621) ... 13 more 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) ... 19 more
内容的提问来源于stack exchange,提问作者Shiva Subramaniam
相关产品推荐
相关产品推荐

