一:HTTPS介紹
HTTPS (Secure Hypertext Transfer Protocol)安全超文本傳輸協(xié)議,是一個安全通信通道,它基于HTTP開發(fā)用于在客戶計算機和服務(wù)器之間交換信息。它使用安全套接字層(SSL)進行信息交換,簡單來說它是HTTP的安全版,是使用TLS/SSL加密的HTTP協(xié)議。
HTTP和HTTPS的區(qū)別
? HTTPS是加密傳輸協(xié)議,HTTP是名文傳輸協(xié)議
? HTTPS需要用到SSL證書,而HTTP不用
? HTTPS比HTTP更加安全,對搜索引擎更友好,利于SEO
? HTTPS標(biāo)準(zhǔn)端口443,HTTP標(biāo)準(zhǔn)端口80
? HTTPS基于傳輸層,HTTP基于應(yīng)用層
? HTTPS在瀏覽器顯示綠色安全鎖,HTTP沒有顯示
二、HTTPS證書
正式發(fā)布的時候,是需要購買正規(guī)的證書的。測試程序時,如果沒有,我們可以使用openssl來生成私人的證書。
(1)首先我們先生成證書私鑰
openssl genrsa -out server.key 2048
(2)根據(jù)私鑰生成公鑰
openssl rsa -in server.key -out server.key.public
(2)根據(jù)私鑰生成證書
openssl req -new -x509 -key server.key -outserver.crt -days 365
注意以上命令是生成在當(dāng)前文件夾下的
三、Golang實現(xiàn)HTTPS程序
第一個HTTPS程序
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,
"Hi, This is an example of https service in golang!")
}
func main1() {
http.HandleFunc("/", handler)
//https監(jiān)聽,必須提供證書文件和對應(yīng)的私鑰文件。
http.ListenAndServeTLS(":8081", "server.crt",
"server.key", nil)
}
瀏覽器輸入 https://127.0.0.1:8081進行測試即可。
注意瀏覽器會提示此鏈接不安全,繼續(xù)訪問即可,因為這個證書使我們自己生成的,并不被瀏覽器所承認(rèn)。上面兩個文件的路徑可以是絕對路徑也可以相對,這里在同一文件夾下使用的
四、訪問自己的HTTPS服務(wù)端
go實現(xiàn)的Client端默認(rèn)也是要對服務(wù)端傳過來的數(shù)字證書進行校驗的,因為我們的證書并不是知名CA簽發(fā)的。所以我們要跳過驗證,如下
func main() {
//要管理代理、TLS配置、keep-alive、壓縮和其他設(shè)置,創(chuàng)建一個Transport
//Client和Transport類型都可以安全的被多個go程同時使用。出于效率考慮,應(yīng)該一次建立、盡量重用。
tr := &http.Transport{
//InsecureSkipVerify用來控制客戶端是否證書和服務(wù)器主機名。如果設(shè)置為true,
//則不會校驗證書以及證書中的主機名和服務(wù)器主機名是否一致。
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://localhost:8081")
if err != nil {
fmt.Println("error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
五、對服務(wù)端證書進行校驗
多數(shù)時候,我們需要對服務(wù)端的證書進行校驗,而不是像上面那樣忽略這個校驗。
首先我們來建立我們自己的CA,需要生成一個CA私鑰和一個CA的數(shù)字證書:
(1)生成CA私鑰
openssl genrsa -out ca.key 2048
(2)生成CA證書
openssl req -x509 -new -nodes -key ca.key -subj "/CN=tonybai.com" -days 5000 -out ca.crt
接下來,生成server端的私鑰,生成數(shù)字證書請求,并用我們的ca私鑰簽發(fā)server的數(shù)字證書:
(1)生成服務(wù)端私鑰
openssl genrsa -out server.key 2048
(2)生成證書請求文件
openssl req -new -key server.key -subj "/CN=localhost" -out server.csr
(3)根據(jù)CA的私鑰和上面的證書請求文件生成服務(wù)端證書
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 5000
client.go
//客戶端對服務(wù)器校驗
func main() {
//CertPool代表一個證書集合/證書池。
//創(chuàng)建一個CertPool
pool := x509.NewCertPool()
caCertPath := "/Users/zt/GOProject/src/https/ca.crt"
//調(diào)用ca.crt文件
caCrt, err := ioutil.ReadFile(caCertPath)
if err != nil {
fmt.Println("ReadFile err:", err)
return
}
//解析證書
pool.AppendCertsFromPEM(caCrt)
tr := &http.Transport{
////把從服務(wù)器傳過來的非葉子證書,添加到中間證書的池中,使用設(shè)置的根證書和中間證書對葉子證書進行驗證。
TLSClientConfig: &tls.Config{RootCAs: pool},
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://localhost:8081")
if err != nil {
fmt.Println("Get error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
? Key 是私用密鑰openssl,通常是rsa算法
? Csr 是證書請求文件,用于申請證書。在制作csr文件的時,必須使用自己的私鑰來簽署,還可以設(shè)定一個密鑰
? crt是CA認(rèn)證后的證書文,簽署人用自己的key給你簽署的憑證
六、對客戶端證書進行校驗
要對客戶端數(shù)字證書進行校驗,首先客戶端需要先有自己的證書。
我們以上面的例子為基礎(chǔ),生成客戶端的私鑰與證書。
(1)生成client私鑰
openssl genrsa -out client.key 2048
(2)生成client請求文件
openssl req -new -key client.key -subj "/CN=tonybai_cn" -out client.csr
(3)生成client證書
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 5000
client.go
type myhandler struct {
}
func (h *myhandler) ServeHTTP(w http.ResponseWriter,
r *http.Request) {
fmt.Fprintf(w,
"Hi, This is an example of http service in golang!\n")
}
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)
s := &http.Server{
Addr: ":8081",
Handler: &myhandler{},
TLSConfig: &tls.Config{
ClientCAs: pool,
ClientAuth: tls.RequireAndVerifyClientCert,
},
}
err = s.ListenAndServeTLS("server.crt", "server.key")
if err != nil {
fmt.Println("ListenAndServeTLS err:", err)
}
}
server.go
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)
cliCrt, err := tls.LoadX509KeyPair("client.crt", "client.key")
if err != nil {
fmt.Println("Loadx509keypair err:", err)
return
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
Certificates: []tls.Certificate{cliCrt},
},
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://localhost:8081")
if err != nil {
fmt.Println("Get error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}