1. 程式人生 > >android httpClient 支援HTTPS的訪問方式

android httpClient 支援HTTPS的訪問方式

專案中Android https請求地址遇到了這個異常(無終端認證):
javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

是SSL協議中沒有終端認證。

沒有遇到過的問題,於是無奈的去找度娘。。。。。。。

看了不少大神的部落格後得到的解決方案如下:

  1. /** 
  2.   * Post請求連線Https服務 
  3.   * @param serverURL  請求地址 
  4.   * @param jsonStr    請求報文 
  5.   * @return 
  6.   * @throws Exception 
  7.   */
  8.  publicstaticsynchronized
     String doHttpsPost(String serverURL, String jsonStr)throws Exception { 
  9.      // 引數
  10.      HttpParams httpParameters = new BasicHttpParams(); 
  11.      // 設定連線超時
  12.      HttpConnectionParams.setConnectionTimeout(httpParameters, 3000); 
  13.      // 設定socket超時
  14.      HttpConnectionParams.setSoTimeout(httpParameters, 3000); 
  15.      // 獲取HttpClient物件 (認證)
  16.      HttpClient hc = initHttpClient(httpParameters); 
  17.      HttpPost post = new HttpPost(serverURL); 
  18.      // 傳送資料型別
  19.      post.addHeader("Content-Type""application/json;charset=utf-8"); 
  20.      // 接受資料型別
  21.      post.addHeader("Accept""application/json"); 
  22.      // 請求報文
  23.      StringEntity entity = new StringEntity(jsonStr, 
    "UTF-8"); 
  24.      post.setEntity(entity); 
  25.      post.setParams(httpParameters); 
  26.      HttpResponse response = null
  27.      try { 
  28.          response = hc.execute(post); 
  29.      } catch (UnknownHostException e) { 
  30.          thrownew Exception("Unable to access " + e.getLocalizedMessage()); 
  31.      } catch (SocketException e) { 
  32.          e.printStackTrace(); 
  33.      } 
  34.      int sCode = response.getStatusLine().getStatusCode(); 
  35.      if (sCode == HttpStatus.SC_OK) { 
  36.          return EntityUtils.toString(response.getEntity()); 
  37.      } else
  38.          thrownew Exception("StatusCode is " + sCode); 
  39.  } 
  40.  privatestatic HttpClient client = null
  41.  /** 
  42.   * 初始化HttpClient物件 
  43.   * @param params 
  44.   * @return 
  45.   */
  46.  publicstaticsynchronized HttpClient initHttpClient(HttpParams params) { 
  47.      if(client == null){ 
  48.          try { 
  49.              KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
  50.              trustStore.load(nullnull); 
  51.              SSLSocketFactory sf = new SSLSocketFactoryImp(trustStore); 
  52.              //允許所有主機的驗證
  53.              sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 
  54.              HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 
  55.              HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); 
  56.              // 設定http和https支援
  57.              SchemeRegistry registry = new SchemeRegistry(); 
  58.              registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 
  59.              registry.register(new Scheme("https", sf, 443)); 
  60.              ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); 
  61.              returnnew DefaultHttpClient(ccm, params); 
  62.          } catch (Exception e) { 
  63.              e.printStackTrace(); 
  64.              returnnew DefaultHttpClient(params); 
  65.          } 
  66.      } 
  67.      return client; 
  68.  } 
  69. publicstaticclass SSLSocketFactoryImp extends SSLSocketFactory { 
  70.      final SSLContext sslContext = SSLContext.getInstance("TLS"); 
  71.      public SSLSocketFactoryImp(KeyStore truststore) 
  72.              throws NoSuchAlgorithmException, KeyManagementException, 
  73.              KeyStoreException, UnrecoverableKeyException { 
  74.          super(truststore); 
  75.          TrustManager tm = new X509TrustManager() { 
  76.              public java.security.cert.X509Certificate[] getAcceptedIssuers() { 
  77.                  returnnull
  78.              } 
  79.              @Override
  80.              publicvoid checkClientTrusted( 
  81.                      java.security.cert.X509Certificate[] chain, 
  82.                      String authType) 
  83.                      throws java.security.cert.CertificateException { 
  84.              } 
  85.              @Override
  86.              publicvoid checkServerTrusted( 
  87.                      java.security.cert.X509Certificate[] chain, 
  88.                      String authType) 
  89.                      throws java.security.cert.CertificateException { 
  90.              } 
  91.          }; 
  92.          sslContext.init(nullnew TrustManager[] { tm }, null); 
  93.      } 
  94.      @Override
  95.      public Socket createSocket(Socket socket, String host, int port, 
  96.              boolean autoClose) throws IOException, UnknownHostException { 
  97.          return sslContext.getSocketFactory().createSocket(socket, host, 
  98.                  port, autoClose); 
  99.      } 
  100.      @Override
  101.      public Socket createSocket() throws IOException { 
  102.          return sslContext.getSocketFactory().createSocket(); 
  103.      } 
  104.  } 

