android httpClient 支援HTTPS的2種處理方式

來源:互聯網
上載者:User

標籤:android   class   blog   code   java   http   

摘自: http://www.kankanews.com/ICkengine/archives/9634.shtml

 

項目中Android https或http請求地址重新導向為HTTPS的地址,相信很多人都遇到了這個異常(無終端認證):
 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

 

 

1.沒遇到過的問題,搜尋吧,少年

log裡出現這個異常,作者第一次遇到,不知道啥意思。看下字面意思,是ssl協議中沒有終端認證。SSL?作者沒用到ssl協議呀,只是通過httpClient請求一個重新導向https的地址。
好吧,google下,知道了個差不多情況的文章,http://www.eoeandroid.com/thread-161747-1-1.html。恩恩,一個不錯的文章,給出了個解決方案。照著來試下。添加個繼承SSLSocketFactory的
自訂類。並在初始化httpclient支援https時,註冊進去。看下面代碼:

public class HttpClientHelper {private static HttpClient httpClient;private HttpClientHelper() {}public static synchronized HttpClient getHttpClient() {if (null == httpClient) {// 初始化工作try {KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());trustStore.load(null, null);SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);  //允許所有主機的驗證HttpParams params = new BasicHttpParams();HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);HttpProtocolParams.setContentCharset(params,HTTP.DEFAULT_CONTENT_CHARSET);HttpProtocolParams.setUseExpectContinue(params, true);// 設定連線管理員的逾時ConnManagerParams.setTimeout(params, 10000);// 設定連線逾時HttpConnectionParams.setConnectionTimeout(params, 10000);// 設定socket逾時HttpConnectionParams.setSoTimeout(params, 10000);// 設定http https支援SchemeRegistry schReg = new SchemeRegistry();schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));schReg.register(new Scheme("https", sf, 443));ClientConnectionManager conManager = new ThreadSafeClientConnManager(params, schReg);httpClient = new DefaultHttpClient(conManager, params);} catch (Exception e) {e.printStackTrace();return new DefaultHttpClient();}}return httpClient;}}class SSLSocketFactoryEx extends SSLSocketFactory {SSLContext sslContext = SSLContext.getInstance("TLS");public SSLSocketFactoryEx(KeyStore truststore)throws NoSuchAlgorithmException, KeyManagementException,KeyStoreException, UnrecoverableKeyException {super(truststore);TrustManager tm = new X509TrustManager() {@Overridepublic java.security.cert.X509Certificate[] getAcceptedIssuers() {return null;}@Overridepublic void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)throws java.security.cert.CertificateException {}@Overridepublic void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType)throws java.security.cert.CertificateException {}};sslContext.init(null, new TrustManager[] { tm }, null);}@Overridepublic Socket createSocket(Socket socket, String host, int port,boolean autoClose) throws IOException, UnknownHostException {return sslContext.getSocketFactory().createSocket(socket, host, port,autoClose);}@Overridepublic Socket createSocket() throws IOException {return sslContext.getSocketFactory().createSocket();}}

ok,run下,狂亂的點到測試按鈕,深吸口氣,盯著eclipse中的logat。咦?神奇的竟然沒有報之前的 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate的異常了。服務端的資料正常返回了。,狂喜中…

 

2.瞭解並分析問題

狂喜中,得分析這問題誒。不然老大來問,啥情況?楞半天不知道咋說(作者就經常這樣,所以吸取教訓。所以的弄懂出現的問題,學習+彙報工作)。
思來想去,就是作者請求的是一個重新導向https的地址。好吧,那就學習下https(之前被老大深深的教過,http就是request/response)。繼續搜尋吧,少年。下面總結下學習到的https知識。

 

2.1 https

HTTPS:超文本安全傳輸協議,和HTTP相比,多了一個SSL/TSL的認證過程,連接埠為443。(鄙視下之前說的)

作者沒用到ssl協議呀,只是通過httpClient請求一個重新導向https的地址

1.peer終端發送一個request,https服務端把支援的密碼編譯演算法等以認證的形式返回一個身份資訊(包含ca頒發機構和加密公開金鑰等)。

2.擷取認證之後,驗證認證合法性。

3.隨機產生一個密鑰,並以認證當中的公開金鑰加密。

4.request https服務端,把用公開金鑰加密過的密鑰傳送給https服務端。

5.https服務端用自己的密鑰解密,擷取隨機值。

6.之後雙方傳送資料都用此祕密金鑰加密後通訊。

看下面一張網上的得來的https的時序圖:

 

2.2分析下出現問題的原因

好吧,大概的流程知道了。定位已經非常清楚了。在第2步驗證認證時,無法驗證。為啥無法驗證呢?沒有添加信任。詳細參考下

http://www.cnblogs.com/P_Chou/archive/2010/12/27/https-ssl-certification.html講的非常清楚https-ssl的認證過程,膜拜下該作者

這樣想來,上面提供的解決方案就是添加預設信任全部認證。以此來通過接下來的通訊。

 

3.解決問題

但是,這樣問題是解決了。但是覺得還是不帶靠譜(信任全部認證有點危險)。繼續劈劈啪啪的網上搜尋一番。又找到了一種解決方案,其過程大致這樣的:

1.瀏覽器訪問https地址,儲存提示的認證到本地,放到android項目中的assets目錄。

2.匯入認證,代碼如下。

3.把認證添加為信任。

String requestHTTPSPage(String mUrl) {InputStream ins = null;String result = "";try {ins = context.getAssets().open("app_pay.cer"); //下載的認證放到項目中的assets目錄中CertificateFactory cerFactory = CertificateFactory.getInstance("X.509");Certificate cer = cerFactory.generateCertificate(ins);KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");keyStore.load(null, null);keyStore.setCertificateEntry("trust", cer);SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore);Scheme sch = new Scheme("https", socketFactory, 443);HttpClient mHttpClient = new DefaultHttpClient();mHttpClient.getConnectionManager().getSchemeRegistry().register(sch);BufferedReader reader = null;try {Log.d(TAG, "executeGet is in,murl:" + mUrl);HttpGet request = new HttpGet();request.setURI(new URI(mUrl));HttpResponse response = mHttpClient.execute(request);if (response.getStatusLine().getStatusCode() != 200) {request.abort();return result;}reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));StringBuffer buffer = new StringBuffer();String line = null;while ((line = reader.readLine()) != null) {buffer.append(line);}result = buffer.toString();Log.d(TAG, "mUrl=" + mUrl + "\nresult = " + result);} catch (Exception e) {e.printStackTrace();} finally {if (reader != null) {reader.close();}}} catch (Exception e) {// TODO: handle exception} finally {try {if (ins != null)ins.close();} catch (IOException e) {e.printStackTrace();}}return result;}

接著,驗證下唄。吼吼,稀裡糊塗的又可以了。感動的淚流滿面,發現個問題:還是原創好呀(作者正真實實在在的分享自己的經驗,不懂還可以私信

 

2種方法都解決了作者遇到的問題,這裡記錄下。以防下次遇到,希望能給遇到相同問題朋友有所參考說明。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.