Java的HttpClient如何去支持无证书访问https

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java的HttpClient如何去支持无证书访问https相关的知识,希望对你有一定的参考价值。

项目里需要访问其他接口,通过http/https协议。我们一般是用HttpClient类来实现具体的http/https协议接口的调用。

// Init a HttpClient
HttpClient client = new HttpClient();
String url=http://www.xxx.com/xxx;

// Init a HttpMethod
HttpMethod get = new GetMethod(url);
get.setDoAuthentication(true);
get.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(1, false));

// Call http interface
try
client.executeMethod(get);

// Handle the response from http interface
InputStream in = get.getResponseBodyAsStream();
SAXReader reader = new SAXReader();
Document doc = reader.read(in);
finally
// Release the http connection
get.releaseConnection();


以上代码在通过普通的http协议是没有问题的,但如果是https协议的话,就会有证书文件的要求了。一般情况下,是这样去做的。

// Init a HttpClient
HttpClient client = new HttpClient();
String url=https://www.xxx.com/xxx;

if (url.startsWith("https:"))
System.setProperty("javax.net.ssl.trustStore", "/.sis.cer");
System.setProperty("javax.net.ssl.trustStorePassword", "public");


于是,这里就需要事先生成一个.sis.cer的文件,生成这个文件的方法一般是先通过浏览器访问https://,导出证书文件,再用JAVA keytool command 生成证书

# $JAVA_HOME/bin/keytool -import -file sis.cer -keystore .sis.cer

但这样做,一比较麻烦,二来证书也有有效期,过了有效期之后,又需要重新生成一次证书。如果能够避开生成证书文件的方式来使用https的话,就比较好了。

还好,在最近的项目里,我们终于找到了方法。

// Init a HttpClient
HttpClient client = new HttpClient();
String url=https://www.xxx.com/xxx;

if (url.startsWith("https:"))
this.supportSSL(url, client);


用到了supportSSL(url, client)这个方法,看看这个方法是如何实现的。

private void supportSSL(String url, HttpClient client)
if(StringUtils.isBlank(url))
return;

String siteUrl = StringUtils.lowerCase(url);
if (!(siteUrl.startsWith("https")))
return;


try
setSSLProtocol(siteUrl, client);
catch (Exception e)
logger.error("setProtocol error ", e);

Security.setProperty( "ssl.SocketFactory.provider",
"com.tool.util.DummySSLSocketFactory");


private static void setSSLProtocol(String strUrl, HttpClient client) throws Exception

URL url = new URL(strUrl);
String host = url.getHost();
int port = url.getPort();

if (port <= 0)
port = 443;

ProtocolSocketFactory factory = new SSLSocketFactory();
Protocol authhttps = new Protocol("https", factory, port);
Protocol.registerProtocol("https", authhttps);
// set https protocol
client.getHostConfiguration().setHost(host, port, authhttps);


