手把手教你用 Go 實現一個 mTLS
- 原文地址:https://venilnoronha.io/a-step-by-step-guide-to-mtls-in-go
- 原文作者:
Venil Noronha
- 本文永久連結:https://github.com/gocn/translator/blob/master/2021/w44_A_step_by_step_guide_to_mTLS_in_Go.md
- 譯者:朱亞光
- 校對:
想知道什麼是 mTLS(雙向 TLS)?來吧,讓我們用 Golang 和 OpenSSL 實現一個 mTLS。
介紹
TLS(安全傳輸層協議簡稱)為網路通訊的應用程式提供必要的加密。HTTPS (超文字安全協議) 是 HTTP 的一種擴充套件,利用 TLS 實現安全性。TLS 技術要求 CA (證書頒發機構) 向服務頒發 X.509 數字證書,然後將該數字證書移交給服務的消費者,由其使用 CA 本身進行驗證。mTLS 將同樣的思想擴充套件到應用程式中,例如,在微服務中,提供者和消費者都需要向對方生成自己的證書。這些證書由雙方使用各自的 CA 進行驗證。一旦經過驗證,伺服器/客戶端或提供者/使用者之間的通訊就會安全地進行。
實現
第一步 - 構建一個簡單的 HTTP 服務端和客戶端
讓我們先 在server.go
裡建立一個簡單的 HTTP 服務,當訪問 8080
埠,請求 /hello
資源時回覆 Hello, world!
package main
import (
"io"
"log"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
// Write "Hello, world!" to the response body
io.WriteString(w, "Hello, world!\n")
}
func main() {
// Set up a /hello resource handler
http.HandleFunc("/hello", helloHandler)
// Listen to port 8080 and wait
log.Fatal(http.ListenAndServe(":8080", nil))
}
客戶端通過 8080
埠請求 /hello
資源,然後將響應體通過 stdout
列印。下面是 client.go
程式碼:
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
func main() {
// Request /hello over port 8080 via the GET method
r, err := http.Get("http://localhost:8080/hello")
if err != nil {
log.Fatal(err)
}
// Read the response body
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
// Print the response body to stdout
fmt.Printf("%s\n", body)
}
開啟一個終端並執行服務端:
go run -v server.go
開啟另外一個終端執行客戶端:
go run -v client.go
你可以從客戶端看到以下輸出:
Hello, world!
第二步 - 生成和使用服務端證書
使用以下命令生成證書。該命令將建立一個有效期為 10 年的 2048 位金鑰證書。此外,CN=localhost
說明該證書對 localhost
域是有效的。
openssl req -newkey rsa:2048 \
-new -nodes -x509 \
-days 3650 \
-out cert.pem \
-keyout key.pem \
-subj "/C=US/ST=California/L=Mountain View/O=Your Organization/OU=Your Unit/CN=localhost"
你應該目錄裡面有 cert.pem
和 key.pem
證書了。
現在讓我們啟用伺服器 HTTP 的 TLS,也就是 HTTPS。server.go
裡面的 http.ListenAndServe(":8080", nil)
呼叫替換成 http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil)
,通過 8443
埠監聽連結。同時提供前面生成的證書。
-// Listen to port 8080 and wait
-log.Fatal(http.ListenAndServe(":8080", nil))
+// Listen to HTTPS connections on port 8443 and wait
+log.Fatal(http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil))
你可以通過執行並在瀏覽器輸入 https://localhost:8443/hello
來驗證伺服器是否工作。
現在我們更新 client.go
程式碼來通過 HTTPS 連線服務端。
-// Request /hello over port 8080 via the GET method
-r, err := http.Get("http://localhost:8080/hello")
+// Request /hello over HTTPS port 8443 via the GET method
+r, err := http.Get("https://localhost:8443/hello")
由於我們客戶端還不知道證書,直接執行伺服器會顯示下面的錯誤:
http: TLS handshake error from [::1]:59436: remote error: tls: bad certificate
在客戶端,你需要注意以下幾點:
x509: certificate is not valid for any names, but wanted to match localhost
第三步- 向客戶端提供證書
更新 client.go
程式碼讀取之前生成的證書,程式碼如下:
-// Request /hello over HTTPS port 8443 via the GET method
-r, err := http.Get("https://localhost:8443/hello")
+// Create a CA certificate pool and add cert.pem to it
+caCert, err := ioutil.ReadFile("cert.pem")
+if err != nil {
+log.Fatal(err)
+}
+caCertPool := x509.NewCertPool()
+caCertPool.AppendCertsFromPEM(caCert)
+
+// Create a HTTPS client and supply the created CA pool
+client := &http.Client{
+Transport: &http.Transport{
+TLSClientConfig: &tls.Config{
+RootCAs: caCertPool,
+},
+},
+}
+
+// Request /hello via the created HTTPS client over port 8443 via GET
+r, err := client.Get("https://localhost:8443/hello")
這裡,我們讀取 cert.pem
檔案並在建立客戶端時提供根 CA 。執行客戶端現在應該可以成功顯示以下內容:
Hello, world!
最後一步 - 啟用 mTLS
在客戶端,讀取並提供金鑰對作為客戶端證書。
+// Read the key pair to create certificate
+cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
+if err != nil {
+log.Fatal(err)
+}
...
-// Create a HTTPS client and supply the created CA pool
+// Create a HTTPS client and supply the created CA pool and certificate
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
+Certificates: []tls.Certificate{cert},
},
},
}
在服務端,我們建立一個類似於 CA 池 ,並將其提供給 TLS 配置,來作為驗證客戶端證書的權威。我們還對伺服器證書使用相同的金鑰對。
-// Listen to HTTPS connections on port 8443 and wait
-log.Fatal(http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil))
+// Create a CA certificate pool and add cert.pem to it
+caCert, err := ioutil.ReadFile("cert.pem")
+if err != nil {
+log.Fatal(err)
+}
+caCertPool := x509.NewCertPool()
+caCertPool.AppendCertsFromPEM(caCert)
+
+// Create the TLS Config with the CA pool and enable Client certificate validation
+tlsConfig := &tls.Config{
+ClientCAs: caCertPool,
+ClientAuth: tls.RequireAndVerifyClientCert,
+}
+tlsConfig.BuildNameToCertificate()
+
+// Create a Server instance to listen on port 8443 with the TLS config
+server := &http.Server{
+Addr: ":8443",
+TLSConfig: tlsConfig,
+}
+
+// Listen to HTTPS connections with the server certificate and wait
+log.Fatal(server.ListenAndServeTLS("cert.pem", "key.pem"))
先 執行 server.go
然後執行 client.go
,然後你可以在客戶端上看到如下一條成功的訊息:
Hello, world!
完整程式碼
最終, server.go
程式碼如下:
package main
import (
"crypto/tls"
"crypto/x509"
"io"
"io/ioutil"
"log"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
// Write "Hello, world!" to the response body
io.WriteString(w, "Hello, world!\n")
}
func main() {
// Set up a /hello resource handler
http.HandleFunc("/hello", helloHandler)
// Create a CA certificate pool and add cert.pem to it
caCert, err := ioutil.ReadFile("cert.pem")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// Create the TLS Config with the CA pool and enable Client certificate validation
tlsConfig := &tls.Config{
ClientCAs: caCertPool,
ClientAuth: tls.RequireAndVerifyClientCert,
}
tlsConfig.BuildNameToCertificate()
// Create a Server instance to listen on port 8443 with the TLS config
server := &http.Server{
Addr: ":8443",
TLSConfig: tlsConfig,
}
// Listen to HTTPS connections with the server certificate and wait
log.Fatal(server.ListenAndServeTLS("cert.pem", "key.pem"))
}
client.go
程式碼如下:
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"log"
"net/http"
)
func main() {
// Read the key pair to create certificate
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
if err != nil {
log.Fatal(err)
}
// Create a CA certificate pool and add cert.pem to it
caCert, err := ioutil.ReadFile("cert.pem")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// Create a HTTPS client and supply the created CA pool and certificate
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
Certificates: []tls.Certificate{cert},
},
},
}
// Request /hello via the created HTTPS client over port 8443 via GET
r, err := client.Get("https://localhost:8443/hello")
if err != nil {
log.Fatal(err)
}
// Read the response body
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
// Print the response body to stdout
fmt.Printf("%s\n", body)
}
結論
Golang 讓實現 mTLS 變得非常容易,而且不到 100 行程式碼。
我很想聽聽你的意見。
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 使用 Java實現mTLS呼叫JavaTLS
- 手把手教你用redis實現一個簡單的mq訊息佇列(java)RedisMQ佇列Java
- 手把手教你用ManagedSQLiteOpenHelper實現資料庫SQLite資料庫
- 手把手和你一起實現一個Web框架實戰——EzWeb框架(二)[Go語言筆記]Go專案實戰Web框架Go筆記
- 手把手和你一起實現一個Web框架實戰——EzWeb框架(三)[Go語言筆記]Go專案實戰Web框架Go筆記
- 手把手和你一起實現一個Web框架實戰——EzWeb框架(四)[Go語言筆記]Go專案實戰Web框架Go筆記
- 手把手和你一起實現一個Web框架實戰——EzWeb框架(五)[Go語言筆記]Go專案實戰Web框架Go筆記
- 手把手教你用node擼一個圖片壓縮工具
- GO 實現一個 PACK 和 UnPackGo
- 用 Go 實現一個 LRU cacheGo
- 百聞不如一碼!手把手教你用Python搭一個TransformerPythonORM
- 手把手教你用vue搭建個人站Vue
- 動手實踐丨手把手教你用STM32做一個智慧魚缸
- 手把手實現一個mini-Retrofit框架框架
- 手把手教你用RecyclerView實現貓眼電影選擇效果View
- 手把手教你用python做一個年會抽獎系統Python
- 手把手教你實現一個引導動畫動畫
- 手把手帶你使用 typescript 實現一個 axios 庫(一)TypeScriptiOS
- 手把手教你用Python實踐深度學習Python深度學習
- 用 go 實現一個簡單的 mvcGoMVC
- 手把手教你實現一個canvas智繪畫板Canvas
- 手把手教你實現一個 Vue 進度條元件!Vue元件
- 技術實踐——教你用100行寫一個 go 的協程池 (任務池)!!!Go
- 手把手教你用node擼一個簡易的headless爬蟲cli工具爬蟲
- 從0開始,手把手教你用Vue開發一個答題AppVueAPP
- 手把手教你用java實現二分查詢樹及其相關操作Java
- Go語言系列之手把手教你擼一個ORM(一)GoORM
- 手把手教你用Hexo搭建個人技術部落格Hexo
- Go實現了一個負載均衡器Go負載
- 用 Go + WebSocket 快速實現一個 chat 服務GoWeb
- go 技巧: 實現一個無限 buffer 的 channelGo
- 手把手教你用Taro框架寫一個影象處理類微信小程式框架微信小程式
- 小白指南:手把手教你用低程式碼開發一個應用頁面
- 【“探探”為例】手把手教你用最少的程式碼實現各種“機器人”機器人
- 手把手教你用Charles抓包
- 手把手教你實現一個基於Redis的分散式鎖Redis分散式
- 手把手實現一個web程式碼模板快速生成CLI工具Web
- Go語言筆記[實現一個Web框架實戰]——EzWeb框架(一)Go筆記Web框架