1. 程式人生 > >HTTPS過程以及詳細案例

HTTPS過程以及詳細案例

HTTPS過程以及詳細案例

1.HTTPS的過程

  

 

1.客戶端向服務端傳送請求,客戶端主要向伺服器提供以下資訊:

  •  支援的協議版本,比如TLS 1.0版。
  • 一個客戶端生成的隨機數,稍後用於生成"對話金鑰"。
  • 支援的加密方法,比如RSA公鑰加密。
  •  支援的壓縮方法。

2.伺服器端收到請求後,向客戶端做出迴應,迴應的內容包括:

  •  確認使用的加密通訊協議版本,比如TLS 1.0版本。如果瀏覽器與伺服器支援的版本不一致,伺服器關閉加密通訊。
  •  一個伺服器生成的隨機數,稍後用於生成"對話金鑰"。
  • 確認使用的加密方法,比如RSA公鑰加密。
  • 伺服器證書。

3.客戶端收到伺服器迴應以後,首先驗證伺服器證書。

 如果證書不是可信機構頒佈、或者證書中的域名與實際域名不一致、或者證書已經過期,就會向訪問者顯示一個警告,如下

   如果證書沒有問題,客戶端就會從證書中取出伺服器的公鑰。然後,向伺服器傳送下面三項資訊:

  •  一個隨機數。該隨機數用伺服器公鑰加密,防止被竊聽。
  • 編碼改變通知,表示隨後的資訊都將用雙方商定的加密方法和金鑰傳送。
  • 客戶端握手結束通知,表示客戶端的握手階段已經結束。這一項同時也是前面傳送的所有內容的hash值,用來供伺服器校驗

 證書的驗證過程如下:

  CA機構在簽發證書的時候,都會使用自己的私鑰對證書進行簽名,如果我們使用的是購買的證書,那麼很有可能,頒發這個證書的CA機構的公鑰已經預置在作業系統中。這樣瀏覽器就可以使用CA機構的公鑰對伺服器的證書進行驗籤,驗籤之後得到的是CA機構使用sha256得到的證書摘要,客戶端就會對伺服器傳送過來的證書使用sha256進行雜湊計算得到一份摘要,然後對比之前由CA得出來的摘要,就可以知道這個證書是不是正確的,是否被修改過。

4. 服務端迴應

 伺服器收到客戶端的第三個隨機數之後,計算生成本次會話所用的"會話金鑰"。然後,向客戶端最後傳送下面資訊:

  • 編碼改變通知,表示隨後的資訊都將用雙方商定的加密方法和金鑰傳送。
  • 伺服器握手結束通知,表示伺服器的握手階段已經結束。這一項同時也是前面傳送的所有內容的hash值,用來供客戶端校驗。

 會話祕鑰是根據前面幾次對話過程中產生的三個隨機數以及一些其他演算法產生的,後面伺服器與客戶端的互動都是通過這對話祕鑰進行加密解密處理的,其他的都和HTTP協議一樣。

2.HTTPS中自簽名證書的生成

  需要注意的是接下來幾種檔案的型別:

  • key是伺服器上的私鑰檔案,用於對傳送給客戶端資料的加密,以及對從客戶端接收到資料的解密
  • csr是證書籤名請求檔案,用於提交給證書頒發機構(CA)對證書籤名
  • crt是由證書頒發機構(CA)簽名後的證書,或者是開發者自簽名的證書,包含證書持有人的資訊,持有人的公鑰,以及簽署者的簽名等資訊
  • keystore 包含證書的檔案,可以自己去匯入證書
  • PEM 檔案格式儲存證書和金鑰,用於匯出,匯入證書時候的證書的格式,有證書開頭,結尾的格式。

 還有就是X.509是一個標準,規範了公開祕鑰認證、證書吊銷列表、授權憑證、憑證路徑驗證演算法等。

 a. 伺服器端使用者證書的生成過程:

  1. 生成私鑰(.key)檔案

1

2

# private key 

$openssl genrsa -des3 -out server.key 1024

  2. 生成證書請求(.csr)檔案

1

2

# generate csr 

