程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 淺析Android體系中HTTPS通訊的完成

淺析Android體系中HTTPS通訊的完成

編輯:關於JAVA

淺析Android體系中HTTPS通訊的完成。本站提示廣大學習愛好者:(淺析Android體系中HTTPS通訊的完成)文章只能為提供參考,不一定能成為您想要的結果。以下是淺析Android體系中HTTPS通訊的完成正文


媒介
比來有一個跟HTTPS相干的成績須要處理,是以花時光進修了一下Android平台HTTPS的應用,同時也看了一些HTTPS的道理,這裡分享一下進修心得。

HTTPS道理
HTTPS(Hyper Text Transfer Protocol Secure),是一種基於SSL/TLS的HTTP,一切的HTTP數據都是在SSL/TLS協定封裝之長進行傳輸的。HTTPS協定是在HTTP協定的基本上,添加了SSL/TLS握手和數據加密傳輸,也屬於運用層協定。所以,研討HTTPS協定道理,終究就是研討SSL/TLS協定。
SSL/TLS協定感化
不應用SSL/TLS的HTTP通訊,就是不加密的通訊,一切的信息明文流傳,帶來了三年夜風險:
1. 竊聽風險:第三方可以獲知通訊內容。
2. 改動風險:第三方可以修正告訴內容。
3. 假裝風險:第三方可以假裝別人身份介入通訊。
SSL/TLS協定是為懂得決這三年夜風險而設計的,願望到達:
1. 一切信息都是加密傳輸,第三方沒法竊聽。
2. 具有校驗機制,一旦被改動,通訊兩邊都邑連忙發明。
3. 裝備身份證書,避免身份被假裝。
根本的運轉進程
SSL/TLS協定的根本思緒是采取公鑰加密法,也就是說,客戶端先向辦事器端索要公鑰,然後用公鑰加密信息,辦事器收到密文後,用本身的私鑰解密。然則這裡須要懂得兩個成績的處理計劃。
1. 若何包管公鑰不被改動?
處理辦法:將公鑰放在數字證書中。只需證書是可托的,公鑰就是可托的。
2. 公鑰加密盤算量太年夜,若何削減耗用的時光?
處理辦法:每次對話(session),客戶端和辦事器端都生成一個“對話密鑰”(session key),用它來加密信息。因為“對話密鑰”是對稱加密,所以運算速度異常快,而辦事器公鑰只用於加密“對話密鑰”自己,如許就削減了加密運算的消費時光。
是以,SSL/TLS協定的根本進程是如許的:
1. 客戶端向辦事器端索要並驗證公鑰。
2. 兩邊協商生成“對話密鑰”。
3. 兩邊采取“對話密鑰”停止加密通訊。
下面進程的前兩布,又稱為“握手階段”。
握手階段的具體進程

