国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - 淺析Android系統中HTTPS通信的實現

淺析Android系統中HTTPS通信的實現

2019-12-31 14:42低調小一 JAVA教程

這篇文章主要介紹了淺析Android系統中HTTPS通信的實現,實現握手的源碼為Java語言編寫,需要的朋友可以參考下

前言
最近有一個跟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. 雙方采用“對話密鑰”進行加密通信。
上面過程的前兩布,又稱為“握手階段”。
握手階段的詳細過程

淺析Android系統中HTTPS通信的實現

握手階段”涉及四次通信,需要注意的是,“握手階段”的所有通信都是明文的。
客戶端發出請求(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

   

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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

   

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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

   

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<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的支持:

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

加入對HTTPS的支持,就可以有效的建立HTTPS連接了,例如“https://www.google.com.hk”了,但是訪問自己基于Nginx搭建的HTTPS服務器卻不行,因為它使用了不被系統承認的自定義證書,會報出如下問題:No peer certificate。
使用自定義證書并忽略驗證的HTTPS連接方式
解決證書不被系統承認的方法,就是跳過系統校驗。要跳過系統校驗,就不能再使用系統標準的SSL SocketFactory了,需要自定義一個。然后為了在這個自定義SSL SocketFactory里跳過校驗,還需要自定義一個TrustManager,在其中忽略所有校驗,即TrustAll。

   

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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,至于配置大家自行google就好了。

?
1
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了。

   

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}

 

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲午夜精品 | 亚洲午夜激情 | 久久久免费精品视频 | 欧美日韩免费 | 欧美自拍一区 | 精品国产乱码久久久久久图片 | 91精品国产色综合久久不卡蜜臀 | 午夜精品电影 | 久久国产福利 | 日韩成人精品在线 | 91嫩草国产露脸精品国产 | 毛片网站在线 | 国产一区二区三区视频 | 成年人免费在线观看视频网站 | 永久免费看黄网站 | 久久com| 水卜樱一区二区av | 国产精品久久久久久久久 | 中文字幕在线一区二区三区 | 青青久久北条麻妃 | 特级淫片日本高清视频免费 | av午夜电影 | 九一视频在线观看 | 亚洲综合一区二区 | 一级黄色大片 | 久久不卡| 国产一区二区欧美 | 国产综合精品一区二区三区 | 日韩成人在线一区 | 日本中文字幕一区二区 | 亚洲一区二区三区在线免费观看 | 日韩精品一二三区 | 91亚洲精品一区 | 精品av| 91天堂网 | 伊人无码高清 | 欧美一级视频 | 一区二区三区在线视频播放 | 欧美激情精品久久久久久变态 | 在线播放亚洲 | 视频四区 |