在supportSSL方法里,调用了Security.setProperty( "ssl.SocketFactory.provider",
"com.tool.util.DummySSLSocketFactory");
那么这个com.tool.util.DummySSLSocketFactory是这样的:
访问https 资源时,让httpclient接受所有ssl证书,在weblogic等容器中很有用
代码如下:
1. import java.io.IOException;
2. import java.net.InetAddress;
3. import java.net.InetSocketAddress;
4. import java.net.Socket;
5. import java.net.SocketAddress;
6. import java.net.UnknownHostException;
7. import java.security.KeyManagementException;
8. import java.security.NoSuchAlgorithmException;
9. import java.security.cert.CertificateException;
10. import java.security.cert.X509Certificate;
11.
12. import javax.net.SocketFactory;
13. import javax.net.ssl.SSLContext;
14. import javax.net.ssl.TrustManager;
15. import javax.net.ssl.X509TrustManager;
16.
17. import org.apache.commons.httpclient.ConnectTimeoutException;
18. import org.apache.commons.httpclient.params.HttpConnectionParams;
19. import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
20.
21. public class MySecureProtocolSocketFactory implements SecureProtocolSocketFactory
22. static
23. System.out.println(">>>>in MySecureProtocolSocketFactory>>");
24.
25. private SSLContext sslcontext = null;
26.
27. private SSLContext createSSLContext()
28. SSLContext sslcontext=null;
29. try
30. sslcontext = SSLContext.getInstance("SSL");
31. sslcontext.init(null, new TrustManager[]new TrustAnyTrustManager(), new java.security.SecureRandom());
32. catch (NoSuchAlgorithmException e)
33. e.printStackTrace();
34. catch (KeyManagementException e)
35. e.printStackTrace();
36.
37. return sslcontext;
38.
39.
40. private SSLContext getSSLContext()
41. if (this.sslcontext == null)
42. this.sslcontext = createSSLContext();
43.
44. return this.sslcontext;
45.
46.
47. public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
48. throws IOException, UnknownHostException
49. return getSSLContext().getSocketFactory().createSocket(
50. socket,
51. host,
52. port,
53. autoClose
54. );
55.
56.
57. public Socket createSocket(String host, int port) throws IOException,
58. UnknownHostException
59. return getSSLContext().getSocketFactory().createSocket(
60. host,
61. port
62. );
63.
64.
65.
66. public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort)
67. throws IOException, UnknownHostException
68. return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
69.
70.
71. public Socket createSocket(String host, int port, InetAddress localAddress,
72. int localPort, HttpConnectionParams params) throws IOException,
73. UnknownHostException, ConnectTimeoutException
74. if (params == null)
75. throw new IllegalArgumentException("Parameters may not be null");
76.
77. int timeout = params.getConnectionTimeout();
78. SocketFactory socketfactory = getSSLContext().getSocketFactory();
79. if (timeout == 0)
80. return socketfactory.createSocket(host, port, localAddress, localPort);
81. else
82. Socket socket = socketfactory.createSocket();
83. SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
84. SocketAddress remoteaddr = new InetSocketAddress(host, port);
85. socket.bind(localaddr);
86. socket.connect(remoteaddr, timeout);
87. return socket;
88.
89.
90.
91. //自定义私有类
92. private static class TrustAnyTrustManager implements X509TrustManager
93.
94. public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException
95.
96.
97. public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException
98.
99.
100. public X509Certificate[] getAcceptedIssuers()
101. return new X509Certificate[];
102.
103.
104.
105.

public class MySecureProtocolSocketFactory implements SecureProtocolSocketFactory
static
System.out.println(">>>>in MySecureProtocolSocketFactory>>");

private SSLContext sslcontext = null;

private SSLContext createSSLContext()
SSLContext sslcontext=null;
try
sslcontext = SSLContext.getInstance("SSL");
sslcontext.init(null, new TrustManager[]new TrustAnyTrustManager(), new java.security.SecureRandom());
catch (NoSuchAlgorithmException e)
e.printStackTrace();
catch (KeyManagementException e)
e.printStackTrace();

return sslcontext;


private SSLContext getSSLContext()
if (this.sslcontext == null)
this.sslcontext = createSSLContext();

return this.sslcontext;


public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
throws IOException, UnknownHostException
return getSSLContext().getSocketFactory().createSocket(
socket,
host,
port,
autoClose
);


public Socket createSocket(String host, int port) throws IOException,
UnknownHostException
return getSSLContext().getSocketFactory().createSocket(
host,
port

然后按如下方式使用HttpClient
Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory (), 443);
Protocol.registerProtocol("https", myhttps);
HttpClient httpclient=new HttpClient();
参考技术A SSL 连接的 context 目前用那个 KeyManager, TrustManager 的实现(Sun 公司提供的)都是默认地从命令行提供的参数或代码中明确初始化的 trust manager / key manager 中查找的,这里面可以肯定的是这个参数在连接建立之前已经固定了的静态形式,这要求我们把可以信任的服务器证书的颁发机构的根证书先导入到 trust store 中然后指派给 java 程序。

因此当我们的服务器使用了一张不在 trust store 中的信任根证书机构名录中的证书是不会被信任的,因此我们必须提供一个有别于 Sun 的 Trust Manager 接口的实现类,然后像 IE 浏览器那样在查找证书来确认是否信任时弹出一个对话框出来让用户检查,当用户点击“以后一直信任该证书的厂商”时我们把它缓存在持久性介质(比如文件或数据库)中,下次访问时先看是否有缓存的,没有再来弹对话框让用户确认,当服务器要求客户端出示客户端证书时(比如网银业务)我们同样需要提供自己的 key manager 实现。本回答被提问者和网友采纳

以上是关于Java的HttpClient如何去支持无证书访问https的主要内容,如果未能解决你的问题,请参考以下文章

用java做一个httpClient 发送https 的get请求,需要证书验证的那种,求大神指点一下!

HttpClient配置SSL绕过https证书

Android - 如何使用 HttpClient 接受任何证书并同时传递证书

httpclient 需要关闭吗

HttpClient 怎么忽略证书验证访问https

解决httpclient访问ssl资源报证书错误的问题