run下,小手發抖的點到測試按鈕,深吸口氣,咦?沒反應。。。馬蛋的,工作執行緒忘記start(),唉,再次run下,終於的有點反應了,神奇的竟然沒有報之前的 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate 的異常了。服務端的資料正常返回了。

分析問題:
HTTPS:超文字安全傳輸協議,和HTTP相比,多了一個SSL/TSL的認證過程,埠為443。

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

2.獲取證書之後,驗證證書合法性。

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

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

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

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

HTTPS流程清楚後,問題也就明顯了,驗證證書時,無法驗證。

上面提供的解決方案就是新增預設信任全部證書。以此來通過接下來的通訊。

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

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

2.匯入證書,程式碼如下。

3.把證書新增為信任。

  1. publicstatic String requestHTTPSPage(Context context, String mUrl) { 
  2.         InputStream ins = null
  3.         String result = ""
  4.         try { 
  5.             ins = context.getAssets().open("my.key"); // 下載的證書放到專案中的assets目錄中
  6.             CertificateFactory cerFactory = CertificateFactory.getInstance("X.509"); 
  7.             Certificate cer = cerFactory.generateCertificate(ins); 
  8.             KeyStore keyStore = KeyStore.getInstance("PKCS12""BC"); 
  9.             keyStore.load(nullnull); 
  10.             keyStore.setCertificateEntry("trust", cer); 
  11.             SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore); 
  12.             Scheme sch = new Scheme("https", socketFactory, 443); 
  13.             HttpClient mHttpClient = new DefaultHttpClient(); 
  14.             mHttpClient.getConnectionManager().getSchemeRegistry().register(sch); 
  15.             BufferedReader reader = null
  16.             try { 
  17.                 HttpGet request = new HttpGet(); 
  18.                 request.setURI(new URI(mUrl)); 
  19.                 HttpResponse response = mHttpClient.execute(request); 
  20.                 if (response.getStatusLine().getStatusCode() != 200) { 
  21.                     request.abort(); 
  22.                     return result; 
  23.                 } 
  24.                 reader = new BufferedReader(new InputStreamReader(response 
  25.                         .getEntity().getContent())); 
  26.                 StringBuffer buffer = new StringBuffer(); 
  27.                 String line = null
  28.                 while ((line = reader.readLine()) != null) { 
  29.                     buffer.append(line); 
  30.                 } 
  31.                 result = buffer.toString(); 
  32.             } catch (Exception e) { 
  33.                 e.printStackTrace(); 
  34.             } finally { 
  35.                 if (reader != null) { 
  36.                     reader.close(); 
  37.                 } 
  38.             } 
  39.         } catch (Exception e) { 
  40.             e.printStackTrace(); 
  41.         } finally { 
  42.             try { 
  43.                 if (ins != null
  44.                     ins.close(); 
  45.             } catch (IOException e) { 
  46.                 e.printStackTrace(); 
  47.             } 
  48.         } 
  49.         return result; 

本文連結:http://my.oschina.net/u/1251149/blog/299010