httpclient向HTTPS发送数据建立SSL连接时的错误

    xiaoxiao2022-07-02  140

    httpclient向HTTPS发送数据建立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

     

    原因:服务器的证书不被信任。一般是这样造成的。

    使用KEYTOOL工具创建证书,然后用TOMCAT启动后,在浏览器打开网站时,会出现证书不被信任的提示。当然,利用HTTPCLIENT向服务端HTTPS发送数据时,HTTPCLIENT也会检测服务端的证书是否被信任,不被信任就抛出上面的异常。

    解决办法有两种,一种是使证书被客户端信任。另一种是使用HTTPCLIENT发送数据时不检测服务器证书是否可信。

     

    第一种办法,使证书被信任。

     

    找正规CA签发证书,或者自己签发证书(只能那一台客户机上可信)。找正规CA签发证书就不说了,自己签发证书呢,见我的其他文章。

     

    我发现,自己签名的证书弄好之后,从客户端打开服务端地址时,不再提示上面的错误,但是还是不能发送数据。原因是什么呢?因为那台证书在客户端操作系统上可信,但是在JAVA的KEYSTORE里不可信,要把服务端的证书导入KEYSTORE库中

     

    导入办法:

    打开命令行窗口,并到<java-home>\lib\security\ 目录下,运行下面的命令:

    keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file your.cer

     

    your.cer是服务端导出的证书,其他可以默认。

     

    第二种办法,使用HTTPCLIENT时不检测服务器证书是否可信

     

    扩展HttpClient 类实现自动接受证书

     

    因为这种方法自动接收所有证书,因此存在一定的安全问题,所以在使用这种方法前请仔细考虑您的系统的安全需求。具体的步骤如下:

     

    •提供一个自定义的socket factory (test.MySecureProtocolSocketFactory )。这个自定义的类必须实现接口org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory ,在实现接口的类中调用自定义的X509TrustManager(test.MyX509TrustManager) ,这两个类可以在随本文带的附件中得到

    •创建一个org.apache.commons.httpclient.protocol.Protocol 的实例,指定协议名称和默认的端口号

    Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory (), 443);

     

    •注册刚才创建的https 协议对象

    Protocol.registerProtocol("https ", myhttps);

     

    •然后按照普通编程 方式打开https 的目标地址,代码如下:    

    package com.nuc.zp.domain; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.CoreConnectionPNames; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @SuppressWarnings("deprecation") public class SSLClient extends DefaultHttpClient { private static final Logger logger = LoggerFactory.getLogger(SSLClient.class); private static volatile SSLClient httpClient = null; public static SSLClient getSingletonHttpClient() { if (httpClient == null) { synchronized (SSLClient.class) { if (httpClient == null) { try { httpClient = new SSLClient(); httpClient.init(); } catch (Exception e) { logger.error("build httpClient occur exception:{}", e.getMessage()); } } } } return httpClient; } private SSLClient() throws Exception { super(new ThreadSafeClientConnManager()); SSLContext ctx = SSLContext.getInstance("TLS"); X509TrustManager tm = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }; ctx.init(null, new TrustManager[]{tm}, null); SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); ClientConnectionManager ccm = this.getConnectionManager(); SchemeRegistry sr = ccm.getSchemeRegistry(); sr.register(new Scheme("https", 443, ssf)); } private void init() { if (httpClient != null) { httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 3000); httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 5000); } } public void shutDown() { if (httpClient != null) { httpClient.close(); httpClient = null; } } }

     

    本文参考自:https://blog.csdn.net/xuguokun1986/article/details/51398088

    最新回复(0)