一:HTTPS介紹
HTTPS (Secure Hypertext Transfer Protocol)安全超文本傳輸協(xié)議,是一個(gè)安全通信通道,它基于HTTP開發(fā)用于在客戶計(jì)算機(jī)和服務(wù)器之間交換信息碴倾。它使用安全套接字層(SSL)進(jìn)行信息交換偎漫,簡(jiǎn)單來說它是HTTP的安全版,是使用TLS/SSL加密的HTTP協(xié)議穆咐。
HTTP和HTTPS的區(qū)別
? HTTPS是加密傳輸協(xié)議耕陷,HTTP是名文傳輸協(xié)議
? HTTPS需要用到SSL證書,而HTTP不用
? HTTPS比HTTP更加安全辙喂,對(duì)搜索引擎更友好捶牢,利于SEO
? HTTPS標(biāo)準(zhǔn)端口443,HTTP標(biāo)準(zhǔn)端口80
? HTTPS基于傳輸層巍耗,HTTP基于應(yīng)用層
? HTTPS在瀏覽器顯示綠色安全鎖秋麸,HTTP沒有顯示
二、HTTPS證書
正式發(fā)布的時(shí)候炬太,是需要購買正規(guī)的證書的竹勉。測(cè)試程序時(shí),如果沒有娄琉,我們可以使用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實(shí)現(xiàn)HTTPS程序
第一個(gè)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)聽孽水,必須提供證書文件和對(duì)應(yīng)的私鑰文件。
http.ListenAndServeTLS(":8081", "server.crt",
"server.key", nil)
}
瀏覽器輸入 https://127.0.0.1:8081進(jìn)行測(cè)試即可城看。
注意瀏覽器會(huì)提示此鏈接不安全女气,繼續(xù)訪問即可,因?yàn)檫@個(gè)證書使我們自己生成的测柠,并不被瀏覽器所承認(rèn)炼鞠。上面兩個(gè)文件的路徑可以是絕對(duì)路徑也可以相對(duì),這里在同一文件夾下使用的
四轰胁、訪問自己的HTTPS服務(wù)端
go實(shí)現(xiàn)的Client端默認(rèn)也是要對(duì)服務(wù)端傳過來的數(shù)字證書進(jìn)行校驗(yàn)的谒主,因?yàn)槲覀兊淖C書并不是知名CA簽發(fā)的。所以我們要跳過驗(yàn)證赃阀,如下
func main() {
//要管理代理霎肯、TLS配置、keep-alive、壓縮和其他設(shè)置观游,創(chuàng)建一個(gè)Transport
//Client和Transport類型都可以安全的被多個(gè)go程同時(shí)使用搂捧。出于效率考慮,應(yīng)該一次建立懂缕、盡量重用允跑。
tr := &http.Transport{
//InsecureSkipVerify用來控制客戶端是否證書和服務(wù)器主機(jī)名。如果設(shè)置為true,
//則不會(huì)校驗(yàn)證書以及證書中的主機(jī)名和服務(wù)器主機(jī)名是否一致搪柑。
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))
}
五聋丝、對(duì)服務(wù)端證書進(jìn)行校驗(yàn)
多數(shù)時(shí)候,我們需要對(duì)服務(wù)端的證書進(jìn)行校驗(yàn)拌屏,而不是像上面那樣忽略這個(gè)校驗(yàn)潮针。
首先我們來建立我們自己的CA,需要生成一個(gè)CA私鑰和一個(gè)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ù)字證書請(qǐng)求,并用我們的ca私鑰簽發(fā)server的數(shù)字證書:
(1)生成服務(wù)端私鑰
openssl genrsa -out server.key 2048
(2)生成證書請(qǐng)求文件
openssl req -new -key server.key -subj "/CN=localhost" -out server.csr
(3)根據(jù)CA的私鑰和上面的證書請(qǐng)求文件生成服務(wù)端證書
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 5000
client.go
//客戶端對(duì)服務(wù)器校驗(yàn)
func main() {
//CertPool代表一個(gè)證書集合/證書池端圈。
//創(chuàng)建一個(gè)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è)置的根證書和中間證書對(duì)葉子證書進(jìn)行驗(yàn)證舱权。
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 是證書請(qǐng)求文件,用于申請(qǐng)證書宴倍。在制作csr文件的時(shí)张症,必須使用自己的私鑰來簽署,還可以設(shè)定一個(gè)密鑰
? crt是CA認(rèn)證后的證書文鸵贬,簽署人用自己的key給你簽署的憑證
六俗他、對(duì)客戶端證書進(jìn)行校驗(yàn)
要對(duì)客戶端數(shù)字證書進(jìn)行校驗(yàn),首先客戶端需要先有自己的證書阔逼。
我們以上面的例子為基礎(chǔ)兆衅,生成客戶端的私鑰與證書。
(1)生成client私鑰
openssl genrsa -out client.key 2048
(2)生成client請(qǐng)求文件
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))
}