https 原理與實踐

2023-05-26 15:00:19

https 原理與實踐

經典三問,是什麼,為什麼,怎麼做?

是什麼

是一種http的安全協定,在tcp ip網路模型裡,http應用層是在tcp 傳輸層之上的,https協定規定了在tcp傳輸層之上還有一層tls/ssl層,這一層對http應用層發出去和接收的報文做加密和解密。

為什麼

出現https原因,在我看來有兩點

1,因為http是明文傳輸,極不安全,需要對報文進行加密。

2,我們無法確認瀏覽的網站的身份資訊,如果是釣魚網站,誘使我們輸入銀行賬號密碼之類的就麻煩了。

怎麼做

簡而言之,「https規定了 加密演演算法對報文進行加密,解決明文傳輸的問題。採用數位憑證的方式解決對伺服器端的身份認證問題」

加密報文方式

對稱加密和非對稱加密

對稱加密是加密和解密都採用同一個金鑰。 非對稱加密 將金鑰分為公鑰和私鑰, 公鑰進行加密的報文,用私鑰可以解密。私鑰加密的報文,用公鑰可以解密(這種加密方式也是數位憑證採用的原理)

一般對稱加密會比非對稱加密效能高几個數量級。但是就安全性而言,非對稱加密安全性會更高,那麼https採用的是什麼加密方式呢?

兩者結合的加密方式

由於對稱加密效能更好,所以https在真正加密報文時還是採用的對稱加密方式,但是對稱加密的金鑰是通過非對稱加密協商後傳給對方的。這樣的加密方式就能保證在效能更好的前提下也有不錯的安全性。

數位憑證原理以及應用

數位憑證是什麼

數位憑證 是一個可信組織驗證和簽發的識別資訊。有點類似於現實生活中的身份證,公安機關給我們頒發身份證為的就是證明個人身份,而在網路世界裡,這個組織就是ca組織。

來看看向ca組織提交註冊資訊以及驗證數位憑證的過程。

go語言精進之路截圖

server.key是網站擁有的自己的私鑰,ca.key是ca的私鑰,server.crt是網站的數位憑證。

1,首先網站服務將自身的一些基本資訊通過自身的私鑰進行加密,然後將自身的公鑰和基本資訊密文通過證書籤名請求的格式傳遞給ca。 2,ca得到請求後,利用網站服務的公鑰,對其基本資訊進行解密。然後再按x509數位憑證規範生成公鑰證書。

這裡涉及到ca對證書資訊的加密方式,前面提到用私鑰加密的資料,可用公鑰解密,當將證書資訊通過私鑰加密後,使用者端就能通過ca的公鑰去解密證書,能成功解密,說明證書的確是ca頒發的。

但實際加密卻不是這樣,因為非對稱加密演演算法 加密的資訊越多效能越差,所以是將證書資訊通過摘要演演算法生成固定長度的字元后,再採用ca私鑰對其摘要進行加密。 摘要演演算法(常見的如MD5,sha)確保了資料的完整性,因為多次相同內容的資料用相同摘要演演算法計算的結果一致,所以能基本保證資料未被篡改。

3,x509數位憑證裡面包含 以下資訊: 伺服器端公鑰(server.pub); 證書相關屬性資訊,如域名、有效期等; 證書頒發機構的簽名資訊 4,然後就是使用者端的解密過程,拿到證書以後,通過ca公鑰對證書以及公鑰資訊的摘要密文進行解密,得到訊息摘要,然後用摘要演演算法對證書資訊以及公鑰資訊進行摘要計算,將使用者端得到的摘要和密文解密後的摘要進行對比,如果相同,說明內容未被篡改,證書有效。

數位憑證的加密演演算法總結

通過私鑰加密,公鑰解密的方式提供證書的頒發證明,能成功解密說明證書的確是ca頒發的。 通過摘要演演算法,確保了證書的完整性,未被篡改的證明。

https握手過程簡析