握手階段”觸及四次通訊,須要留意的是,“握手階段”的一切通訊都是明文的。
客戶端收回要求(ClientHello)
起首,客戶端(平日是閱讀器)先向辦事器收回加密通訊的要求,這被叫做ClientHello要求。在這一步中,客戶端重要向辦事器供給以下信息:
1. 支撐的協定版本,好比TLS 1.0版
2. 一個客戶端生成的隨機數,稍後用於生成“對話密鑰”。
3. 支撐的加密辦法,好比RSA公鑰加密。
4. 支撐的緊縮辦法。
這裡須要留意的是,客戶端發送的信息當中不包含辦事器的域名。也就是說,實際上辦事器只能包括一個網站,不然會分不清運用向客戶端供給哪個網站的數字證書。這就是為何平日一台辦事器只能有一張數字證書的緣由。
辦事器回應(ServerHello)
辦事器收到客戶端要求後,向客戶端收回回應,這叫做ServerHello。辦事器的回應包括以下內容:
1. 確認應用的加密通訊協定版本,好比TLS 1.0版本。假如閱讀器與辦事器支撐的版本紛歧致,辦事器封閉加密通訊。
2. 一個辦事器生成的隨機數,稍後用於生成“對話密鑰”。
3. 確認應用的加密辦法,好比RSA公鑰加密。
4. 辦事器證書。
除下面這些信息,假如辦事器須要確認客戶真個身份,就會再包括一項要求,請求客戶端供給“客戶端證書”。好比,金融機構常常只許可認證客戶連入本身的收集,就會向正式客戶供給USB密鑰,外面就包括了一張客戶端證書。
客戶端回應
客戶端收到辦事器回應今後,起首驗證辦事器證書。假如證書不是可托機構發表,或許證書中的域名與現實域名紛歧致,或許證書曾經過時,就會向拜訪者顯示一個正告,由其選擇能否還要持續通訊。
假如證書沒有成績,客戶端就會從證書中掏出辦事器的公鑰。然後,向辦事器發送上面三項新聞。
1. 一個隨機數。該隨機數用辦事器公鑰加密,避免被竊聽。
2. 編碼轉變告訴,表現隨後的信息都將用兩邊約定的加密辦法和密鑰發送。
3. 客戶端握手停止告訴,表現客戶真個握手階段曾經停止。這一項平日也是後面發送的一切內容的hash值,用來供辦事器校驗。
下面第一項隨機數,是全部握手階段湧現的第三個隨機數,又稱“pre-master key”。有了它今後,客戶端和辦事器就同時有了三個隨機數,接著兩邊就用事前約定的加密辦法,各自生本錢次會話所用的統一把“會話密鑰”。
辦事器的最初回應
辦事器收到客戶真個第三個隨機數pre-master key以後,盤算生本錢次會話所用的“會話密鑰”。然後,向客戶端最初發送上面信息。
1. 編碼轉變告訴,表現隨後的信息都將用兩邊約定的加密辦法和密鑰發送。
2. 辦事器握手停止告訴,表現辦事器的握手階段曾經停止。這一項同時也是後面產生的一切內容的hash值,用來供客戶端校驗。
握手停止
至此,全部握手階段全體停止。接上去,客戶端與辦事器進入加密通訊,就完整是應用通俗的HTTP協定,只不外用“會話密鑰”加密內容。

辦事器基於Nginx搭建HTTPS虛擬站點
之前一篇文章具體引見了在辦事器端若何生成SSL證書,並基於Nginx搭建HTTPS辦事器,鏈接:Nginx搭建HTTPS辦事器

Android完成HTTPS通訊
因為各類緣由吧,這裡應用HttpClicent類講授一下Android若何樹立HTTPS銜接。代碼demo以下。
MainActivity.java

   

 package com.example.photocrop; 
   
  import java.io.BufferedReader; 
  import java.io.InputStreamReader; 
   
  import org.apache.http.HttpResponse; 
  import org.apache.http.HttpStatus; 
  import org.apache.http.StatusLine; 
  import org.apache.http.client.HttpClient; 
  import org.apache.http.client.methods.HttpPost; 
  import org.apache.http.client.methods.HttpUriRequest; 
   
  import android.app.Activity; 
  import android.os.AsyncTask; 
  import android.os.Bundle; 
  import android.os.AsyncTask.Status; 
  import android.text.TextUtils; 
  import android.util.Log; 
  import android.view.View; 
  import android.widget.Button; 
  import android.widget.TextView; 
   
  public class MainActivity extends Activity { 
    private Button httpsButton; 
    private TextView conTextView; 
   
    private CreateHttpsConnTask httpsTask; 
   
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 
      setContentView(R.layout.activity_main); 
   
      httpsButton = (Button) findViewById(R.id.create_https_button); 
      httpsButton.setOnClickListener(new View.OnClickListener() { 
   
        @Override 
        public void onClick(View v) { 
          runHttpsConnection(); 
        } 
      }); 
   
      conTextView = (TextView) findViewById(R.id.content_textview); 
      conTextView.setText("初始為空"); 
    } 
   
    private void runHttpsConnection() { 
      if (httpsTask == null || httpsTask.getStatus() == Status.FINISHED) { 
        httpsTask = new CreateHttpsConnTask(); 
        httpsTask.execute(); 
      } 
    } 
   
    private class CreateHttpsConnTask extends AsyncTask<Void, Void, Void> { 
      private static final String HTTPS_EXAMPLE_URL = "自界說"; 
      private StringBuffer sBuffer = new StringBuffer(); 
   
      @Override 
      protected Void doInBackground(Void... params) { 
        HttpUriRequest request = new HttpPost(HTTPS_EXAMPLE_URL); 
        HttpClient httpClient = HttpUtils.getHttpsClient(); 
        try { 
          HttpResponse httpResponse = httpClient.execute(request); 
          if (httpResponse != null) { 
            StatusLine statusLine = httpResponse.getStatusLine(); 
            if (statusLine != null 
                && statusLine.getStatusCode() == HttpStatus.SC_OK) { 
              BufferedReader reader = null; 
              try { 
                reader = new BufferedReader(new InputStreamReader( 
                    httpResponse.getEntity().getContent(), 
                    "UTF-8")); 
                String line = null; 
                while ((line = reader.readLine()) != null) { 
                  sBuffer.append(line); 
                } 
   
              } catch (Exception e) { 
                Log.e("https", e.getMessage()); 
              } finally { 
                if (reader != null) { 
                  reader.close(); 
                  reader = null; 
                } 
              } 
            } 
          } 
   
        } catch (Exception e) { 
          Log.e("https", e.getMessage()); 
        } finally { 
   
        } 
   
        return null; 
      } 
   
      @Override 
      protected void onPostExecute(Void result) { 
        if (!TextUtils.isEmpty(sBuffer.toString())) { 
          conTextView.setText(sBuffer.toString()); 
        } 
      } 
   
    } 
  } 

