这两天在做与渠道联调的回调,其中回调渠道的时候使用的是https。
简单的测试代码如下;
Java代码
- public static void simpleTest(String httpsURL) throws Exception {
- URL myurl = new URL(httpsURL);
- HttpsURLConnection con = (HttpsURLConnection) myurl.openConnection();
- InputStream ins = con.getInputStream();
- InputStreamReader isr = new InputStreamReader(ins);
- BufferedReader in = new BufferedReader(isr);
- String inputLine;
- while ((inputLine = in.readLine()) != null) {
- System.out.println(inputLine);
- }
- in.close();
- }
但是,如果为自签发的SSL证书(未提供证书发行链而不被信任)的请求地址,上面的代码就不起作用了。
错误为:
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。
经过查找资料,现有两种解决方案:
1)为自身系统增加证书
2)绕过证书验证
先来说第一种方案:
1)用IE打开访问的https网址,点击地址栏后面的小锁头(或证书错误标识),查看证书。在弹出的证书信息框中选择第二个页签“详细信息”。点击复制到文件,选择Base64编码的X.509证书导出即可。
2)把证书从其它文件导入到TrustStore文件中。
keytool -import -file D:/test1.cer -keystore D:/crt_test1
执行命令后要求输入密码,记住输入的密码。
3)编写如下代码:
Java代码
- public static void certTest1()throws Exception{
- String httpsURL = "https://xxx.xxx.cn/";
- String trustStor="D:/crt_test1";
- System.setProperty("javax.net.ssl.trustStore", trustStor);
- URL myurl = new URL(httpsURL);
- HttpsURLConnection con = (HttpsURLConnection) myurl.openConnection();
- con.setHostnameVerifier(hv);
- InputStream ins = con.getInputStream();
- InputStreamReader isr = new InputStreamReader(ins);
- BufferedReader in = new BufferedReader(isr);
- String inputLine=null;
- while ((inputLine = in.readLine()) != null) {
- System.out.println(inputLine);
- }
- in.close();
- }
- private static HostnameVerifier hv = new HostnameVerifier() {
- public boolean verify(String urlHostName, SSLSession session) {
- return urlHostName.equals(session.getPeerHost());
- }
- };
可是如果出现两个https地址并且证书不同的话,上面的代码就没有办法处理了。有人可能说用System.setProperty重新设置一下trustStore,很抱歉,我试了好几次,没有成功。如果他人有成功的请给出代码,谢谢。
为了处理两个证书的情况,我又查了下资料,整理一下,使用TrustManager来处理(此时会用到证书的密码)。
代码如下:
Java代码
- public static void certTest2(String certDir, String passwd, String urlStr)
- throws Exception {
- SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
- TrustManager[] tms = getTms(certDir, passwd);
- sslContext.init(null, tms, new java.security.SecureRandom());
- SSLSocketFactory ssf = sslContext.getSocketFactory();
- URL url = new URL(urlStr);
- HttpsURLConnection.setDefaultHostnameVerifier(hv);
- HttpsURLConnection conn = ((HttpsURLConnection) url.openConnection());
- conn.setSSLSocketFactory(ssf);
- InputStreamReader im = new InputStreamReader(conn.getInputStream(),
- "GBK");
- BufferedReader reader = new BufferedReader(im);
- StringBuffer sb = new StringBuffer();
- String line = null;
- while ((line = reader.readLine()) != null) {
- sb.append(line + "\r\n");
- }
- System.out.println(sb);
- }
- public static TrustManager[] getTms(String dir, String keyPassword)
- throws Exception {
- String talg = TrustManagerFactory.getDefaultAlgorithm();
- TrustManagerFactory tmFact = TrustManagerFactory.getInstance(talg);
- FileInputStream tfis = new FileInputStream(dir);
- KeyStore ts = KeyStore.getInstance("jks");
- ts.load(tfis, keyPassword.toCharArray());
- tfis.close();
- tmFact.init(ts);
- return tmFact.getTrustManagers();
- }
- private static HostnameVerifier hv = new HostnameVerifier() {
- public boolean verify(String urlHostName, SSLSession session) {
- return urlHostName.equals(session.getPeerHost());
- }
- };
再说第二种方案:
不多说,直接上代码
Java代码
- public static void withoutCertTest(String urlStr) throws Exception {
- SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
- TrustManager[] tms = { ignoreCertificationTrustManger };
- sslContext.init(null, tms, new java.security.SecureRandom());
- SSLSocketFactory ssf = sslContext.getSocketFactory();
- URL url = new URL(urlStr);
- HttpsURLConnection.setDefaultHostnameVerifier(hv);
- HttpsURLConnection conn = ((HttpsURLConnection) url.openConnection());
- conn.setSSLSocketFactory(ssf);
- InputStreamReader im = new InputStreamReader(conn.getInputStream(),
- "GBK");
- BufferedReader reader = new BufferedReader(im);
- StringBuffer sb = new StringBuffer();
- String line = null;
- while ((line = reader.readLine()) != null) {
- sb.append(line + "\r\n");
- }
- System.out.println(sb);
- }
- private static TrustManager ignoreCertificationTrustManger = new X509TrustManager() {
- private X509Certificate[] certificates;
- @Override
- public void checkClientTrusted(X509Certificate certificates[],
- String authType) throws CertificateException {
- if (this.certificates == null) {
- this.certificates = certificates;
- }
- }
- @Override
- public void checkServerTrusted(X509Certificate[] ax509certificate,
- String s) throws CertificateException {
- if (this.certificates == null) {
- this.certificates = ax509certificate;
- }
- }
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return null;
- }
- };
参考网址:
http://guoguanfei.blog.163.com/blog/static/555830372009217115420766/
http://blog.csdn.net/c_4818/article/details/7825388
http://www.wenhq.com/article/view_711.html
===========================================================
以上测试以后,与渠道调用的时候并没有成功,于是使用apache提供的HttpClients,问题解决。
代码如下:
Java代码
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.net.SocketTimeoutException;
- import java.security.GeneralSecurityException;
- import java.security.cert.CertificateException;
- import java.security.cert.X509Certificate;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import javax.net.ssl.SSLContext;
- import javax.net.ssl.SSLException;
- import javax.net.ssl.SSLSession;
- import javax.net.ssl.SSLSocket;
- import org.apache.commons.io.IOUtils;
- import org.apache.commons.lang.StringUtils;
- import org.apache.http.Consts;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpResponse;
- import org.apache.http.NameValuePair;
- import org.apache.http.client.HttpClient;
- import org.apache.http.client.config.RequestConfig;
- import org.apache.http.client.config.RequestConfig.Builder;
- import org.apache.http.client.entity.UrlEncodedFormEntity;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.conn.ConnectTimeoutException;
- import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
- import org.apache.http.conn.ssl.SSLContextBuilder;
- import org.apache.http.conn.ssl.TrustStrategy;
- import org.apache.http.conn.ssl.X509HostnameVerifier;
- import org.apache.http.entity.ContentType;
- import org.apache.http.entity.StringEntity;
- import org.apache.http.impl.client.CloseableHttpClient;
- import org.apache.http.impl.client.HttpClients;
- import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
- import org.apache.http.message.BasicNameValuePair;
- /**
- * HttpClientUtils, 使用 HttpClient 4.x<br>
- *
- */
- public class HttpClientUtils {
- private static HttpClient client = null;
- static {
- PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
- cm.setMaxTotal(128);
- cm.setDefaultMaxPerRoute(128);
- client = HttpClients.custom().setConnectionManager(cm).build();
- }
- /**
- * 发送一个 Post 请求, 使用指定的字符集编码.
- *
- * @param url
- * @param body
- * RequestBody
- * @param mimeType
- * 例如 application/xml
- * @param charset
- * 编码
- * @param connTimeout
- * 建立链接超时时间,毫秒.
- * @param readTimeout
- * 响应超时时间,毫秒.
- * @return ResponseBody, 使用指定的字符集编码.
- *
- * @throws ConnectTimeoutException
- * 建立链接超时异常
- * @throws SocketTimeoutException
- * 响应超时
- * @throws Exception
- */
- public static String post(String url, String body, String mimeType,
- String charset, Integer connTimeout, Integer readTimeout)
- throws ConnectTimeoutException, SocketTimeoutException, Exception {
- HttpClient client = null;
- HttpPost post = new HttpPost(url);
- String result = "";
- try {
- if (StringUtils.isNotBlank(body)) {
- HttpEntity entity = new StringEntity(body, ContentType.create(
- mimeType, charset));
- post.setEntity(entity);
- }
- // 设置参数
- Builder customReqConf = RequestConfig.custom();
- if (connTimeout != null) {
- customReqConf.setConnectTimeout(connTimeout);
- }
- if (readTimeout != null) {
- customReqConf.setSocketTimeout(readTimeout);
- }
- post.setConfig(customReqConf.build());
- HttpResponse res;
- if (url.startsWith("https")) {
- // 执行 Https 请求.
- client = createSSLInsecureClient();
- res = client.execute(post);
- } else {
- // 执行 Http 请求.
- client = HttpClientUtils.client;
- res = client.execute(post);
- }
- result = IOUtils.toString(res.getEntity().getContent(), charset);
- } finally {
- post.releaseConnection();
- if (url.startsWith("https") && client != null
- && client instanceof CloseableHttpClient) {
- ((CloseableHttpClient) client).close();
- }
- }
- return result;
- }
- /**
- * 提交form表单
- *
- * @param url
- * @param params
- * @param connTimeout
- * @param readTimeout
- * @return
- * @throws ConnectTimeoutException
- * @throws SocketTimeoutException
- * @throws Exception
- */
- public static String postForm(String url, Map<String, String> params,
- Map<String, String> headers, Integer connTimeout,
- Integer readTimeout) throws ConnectTimeoutException,
- SocketTimeoutException, Exception {
- HttpClient client = null;
- HttpPost post = new HttpPost(url);
- try {
- if (params != null && !params.isEmpty()) {
- List<NameValuePair> formParams = new ArrayList<org.apache.http.NameValuePair>();
- Set<Entry<String, String>> entrySet = params.entrySet();
- for (Entry<String, String> entry : entrySet) {
- formParams.add(new BasicNameValuePair(entry.getKey(), entry
- .getValue()));
- }
- UrlEncodedFormEntity entity = new UrlEncodedFormEntity(
- formParams, Consts.UTF_8);
- post.setEntity(entity);
- }
- if (headers != null && !headers.isEmpty()) {
- for (Entry<String, String> entry : headers.entrySet()) {
- post.addHeader(entry.getKey(), entry.getValue());
- }
- }
- // 设置参数
- Builder customReqConf = RequestConfig.custom();
- if (connTimeout != null) {
- customReqConf.setConnectTimeout(connTimeout);
- }
- if (readTimeout != null) {
- customReqConf.setSocketTimeout(readTimeout);
- }
- post.setConfig(customReqConf.build());
- HttpResponse res = null;
- if (url.startsWith("https")) {
- // 执行 Https 请求.
- client = createSSLInsecureClient();
- res = client.execute(post);
- } else {
- // 执行 Http 请求.
- client = HttpClientUtils.client;
- res = client.execute(post);
- }
- return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
- } finally {
- post.releaseConnection();
- if (url.startsWith("https") && client != null
- && client instanceof CloseableHttpClient) {
- ((CloseableHttpClient) client).close();
- }
- }
- }
- /**
- * 发送一个 GET 请求
- *
- * @param url
- * @param charset
- * @return
- * @throws Exception
- */
- public static String get(String url, String charset) throws Exception {
- return get(url, charset, null, null);
- }
- /**
- * 发送一个 GET 请求
- *
- * @param url
- * @param charset
- * @param connTimeout
- * 建立链接超时时间,毫秒.
- * @param readTimeout
- * 响应超时时间,毫秒.
- * @return
- * @throws ConnectTimeoutException
- * 建立链接超时
- * @throws SocketTimeoutException
- * 响应超时
- * @throws Exception
- */
- public static String get(String url, String charset, Integer connTimeout,
- Integer readTimeout) throws ConnectTimeoutException,
- SocketTimeoutException, Exception {
- HttpClient client = null;
- HttpGet get = new HttpGet(url);
- String result = "";
- try {
- // 设置参数
- Builder customReqConf = RequestConfig.custom();
- if (connTimeout != null) {
- customReqConf.setConnectTimeout(connTimeout);
- }
- if (readTimeout != null) {
- customReqConf.setSocketTimeout(readTimeout);
- }
- get.setConfig(customReqConf.build());
- HttpResponse res = null;
- if (url.startsWith("https")) {
- // 执行 Https 请求.
- client = createSSLInsecureClient();
- res = client.execute(get);
- } else {
- // 执行 Http 请求.
- client = HttpClientUtils.client;
- res = client.execute(get);
- }
- result = IOUtils.toString(res.getEntity().getContent(), charset);
- } finally {
- get.releaseConnection();
- if (url.startsWith("https") && client != null
- && client instanceof CloseableHttpClient) {
- ((CloseableHttpClient) client).close();
- }
- }
- return result;
- }
- /**
- * 从 response 里获取 charset
- *
- * @param ressponse
- * @return
- */
- @SuppressWarnings("unused")
- private static String getCharsetFromResponse(HttpResponse ressponse) {
- // Content-Type:text/html; charset=GBK
- if (ressponse.getEntity() != null
- && ressponse.getEntity().getContentType() != null
- && ressponse.getEntity().getContentType().getValue() != null) {
- String contentType = ressponse.getEntity().getContentType()
- .getValue();
- if (contentType.contains("charset=")) {
- return contentType
- .substring(contentType.indexOf("charset=") + 8);
- }
- }
- return null;
- }
- private static CloseableHttpClient createSSLInsecureClient()
- throws GeneralSecurityException {
- try {
- SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(
- null, new TrustStrategy() {
- public boolean isTrusted(X509Certificate[] chain,
- String authType) throws CertificateException {
- return true;
- }
- }).build();
- SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
- sslContext, new X509HostnameVerifier() {
- @Override
- public boolean verify(String arg0, SSLSession arg1) {
- return true;
- }
- @Override
- public void verify(String host, SSLSocket ssl)
- throws IOException {
- }
- @Override
- public void verify(String host, X509Certificate cert)
- throws SSLException {
- }
- @Override
- public void verify(String host, String[] cns,
- String[] subjectAlts) throws SSLException {
- }
- });
- return HttpClients.custom().setSSLSocketFactory(sslsf).build();
- } catch (GeneralSecurityException e) {
- throw e;
- }
- }
- public static void main(String[] args) {
- try {
- String xml = IOUtils.toString(new FileInputStream(new File(
- "D:\\hongkangtest.txt")));
- System.out
- .println(post(
- "https://trade.tongbanjie.com/insurance/callback/hk/redeem.htm",
- xml, "html/text", "GBK", 10000, 10000));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
依赖的jar包有:commons-lang-2.6.jar、httpclient-4.3.2.jar、httpcore-4.3.1.jar、commons-io-2.4.jar
原文:http://evan0625.iteye.com/blog/2064992