使用 golang 進行證書籤發和雙向認證
【導讀】本文介紹 go 語言實現認證的思路。
前言
數字證書是一個經證書授權中心數字簽名的包含公開密鑰擁有者信息以及公開密鑰的文件。證書籤發涉及到了非對稱加密方面的知識, 這裏介紹使用 golang 中的 x509 標準庫進行證書自簽發, 還有證書籤發後如何使用 golang 進行雙向認證.
自簽發證書
生成根證書
根證書是 CA 認證中心給自己頒發的證書, 是信任鏈的起始點. 這裏我們自己做 CA 使用 openssl 命令來生成根證書.
首先生成私鑰
openssl genrsa -out key.pem 2048
然後根據私鑰提取公鑰
openssl rsa -in key.pem -pubout -out key.pub
開始生成 X509 格式的自簽名證書, 會要求輸入區別名 DN 的各項信息(國家,城市,組織,姓名,email 等.
penssl req -x509 -new -days 365 -key rsakey.pem -out cert.crt
到這裏根證書就製作好了, 下面開始使用 golang 根據根證書籤發下一級證書.
使用 golang 自簽發證書
關於自簽發證書的流程, 這裏做個簡單介紹. golang 的 x509 標準庫下有個 Certificate 結構, 這個結構就是證書解析後對應的實體. 新證書需要先生成祕鑰對, 然後使用根證書的私鑰進行簽名. 證書和私鑰以及公鑰這裏使用的是 pem 編碼方式.
首先讀取根證書的證書和私鑰
//解析根證書
caFile, err := ioutil.ReadFile(rootCa)
if err != nil {
return
}
caBlock, _ := pem.Decode(caFile)
cert, err := x509.ParseCertificate(caBlock.Bytes)
if err != nil {
return
}
//解析私鑰
keyFile, err := ioutil.ReadFile(rootKey)
if err != nil {
return
}
keyBlock, _ := pem.Decode(keyFile)
praKey, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
if err != nil {
return
}
然後需要生成新證書的模板, 裏面的字段根據自己需求填寫,
cer := &x509.Certificate{
SerialNumber: big.NewInt(rd.Int63()), //證書序列號
Subject: pkix.Name{
Country: []string{"CN"},
Organization: []string{"Easy"},
OrganizationalUnit: []string{"Easy"},
Province: []string{"ShenZhen"},
CommonName: equi.Code,
Locality: []string{"ShenZhen"},
},
NotBefore: time.Now(), //證書有效期開始時間
NotAfter: time.Now().AddDate(1, 0, 0), //證書有效期結束時間
BasicConstraintsValid: true, //基本的有效性約束
IsCA: false, //是否是根證書
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, //證書用途(客戶端認證,數據加密)
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment,
EmailAddresses: []string{"test@test.com"},
IPAddresses: []net.IP{net.ParseIP("192.168.1.59")},
}
當獲取到這種信息後就可以簽發證書了, key 和 ca 就是簽發好的證書和私鑰.
//生成公鑰私鑰對
priKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return
}
ca, err = x509.CreateCertificate(rand.Reader, equiCer, rootCa, &priKey.PublicKey, rootKey)
if err != nil {
return
}
//編碼證書文件和私鑰文件
caPem := &pem.Block{
Type: "CERTIFICATE",
Bytes: ca,
}
ca = pem.EncodeToMemory(caPem)
buf := x509.MarshalPKCS1PrivateKey(priKey)
keyPem := &pem.Block{
Type: "PRIVATE KEY",
Bytes: buf,
}
key = pem.EncodeToMemory(keyPem)
使用 golang 進行雙向認證
客戶端
//加載客戶端證書
//這裏加載的是服務端簽發的
cert, err := tls.LoadX509KeyPair("client_cert.pem", "client_key.pem")
if err != nil {
log.Fatalln(err)
}
config := &tls.Config{
//這裏先不驗證服務端證書,是自己簽發的呀
InsecureSkipVerify: true,
Certificates: []tls.Certificate{cert},
}
raddr, err := net.ResolveTCPAddr("tcp", "192.168.1.59:6001")
if err != nil {
log.Fatalln(err)
}
conn, err := net.DialTCP("tcp", nil, raddr)
if err != nil {
log.Fatalln(err)
}
tlsConn := tls.Client(conn, config)
tlsConn 就和 net.Conn 一樣了, 當調用 Wirte 時就會進行握手, 如果服務端證書不符合要求, 就會返回錯誤.
服務端
//這裏讀取的是根證書
buf, err := ioutil.ReadFile(d.conf.Tls.CA)
if err != nil {
return
}
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(buf)
//加載服務端證書
cert, err := tls.LoadX509KeyPair(d.conf.Tls.Cert, d.conf.Tls.Key)
if err != nil {
return
}
tlsConfig := &tls.Config
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: pool,
}
//accept到conn後
tlsConn := tls.Server(conn, tlsConfig)
這個 tlsConn 和客戶端的一樣, 也可以手動調用 Handshake 進行握手.
結語
這裏只是進行了簡單介紹, 關於證書的知識還不止這些. 比如 x509 標準庫還可以生成簽發證書請求, 解析證書吊銷列表等. 多加練習才能對證書方面知識理解更深.
轉自:
segmentfault.com/a/1190000009666888
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/JtIWAyOPNgc08JSvqoFBmA