HttpUtils.java

   

package com.example.photocrop; 
   
  import org.apache.http.HttpVersion; 
  import org.apache.http.client.HttpClient; 
  import org.apache.http.conn.ClientConnectionManager; 
  import org.apache.http.conn.scheme.PlainSocketFactory; 
  import org.apache.http.conn.scheme.Scheme; 
  import org.apache.http.conn.scheme.SchemeRegistry; 
  import org.apache.http.conn.ssl.SSLSocketFactory; 
  import org.apache.http.impl.client.DefaultHttpClient; 
  import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; 
  import org.apache.http.params.BasicHttpParams; 
  import org.apache.http.params.HttpProtocolParams; 
  import org.apache.http.protocol.HTTP; 
   
  import android.content.Context; 
   
   
  public class HttpUtils { 
    public static HttpClient getHttpsClient() { 
      BasicHttpParams params = new BasicHttpParams(); 
      HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 
      HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET); 
      HttpProtocolParams.setUseExpectContinue(params, true); 
       
      SchemeRegistry schReg = new SchemeRegistry(); 
      schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 
      schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); 
       
      ClientConnectionManager connMgr = new ThreadSafeClientConnManager(params, schReg); 
       
      return new DefaultHttpClient(connMgr, params); 
    } 
     
    public static HttpClient getCustomClient() { 
      BasicHttpParams params = new BasicHttpParams(); 
      HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 
      HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET); 
      HttpProtocolParams.setUseExpectContinue(params, true); 
       
      SchemeRegistry schReg = new SchemeRegistry(); 
      schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 
      schReg.register(new Scheme("https", MySSLSocketFactory.getSocketFactory(), 443)); 
       
      ClientConnectionManager connMgr = new ThreadSafeClientConnManager(params, schReg); 
       
      return new DefaultHttpClient(connMgr, params); 
    } 
     
    public static HttpClient getSpecialKeyStoreClient(Context context) { 
      BasicHttpParams params = new BasicHttpParams(); 
      HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 
      HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET); 
      HttpProtocolParams.setUseExpectContinue(params, true); 
       
      SchemeRegistry schReg = new SchemeRegistry(); 
      schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 
      schReg.register(new Scheme("https", CustomerSocketFactory.getSocketFactory(context), 443)); 
       
      ClientConnectionManager connMgr = new ThreadSafeClientConnManager(params, schReg); 
       
      return new DefaultHttpClient(connMgr, params); 
    } 
  } 

activity_main.xml

   

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical"> 
   
    <Button 
      android:id="@+id/create_https_button" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:text="@string/hello_world" 
      android:textSize="16sp" /> 
   
    <TextView 
      android:id="@+id/content_textview" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:gravity="center" 
      android:textSize="16sp" /> 
   
  </LinearLayout> 