建立在https要解決什麼問題的基礎上,理解https的握手過程,看看它究竟做了什麼設計,讓握手過程更高效,更安全。

https是為了解決明文傳輸的不安全性,以及對端身份認證的問題。

1,使用者端傳送client hello資訊將自身支援的tls版本,密碼套件的資訊傳給伺服器端。

2,伺服器端接收後會傳送server hello資訊 選擇tls版本和加密演演算法發給使用者端。

3,然後伺服器端傳送server certificate 資訊將自身的證書下發給使用者端。

4,然後伺服器端接著又傳送金鑰協商請求 server key exchange, 在金鑰協商環節,通常會使用到Diffie-Hellman(DH)金鑰交換演演算法,這是一種金鑰協商的協定,支援通訊雙方在不安全的通道上生成對稱加密演演算法所需的共用金鑰。注意這種交換演演算法使用的目的是既讓通訊雙方都擁有一樣的金鑰,但是呢,金鑰又不是通過資料傳輸到對面的,是協商產生的。

5 接著使用者端收到證書後,先檢驗證書的有效性, 然後使用者端生成接下來加密通訊的金鑰,同時將生成金鑰的一些引數 用伺服器端的公鑰加密後 傳送給 伺服器端,這個階段稱為client key exchange階段,伺服器端收到後按這些引數後用自身的私鑰解密 然後也生成相同金鑰。

6,最後使用者端和伺服器端分別會將連線至今的所有報文進行加密傳送給對方,以驗證tls握手成功。

golang宣告https伺服器

懂了互動邏輯以後,就知道了,宣告一個https服務的時候,得有一個ca頒發的證書,證書裡面包含伺服器端自身的公鑰資訊和一些基本資訊,然後還得指明明伺服器的私鑰,用於tls握手過程中對金鑰協商引數的加解密。

package main

import (
    // "fmt"
    // "io"
    "net/http"
    "log"
)

func HelloServer(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    w.Write([]byte("This is an example server.\n"))
    // fmt.Fprintf(w, "This is an example server.\n")
    // io.WriteString(w, "This is an example server.\n")
}

func main() {
    http.HandleFunc("/hello", HelloServer)
    err := http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

基於上述https原理,下面我將會用openssl 工具以及golang程式來演示下https加解密過程。

生成伺服器私鑰

openssl genrsa -out server.key 2048

生成x509證書

openssl req -new -x509  -key server.key -out server.crt -days 3650

server.crt 就是我們的自簽名證書了,然後啟動go https服務

啟動go https服務

package main

import (
 "log"
 // "fmt"
 // "io"
 "net/http"
)

func HelloServer(w http.ResponseWriter, req *http.Request) {
 w.Header().Set("Content-Type", "text/plain")
 w.Write([]byte("This is an example server.\n"))
 // fmt.Fprintf(w, "This is an example server.\n")
 // io.WriteString(w, "This is an example server.\n")
}

func main() {
 http.HandleFunc("/hello", HelloServer)
 err := http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)
 if err != nil {
  log.Fatal("ListenAndServe: ", err)
 }
}

先測試下存取

(base) ➜  ~ curl https://proxy.example.com:443/hello
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.se/docs/sslcerts.html

curl 提示是自簽名證書,因為我們使用的openssl 按x509標準生成的證書,不是ca頒發的,所以curl有這個錯誤。

讓系統信任該證書

不同作業系統新增信任的方式不同,我使用的是mac os,在鑰匙串應用裡將證書新增進來並設定為信任。 注意這裡我設定了證書的域名是proxy.example.com ,所以我還需要設定下這個域名對應的ip

設定域名

vim /etc/hosts

255.255.255.255 broadcasthost
127.0.0.1       localhost
::1             localhost

## 新增上這行
127.0.0.1 proxy.example.com

再次存取

(base) ➜  ~ curl https://proxy.example.com:443/hello
This is an example server.

發現當前存取已經成功了。

完美收工。。。

參考文獻: go 語言精進之路