$openssl req -new -key server.key -out server.csr 

  Country Name (2 letter code) [XX]:CN----------------------------------- 證書持有者所在國家

  State or Province Name (full name) []:BJ------------------------------- 證書持有者所在州或省份(可省略不填)

  Locality Name (eg, city) []:BJ----------------------------------------- 證書持有者所在城市(可省略不填)

  Organization Name (eg, company) []:NH---------------------------------- 證書持有者所屬組織或公司

  Organizational Unit Name (eg, section) []:.---------------------------- 證書持有者所屬部門(可省略不填)

  Common Name (eg, your name or your server's hostname) []:ceshi.com----- 必須要填寫域名或者ip地址

  Email Address []:------------------------------------------------------ 郵箱(可省略不填)

  challenge password:............................................................自定義密碼

  An optional company name:.............................................(可選)公司名稱

  3.  自簽名的證書檔案

1

openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

  4.本案例需要用到pem格式的證書,可以用以下方式合併證書檔案(crt)和私鑰檔案(key)來生成 

1

cat server.crt server.key > server.pem

 b. 客戶端證書檔案的生成

  這裡採用的是自簽名的方式,所以客戶端需要有一個匯入服務端證書的檔案,以供客戶端去驗證服務段的證書過程。

  1.生成.keystore檔案

1

keytool -genkey -validity 36000 -alias www.hellobcdb.com -keyalg RSA -keystore client.keystore

  2.匯入服務端證書

1

keytool -import -alias serverkey -file server.crt -keystore client.keystore

3.HTTPS案例

 環境及工具:ubuntu16.04,QtCreator,mongoose(cesanta),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

#include "mongoose.h"

 

static const char *s_http_port = "8443";

static const char *s_ssl_cert = "/home/gqx/workplace/TestHttps/server.pem";

static const char *s_ssl_key = "/home/gqx/workplace/TestHttps/server.key";

static struct mg_serve_http_opts s_http_server_opts;

 

static void ev_handler(struct mg_connection *nc, int ev, void *p) {

  if (ev == MG_EV_HTTP_REQUEST) {

    mg_serve_http(nc, (struct http_message *) p, s_http_server_opts);

  }

}

 

int main(void) {

  struct mg_mgr mgr;

  struct mg_connection *nc;

  struct mg_bind_opts bind_opts;

  const char *err;

 

  mg_mgr_init(&mgr, NULL);

  memset(&bind_opts, 0, sizeof(bind_opts));

  bind_opts.ssl_cert = s_ssl_cert;

  bind_opts.ssl_key = s_ssl_key;

  bind_opts.error_string = &err;

 

  printf("Starting SSL= server on port %s, cert from %s, key from %s\n",

         s_http_port, bind_opts.ssl_cert, bind_opts.ssl_key);

  nc = mg_bind_opt(&mgr, s_http_port, ev_handler, bind_opts);

  if (nc == NULL) {

    printf("Failed to create listener: %s\n", err);

    return 1;

  }

 

  // Set up HTTP server parameters

  mg_set_protocol_http_websocket(nc);

  s_http_server_opts.document_root = ".";  // Serve current directory

  s_http_server_opts.enable_directory_listing = "yes";

 

  for (;;) {

    mg_mgr_poll(&mgr, 1000);

  }

  mg_mgr_free(&mgr);

 

  return 0;

}

 QT中C++專案的pro檔案內容如下,注意要新增相應的庫:

1

2

3

4

5

6

7

8

9

10

11

TEMPLATE = app

CONFIG += console c++11

CONFIG -= app_bundle

CONFIG -= qt

LIBS += -lpthread

LIBS += -lssl -lcrypto

LIBS += -L /usr/local/bin -lcryptopp -ldl -lz

SOURCES += main.cpp \

    mongoose.cpp

HEADERS += \

    mongoose.h

 java客戶端程式碼(也可以直接在瀏覽器端直接訪問,不過會出現證書不安全的警告提示) 

  HttpsClient類

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

107

108

109

110

111

112

package com.gqx.test;

 

import java.io.FileInputStream;

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.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.HttpConnectionParams;

import org.apache.http.params.HttpParams;

import org.apache.http.params.HttpProtocolParams;

import org.apache.http.protocol.HTTP;

 

public class HttpsClient {

 

    private static final int SET_CONNECTION_TIMEOUT = 5 1000

    private static final int SET_SOCKET_TIMEOUT = 20 1000

       

    public static HttpClient getNewHttpClient() { 

        try 

            FileInputStream fis = new FileInputStream("/home/gqx/文件/oscar/client.keystore");

            String storepass = "starchain";

             

            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); 

            trustStore.load(fis, storepass.toCharArray()); 

   

            SSLSocketFactory sf = new MySSLSocketFactory(trustStore); 

            sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 

   

            HttpParams params = new BasicHttpParams(); 

   

            HttpConnectionParams.setConnectionTimeout(params, 10000); 

            HttpConnectionParams.setSoTimeout(params, 10000); 

   

            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 

            HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); 

   

            SchemeRegistry registry = new SchemeRegistry(); 

            registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 

            registry.register(new Scheme("https", sf, 443)); 

   

            ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); 

   

            HttpConnectionParams.setConnectionTimeout(params, SET_CONNECTION_TIMEOUT); 

            HttpConnectionParams.setSoTimeout(params, SET_SOCKET_TIMEOUT); 

            DefaultHttpClient client = new DefaultHttpClient(ccm, params); 

   

            return client; 

        catch (Exception e) { 

            return new DefaultHttpClient(); 

        

    }

     

     

     

     

    private static class MySSLSocketFactory extends SSLSocketFactory { 

        SSLContext sslContext = SSLContext.getInstance("TLS"); 

   

        public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, 

                KeyManagementException, KeyStoreException, UnrecoverableKeyException { 

            super(truststore); 

   

            TrustManager tm = new X509TrustManager() { 

                public void checkClientTrusted(X509Certificate[] chain, String authType) 

                        throws CertificateException { 

                

   

                public void checkServerTrusted(X509Certificate[] chain, String authType) 

                        throws CertificateException { 

                

   

                public X509Certificate[] getAcceptedIssuers() { 

                    return null

                

            }; 

   

            sslContext.init(nullnew TrustManager[] { tm }, null); 

        

   

        @Override 

        public Socket createSocket(Socket socket, String host, int port, boolean autoClose) 

                throws IOException, UnknownHostException { 

            return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); 

        

   

        @Override 

        public Socket createSocket() throws IOException { 

            return sslContext.getSocketFactory().createSocket(); 

        

    }

}

  測試類,post請求

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

package com.gqx.test;

 

import java.io.BufferedReader;

import java.io.InputStreamReader;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

import java.util.stream.Collectors;

import org.apache.http.HttpEntity;

import org.apache.http.HttpResponse;

import org.apache.http.client.HttpClient;

import org.apache.http.client.entity.UrlEncodedFormEntity;

import org.apache.http.client.methods.HttpPost;

import org.apache.http.message.BasicNameValuePair;

 

public class TestMain {

 

    public static void main(String[] args) {

        String urlStr = "https://www.hellobcdb.com:7999/fff";

        Map<String,String> params = new HashMap<>();

        params.put("value""publish.1666,'Database','computer science','Alice'");

        params.put("name""gqx");

        params.put("password""111");

        try {

            //DefaultHttpClient client = new DefaultHttpClient();

            HttpClient client = HttpsClient.getNewHttpClient();

            HttpPost httpPost = new HttpPost(urlStr);

         

            HttpEntity entity;

 

            ArrayList<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>();

            if(params != null){

                Set<String> keys = params.keySet();

                for(Iterator<String> i = keys.iterator(); i.hasNext();) {

                    String key = (String)i.next();

                    pairs.add(new BasicNameValuePair(key, params.get(key)));

                }

            }

            entity = new UrlEncodedFormEntity(pairs, "utf-8");

            httpPost.setEntity(entity);

            //Log.i(TAG, "post總位元組數:"+entity.getContentLength());

            HttpResponse response = client.execute(httpPost);

            try {

                // 獲取響應實體

                HttpEntity entitys = response.getEntity();

                System.out.println("--------------------------------------");

                // 列印響應狀態

                System.out.println(response.getStatusLine());

                if (entitys != null) {

                    // 列印響應內容長度

                    String result = new BufferedReader(new InputStreamReader(entitys.getContent()))

                            .lines().collect(Collectors.joining(System.lineSeparator()));

                   System.out.println(result);

                }

                System.out.println("------------------------------------");

            catch (Exception e) {

                e.printStackTrace();

            }

        }catch(Exception e){

            e.printStackTrace();

        }

    }

}

  相關jar包下載 

參考資料

Https流程和原理

OPENSSL生成SSL自簽證書 

mongoose的ssl案例

SSL/TLS協議執行機制的概述

數字簽名是什麼?

很希望自己是一棵樹,守靜、向光、安然,敏感的神經末梢,觸著流雲和微風,竊竊的歡喜。腳下踩著最卑賤的泥,很踏實。還有,每一天都在隱祕成長。