http:
是一個客戶端與服務端請求及應答的一個基於tcp傳輸的標准協議
瀏覽器通過http協議向一個服務器發送請求,服務器接收到請求之后經過一系列的處理將將請求結果內容返回給瀏覽器,此時瀏覽器網頁上便獲得我們所需要的內容
但是基於http協議瀏覽器與服務器傳輸數據的過程中,數據是明文的,在21世紀網絡發達的今天,明文數據極易被截獲修改,對安全性造成了很大的隱患
因安全要求有必要對傳輸數據進行加密,因而https協議便由此誕生
https:
https協議是基於http協議基礎上的一種安全(基於ssl或者tls安全加密)傳輸協議
數字證書主要由兩部分組成:
1、C:證書相關信息(自身公鑰信息,過期時間,CA名稱,證書簽名算法….)
2、S:證書的數字簽名(說白了也就是對證書內容(C部分內容)進行哈希運算)
其主要流程:
1,瀏覽器(客戶端)請求一個安全的頁面(通過以https://開頭)
2,WEB服務器返回它的數字證書(里面包含公鑰信息)
3,瀏覽器校驗證書是由可信的機構頒發的(通常是可信的根CA),證書仍然有效並且證書與被訪問的網站相關
4,瀏覽器從得到的證書中提取公鑰
4-1,通過公鑰來加密(一般是rsa非對稱加密)一個隨機的對稱密鑰(每次https連接都會產生一個隨機對稱密鑰,對稱加密算法是客戶端與服務端協商確定的(外界不知道,每次連接可能對稱加密算法就變了)),
4-2,使用客戶端服務端都知道的對稱加密算法,客戶端對其url及要發送的數據進行對稱加密
4-3,客戶端將經過公鑰非對稱加密過的加密密鑰,及加密過的數據 發送給服務端
5,服務端使用私鑰解密對稱密鑰,並用它來解密在瀏覽器上加密了的URL和http數據
6,服務端使用已用私鑰解密的對稱密鑰 加密請求的HTML文檔和http數據並發回至客戶端
7,客戶端使用對稱密鑰解密HTML文檔和http數據並展示給用戶
https其主要包含兩個主要功能:
1,對通信雙方進行身份認證(一般都是客戶端通過服務端發過來的數字證書進行其驗證從而確定服務端身份是否合法,是否安全)
1-1,數字證書包含一些服務端公鑰信息,及一些證書的相關信息如該證書是由哪個CA(證書授權機構)頒發的,來自簽發機構的簽名等
1-2,為什么不將服務端公鑰信息單獨發送給 客戶端?
因為防止公鑰信息在數據傳輸過程中被竊取及修改,為了安全性將公鑰信息與一些證書相關信息組合在一起發送給客戶端
2,通信雙方對數據進行加密
客戶端CA對其服務端證書進行校驗過程如下:
一:對其證書不進行校驗
1,簡易的https web 服務器
server.go:
package main
import (
"fmt"
"net/http"
)
/*
只要實現了 ServerHTTP方法 便可構建web服務器
*/
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Hi, This is an example of https service in golang!")
}
func main() {
http.HandleFunc("/", handler) //設置路由及相對應的處理函數 且實現了ServerHTTP方法
http.ListenAndServeTLS("192.168.20.162:8001", "server.crt",
"server.key", nil) //server.crt:服務端證書 包含服務端公鑰信息 server.key:服務端私鑰
/*
生成私鑰:
openssl genrsa -out server.key 2048
生成證書信息:
openssl req -new -x509 -key server.key -out server.crt -days 365
*/
}
client.go:
package main
import (
"crypto/tls"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
/*
client與server進行通信時 client也要對server返回數字證書進行校驗
因為server自簽證書是無效的 為了client與server正常通信
通過設置客戶端跳過證書校驗
TLSClientConfig:{&tls.Config{InsecureSkipVerify: true}
true:跳過證書校驗
*/
client := &http.Client{Transport: tr}
resp, err := client.Get("https://192.168.20.162:8002")
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
1-1,執行 go run server.go
1-2,瀏覽器訪問 https://192.168.20.162:8001 如下:
1-3,繼續點擊 添加例外方可繼續進行訪問
出現這種原因:
瀏覽器利用自身的CA對服務器返回的 數字證書進行合法性校驗時發現 該數字證書是自簽證書,對其該證書不信任及認定為無效證書,因而導致無法繼續訪問
1-4, 執行 go run client.go 便可正常訪問服務器(因為此時客戶端跳過了證書校驗)
二:對其服務端證書進行校驗
1,瀏覽器本身內置了一些有權威的CA(證書授權機構其實也是一個數字證書)
2,CA證書自身也包含自己的公鑰信息,及一些證書的相關信息如該證書是由哪個CA(證書授權機構)頒發的,來自簽發機構的簽名等
3,客戶端對來自服務端證書的校驗就是使用CA證書 校驗對來自服務端證書的簽名是否是 這個CA簽的
3-1 CA校驗服務端數字證書簽名過程:
1,客戶端利用自身CA證書中的簽名算法對 服務端證書內容部分(C部分)進行相對應的哈希運算得到哈希值(也就是對內容利用自身的哈希算法進行簽名)
2,客戶端利用得到的哈希值與服務端數字證書的證書簽名 進行比較
若相同則服務端證書 便是由該CA頒發的 否則不是該CA頒發的
4,代碼如下
首先准備好服務端的私鑰及證書 客戶端的CA證書
4-1,使用openssl命令生成相關私鑰及證書
1,生成 CA 私鑰
openssl genrsa -out ca.key 2048
2,生成CA證書
openssl req -x509 -new -nodes -key ca.key -subj "/CN=ca_host" -days 5000 -out ca.crt
CN=ca_host":設置該證書 由那台服務器生成(若只進行客戶端對服務端證書校驗 此處可以隨便填 不影響)
3,生成服務端私鑰
openssl genrsa -out server.key 2048
4,生成服務端證書認證請求
openssl req -new -key server.key -subj "/CN=gc_host" -out server.csr
CN=gc_host:此處必須按真實填寫 服務端在哪台服務器設備起着就必須填寫哪台設備的主機名
不同的客戶端設備在調用時 需在自身設備 /etc/hosts 配置服務器設備ip及主機名
因為客戶端在請求url中只識別服務端證書的 CN
證書認證請求並不是證書,需要CA的私鑰進行簽名之后方是證書
5,生成服務端證書
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 5000
server.go:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,
"Hi, This is an example of http service in golang!")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServeTLS("192.168.20.162:8003",
"server.crt", "server.key", nil)
}
client.go:
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
)
/*
客戶端若要對服務端的數字證書進行校驗 需發送請求之前 加載CA證書
*/
func main() {
pool := x509.NewCertPool()
caCertPath := "ca.crt"
caCrt, err := ioutil.ReadFile(caCertPath)
if err != nil {
fmt.Println("ReadFile err:", err)
return
}
pool.AppendCertsFromPEM(caCrt) //客戶端添加ca證書
tr := &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: pool}, //客戶端加載ca證書
DisableCompression: true,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://gc_host:8003/")
if err != nil {
fmt.Println("Get error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