Android應用DefaultHttpClient樹立HTTPS銜接,症結須要參加對HTTPS的支撐:

  schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); 

參加對HTTPS的支撐,便可以有用的樹立HTTPS銜接了,例如“https://www.谷歌.com.hk”了,然則拜訪本身基於Nginx搭建的HTTPS辦事器卻不可,由於它應用了不被體系認可的自界說證書,會報出以下成績:No peer certificate。
應用自界說證書並疏忽驗證的HTTPS銜接方法
處理證書不被體系認可的辦法,就是跳過體系校驗。要跳過體系校驗,就不克不及再應用體系尺度的SSL SocketFactory了,須要自界說一個。然後為了在這個自界說SSL SocketFactory裡跳過校驗,還須要自界說一個TrustManager,在個中疏忽一切校驗,即TrustAll。

   

package com.example.photocrop; 
   
  import java.io.IOException; 
  import java.net.Socket; 
  import java.net.UnknownHostException; 
  import java.security.KeyManagementException; 
  import java.security.KeyStore; 
  import java.security.KeyStoreException; 
  import java.security.NoSuchAlgorithmException; 
  import java.security.UnrecoverableKeyException; 
  import java.security.cert.CertificateException; 
  import java.security.cert.X509Certificate; 
   
  import javax.net.ssl.SSLContext; 
  import javax.net.ssl.TrustManager; 
  import javax.net.ssl.X509TrustManager; 
  import org.apache.http.conn.ssl.SSLSocketFactory; 
   
  public class MySSLSocketFactory extends SSLSocketFactory { 
    SSLContext sslContext = SSLContext.getInstance("TLS"); 
   
    public MySSLSocketFactory(KeyStore truststore) 
        throws NoSuchAlgorithmException, KeyManagementException, 
        KeyStoreException, UnrecoverableKeyException { 
      super(truststore); 
      TrustManager tm = new X509TrustManager() { 
   
        @Override 
        public X509Certificate[] getAcceptedIssuers() { 
          return null; 
        } 
   
        @Override 
        public void checkServerTrusted(X509Certificate[] chain, 
            String authType) throws CertificateException { 
   
        } 
   
        @Override 
        public void checkClientTrusted(X509Certificate[] chain, 
            String authType) throws CertificateException { 
   
        } 
      }; 
   
      sslContext.init(null, new TrustManager[] { tm }, null); 
    } 
   
    @Override 
    public Socket createSocket() throws IOException { 
      return sslContext.getSocketFactory().createSocket(); 
    } 
   
    @Override 
    public Socket createSocket(Socket socket, String host, int port, 
        boolean autoClose) throws IOException, UnknownHostException { 
      return sslContext.getSocketFactory().createSocket(socket, host, port, 
          autoClose); 
    } 
   
    public static SSLSocketFactory getSocketFactory() { 
      try { 
        KeyStore trustStore = KeyStore.getInstance(KeyStore 
            .getDefaultType()); 
        trustStore.load(null, null); 
        SSLSocketFactory factory = new MySSLSocketFactory(trustStore); 
        return factory; 
      } catch (Exception e) { 
        e.getMessage(); 
        return null; 
      } 
    } 
  } 

同時,須要修正DefaultHttpClient的register辦法,改成本身構建的sslsocket:

 

  public static HttpClient getCustomClient() { 
    BasicHttpParams params = new BasicHttpParams(); 
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 
    HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET); 
    HttpProtocolParams.setUseExpectContinue(params, true); 
     
    SchemeRegistry schReg = new SchemeRegistry(); 
    schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 
    schReg.register(new Scheme("https", MySSLSocketFactory.getSocketFactory(), 443)); 
     
    ClientConnectionManager connMgr = new ThreadSafeClientConnManager(params, schReg); 
     
    return new DefaultHttpClient(connMgr, params); 
  } 

