android HTTPURLConnection解决不能访问HTTPs请求
2017-02-27 19:16
477 查看
详细分析Android及Java中访问https请求exception(SSLHandshakeException, SSLPeerUnverifiedException)的原因及解决方法。
1、现象
用Android(或Java)测试程序访问下面两个链接。
https链接一:web服务器为jetty,后台语言为java。
https链接二:web服务器为nginx,后台语言为php。
链接一能正常访问,访问链接二报异常,且用HttpURLConnection和apache的HttpClient两种不同的api访问异常信息不同,具体如下:
(1) 用HttpURLConnection访问,测试代码如下:
HttpURLConnection访问https
Java
异常信息为:
(2) 用apache的HttpClient访问,测试代码如下:
HttpClient访问https
Java
异常信息为:
2、原因分析
需要快速寻求答案的可直接看第3部分 解决方式,这部分详细分析原因。
google发现stackoverflow上不少人反应,twitter和新浪微博的api也会报这个异常,不少人反映客户端需要导入证书,其实大可不必,如果要导证书的话,用户不得哭了。。
从上面的情况可以看出,用jetty做为容器是能正常访问的,只是当容器为nginx时才会异常。
配合后台开发调试了很久,开始以为是cipher suite的问题,为此特地把
ssl_ciphers EDH-RSA-DES-CBC3-SHA;
加入了nginx的配置中,后来发现依然无效。stackoverflow发现,如下代码是能正常访问上面异常的https url
HttpURLConnection访问https并相信所有证书
Java
可以看出其中与之前的HttpsURLConnection测试代码主要的不同就是加入了
和
表示相信所有证书,并且所有host name验证返回true,这样就能定位到之前的异常是证书验证不通过的问题了。
在上面checkServerTrusted函数中添加断点,查看X509Certificate[] chain的值,即证书信息,发现访问两个不同链接X509Certificate[] chain值有所区别,nginx传过来证书信息缺少了startssl 的ca证书,证书如下:
至此原因大白:
android的证书库里已经带了startssl
ca证书,而nginx默认不带startssl ca证书,这样android端访问nginx为容器的https url校验就会失败,jetty默认带startssl ca证书,所以正常。
PS:后来对windows和mac下java访问https也做了测试,发现mac上的jdk缺省不带startssl ca证书所以能访问通过,而加上startssl ca证书后同android一样访问不通过。而windows上的jdk缺省带startssl ca证书同android一样访问失败。
3、解决方式
上面的分析中已经介绍了一种解决方法即客户端相信所有证书,不过这种方式只是规避了问题,同时也给客户端带来了风险,比较合适的解决方式是为nginx添加startssl
ca证书,添加方法如下:
First, use the StartSSL™ Control Panel to create a private key and certificate and transfer them to your server. Then execute the following steps (if you use a class 2 certificate replace class1 by class2 in the instructions below):
Decrypt the private key by using the password you entered when you created your key:
openssl
rsa -in ssl.key -out /etc/nginx/conf/ssl.key
Alternatively you can also use the Tool Box decryption tool of your StartSSL™ account.
Protect your key from prying eyes:
chmod
600 /etc/nginx/conf/ssl.key
Fetch the Root CA and Class 1 Intermediate Server CA certificates:
wget http://www.startssl.com/certs/ca.pem
wget http://www.startssl.com/certs/sub.class1.server.ca.pem
Create a unified certificate from your certificate and the CA certificates:
cat
ssl.crt sub.class1.server.ca.pem ca.pem > /etc/nginx/conf/ssl-unified.crt
Configure your nginx server to use the new key and certificate (in the global settings or a server section):
ssl
on;
ssl_certificate /etc/nginx/conf/ssl-unified.crt;
ssl_certificate_key /etc/nginx/conf/ssl.key;
Tell nginx to reload its configuration:
killall
-HUP nginx
也可以直接访问install startssl on nginx.
参考:http://blog.csdn.net/jianglili611/article/details/46290431
1、现象
用Android(或Java)测试程序访问下面两个链接。
https链接一:web服务器为jetty,后台语言为java。
https链接二:web服务器为nginx,后台语言为php。
链接一能正常访问,访问链接二报异常,且用HttpURLConnection和apache的HttpClient两种不同的api访问异常信息不同,具体如下:
(1) 用HttpURLConnection访问,测试代码如下:
HttpURLConnection访问https
Java
12345678910111213141516171819202122232425262728293031323334353637 | public static String httpGet(String httpUrl) { BufferedReader input = null; StringBuilder sb = null; URL url = null; HttpURLConnection con = null; try { url = new URL(httpUrl); try { con = (HttpURLConnection)url.openConnection(); input = new BufferedReader(new InputStreamReader(con.getInputStream())); sb = new StringBuilder(); String s; while ((s = input.readLine()) != null) { sb.append(s).append("\n"); } } catch (IOException e) { e.printStackTrace(); } } catch (MalformedURLException e1) { e1.printStackTrace(); } finally { // close buffered if (input != null) { try { input.close(); } catch (IOException e) { e.printStackTrace(); } } // disconnecting releases the resources held by a connection so they may be closed or reused if (con != null) { con.disconnect(); } } return sb == null ? null : sb.toString();} |
1 | javax.net.ssl.SSLPeerUnverifiedException:Nopeercertificate |
HttpClient访问https
Java
123456789101112131415161718 | public static String httpGet(String httpUrl) { HttpClient httpClient = new HttpClient(); GetMethod httpGet = new GetMethod(httpUrl); try { if (httpClient.executeMethod(httpGet) != HttpStatus.SC_OK) { // System.err.println("HttpGet Method failed: " + httpGet.getStatusLine()); return null; } return httpGet.getResponseBodyAsString(); } catch (Exception e) { e.printStackTrace(); } finally { httpGet.releaseConnection(); httpClient = null; } return null;} |
1 | javax.net.ssl.SSLHandshakeException:sun.security.validator.ValidatorException:PKIXpathbuildingfailed:sun.security.provider.certpath.SunCertPathBuilderException:unabletofindvalidcertificationpathtorequestedtarget |
需要快速寻求答案的可直接看第3部分 解决方式,这部分详细分析原因。
google发现stackoverflow上不少人反应,twitter和新浪微博的api也会报这个异常,不少人反映客户端需要导入证书,其实大可不必,如果要导证书的话,用户不得哭了。。
从上面的情况可以看出,用jetty做为容器是能正常访问的,只是当容器为nginx时才会异常。
配合后台开发调试了很久,开始以为是cipher suite的问题,为此特地把
ssl_ciphers EDH-RSA-DES-CBC3-SHA;
加入了nginx的配置中,后来发现依然无效。stackoverflow发现,如下代码是能正常访问上面异常的https url
HttpURLConnection访问https并相信所有证书
Java
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 | public static String httpGet(String httpUrl) { BufferedReader input = null; StringBuilder sb = null; URL url = null; HttpURLConnection con = null; try { url = new URL(httpUrl); try { // trust all hosts trustAllHosts(); HttpsURLConnection https = (HttpsURLConnection)url.openConnection(); if (url.getProtocol().toLowerCase().equals("https")) { https.setHostnameVerifier(DO_NOT_VERIFY); con = https; } else { con = (HttpURLConnection)url.openConnection(); } input = new BufferedReader(new InputStreamReader(con.getInputStream())); sb = new StringBuilder(); String s; while ((s = input.readLine()) != null) { sb.append(s).append("\n"); } } catch (IOException e) { e.printStackTrace(); } } catch (MalformedURLException e1) { e1.printStackTrace(); } finally { // close buffered if (input != null) { try { input.close(); } catch (IOException e) { e.printStackTrace(); } } // disconnecting releases the resources held by a connection so they may be closed or reused if (con != null) { con.disconnect(); } } return sb == null ? null : sb.toString();} final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; /** * Trust every server - dont check for any certificate */private static void trustAllHosts() { final String TAG = "trustAllHosts"; // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[] {}; } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { Log.i(TAG, "checkClientTrusted"); } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { Log.i(TAG, "checkServerTrusted"); } } }; // Install the all-trusting trust manager try { SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (Exception e) { e.printStackTrace(); }} |
1 | trustAllHosts(); |
1 | https.setHostnameVerifier(DO_NOT_VERIFY); |
在上面checkServerTrusted函数中添加断点,查看X509Certificate[] chain的值,即证书信息,发现访问两个不同链接X509Certificate[] chain值有所区别,nginx传过来证书信息缺少了startssl 的ca证书,证书如下:
至此原因大白:
android的证书库里已经带了startssl
ca证书,而nginx默认不带startssl ca证书,这样android端访问nginx为容器的https url校验就会失败,jetty默认带startssl ca证书,所以正常。
PS:后来对windows和mac下java访问https也做了测试,发现mac上的jdk缺省不带startssl ca证书所以能访问通过,而加上startssl ca证书后同android一样访问不通过。而windows上的jdk缺省带startssl ca证书同android一样访问失败。
3、解决方式
上面的分析中已经介绍了一种解决方法即客户端相信所有证书,不过这种方式只是规避了问题,同时也给客户端带来了风险,比较合适的解决方式是为nginx添加startssl
ca证书,添加方法如下:
First, use the StartSSL™ Control Panel to create a private key and certificate and transfer them to your server. Then execute the following steps (if you use a class 2 certificate replace class1 by class2 in the instructions below):
Decrypt the private key by using the password you entered when you created your key:
openssl
rsa -in ssl.key -out /etc/nginx/conf/ssl.key
Alternatively you can also use the Tool Box decryption tool of your StartSSL™ account.
Protect your key from prying eyes:
chmod
600 /etc/nginx/conf/ssl.key
Fetch the Root CA and Class 1 Intermediate Server CA certificates:
wget http://www.startssl.com/certs/ca.pem
wget http://www.startssl.com/certs/sub.class1.server.ca.pem
Create a unified certificate from your certificate and the CA certificates:
cat
ssl.crt sub.class1.server.ca.pem ca.pem > /etc/nginx/conf/ssl-unified.crt
Configure your nginx server to use the new key and certificate (in the global settings or a server section):
ssl
on;
ssl_certificate /etc/nginx/conf/ssl-unified.crt;
ssl_certificate_key /etc/nginx/conf/ssl.key;
Tell nginx to reload its configuration:
killall
-HUP nginx
也可以直接访问install startssl on nginx.
参考:http://blog.csdn.net/jianglili611/article/details/46290431
相关文章推荐
- android HTTPURLConnection解决不能访问HTTPs请求
- android HTTPURLConnection解决不能访问Https请求
- android HTTPURLConnection解决不能访问HTTPs请求
- 解决android 5.0 webview不能加载http与https混合内容的问题
- 让apache服务器可以保证http和https同时访问(解决https可以访问而http不能访问)
- 解决android 5.0 webview不能加载http与https混合内容的问题
- 让apache服务器可以保证http和https同时访问(解决https可以访问而http不能访问)
- Android-OKhttp解决https安全链接请求问题
- Android中原http请求的https实现(URLConnection 、volley)(volley不修改源码)
- 解决android 5.0 webview不能加载http与https混合内容的问题
- Android4.04下载源码图解~解决公网IP不能直接访问问题
- android获取URLConnection和HttpClient网络请求响应码
- HTTP 错误 500.19 - Internal Server Error 无法访问请求的页面,因为该页的相关配置数据无效 的解决办法
- 解决内网通过代理服务器访问外网部署WebService报错问题“请求因 HTTP 状态 407 失败”
- Android4.04下载源码图解~解决公网IP不能直接访问问题
- 无法安装ADT(无法访问https://dl-ssl.google.com/android/eclipse/) 的解决方法
- Android学习指南之三十:使用URLConnection和HttpClient访问网络的方法
- Android4.04下载源码图解~解决公网IP不能直接访问问题
- android 联网请求的两种方式HttpURLConnection和HttpClient
- [Android实例] http请求No peer certificate的解决方法