如許便可以勝利的拜訪本身構建的基於Nginx的HTTPS虛擬站點了。
缺點:
不外,固然這個計劃應用了HTTPS,客戶端和辦事器真個通訊內容獲得了加密,嗅探法式沒法獲得傳輸的內容,然則沒法招架“中央人進擊”。例如,在內網設置裝備擺設一個DNS,把目的辦事器域名解析到當地的一個地址,然後在這個地址上應用一個中央辦事器作為署理,它應用一個假的證書與客戶端通信,然後再由這個署理辦事器作為客戶端銜接到現實的辦事器,用真的證書與辦事器通信。如許一切的通信內容都邑經由這個署理,而客戶端不會感知,這是因為客戶端不校驗辦事器公鑰證書招致的。

應用自界說證書樹立HTTPS銜接
為了避免下面計劃能夠招致的“中央人進擊”,我們可以下載辦事器端公鑰證書,然後將公鑰證書編譯到Android運用中,由運用本身來驗證證書。
生成KeyStore

要驗證自界說證書,起首要把證書編譯到運用中,這須要應用keytool對象臨盆KeyStore文件。這裡的證書就是指目的辦事器的公鑰,可以從web辦事器設置裝備擺設的.crt文件或.pem文件取得。同時,你須要設置裝備擺設bouncycastle,我下載的是bcprov-jdk16-145.jar,至於設置裝備擺設年夜家自行谷歌就行了。

  keytool -importcert -v -trustcacerts -alias example -file www.example.com.crt -keystore example.bks -storetype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /home/wzy/Downloads/java/jdk1.7.0_60/jre/lib/ext/bcprov-jdk16-145.jar -storepass pw123456 

運轉後將顯示證書內容並提醒你能否確認,輸出Y回車便可。

臨盆KeyStore文件勝利後,將其放在app運用的res/raw目次下便可。
應用自界說KeyStore完成銜接
思緒和TrushAll差不多,也是須要一個自界說的SSLSokcetFactory,不外由於還須要驗證證書,是以不須要自界說TrustManager了。

   

 package com.example.photocrop; 
   
  import java.io.IOException; 
  import java.io.InputStream; 
  import java.security.KeyManagementException; 
  import java.security.KeyStore; 
  import java.security.KeyStoreException; 
  import java.security.NoSuchAlgorithmException; 
  import java.security.UnrecoverableKeyException; 
   
  import org.apache.http.conn.ssl.SSLSocketFactory; 
   
  import android.content.Context; 
   
  public class CustomerSocketFactory extends SSLSocketFactory { 
   
    private static final String PASSWD = "pw123456"; 
   
    public CustomerSocketFactory(KeyStore truststore) 
        throws NoSuchAlgorithmException, KeyManagementException, 
        KeyStoreException, UnrecoverableKeyException { 
      super(truststore); 
    } 
   
    public static SSLSocketFactory getSocketFactory(Context context) { 
      InputStream input = null; 
      try { 
        input = context.getResources().openRawResource(R.raw.example); 
        KeyStore trustStore = KeyStore.getInstance(KeyStore 
            .getDefaultType()); 
   
        trustStore.load(input, PASSWD.toCharArray()); 
   
        SSLSocketFactory factory = new CustomerSocketFactory(trustStore); 
   
        return factory; 
      } catch (Exception e) { 
        e.printStackTrace(); 
        return null; 
      } finally { 
        if (input != null) { 
          try { 
            input.close(); 
          } catch (IOException e) { 
            e.printStackTrace(); 
          } 
          input = null; 
        } 
      } 
    } 
   
  } 

同時,須要修正DefaultHttpClient的register辦法,改成本身構建的sslsocket:

 

  public static HttpClient getSpecialKeyStoreClient(Context context) { 
    BasicHttpParams params = new BasicHttpParams(); 
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 
    HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET); 
    HttpProtocolParams.setUseExpectContinue(params, true); 
     
    SchemeRegistry schReg = new SchemeRegistry(); 
    schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 
    schReg.register(new Scheme("https", CustomerSocketFactory.getSocketFactory(context), 443)); 
     
    ClientConnectionManager connMgr = new ThreadSafeClientConnManager(params, schReg); 
     
    return new DefaultHttpClient(connMgr, params); 
  } 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved