前言
在進行專案總結的時候,領導提出有關資料安全的問題。總結會議過後,自己查閱了一下資料,發現基於CA的TLS證照認證方案是一個很好的選擇,雖然專案本身也有關於資料安全的處理,但是從遠不及TLS的處理方式。
本文只介紹tls的開發,採用go語言,不會涉及到太多專業的詞語。
製作自簽名證照
初始目錄如下:
grpc-tls/
├── configs
│ ├── cert # 存放證照相關的目錄
├── cmd
CA
為了保證證照的可靠性和有效性,在這裡可引入 CA 頒發的根證照的概念。CA就是專門用自己的私鑰給別人進行簽名的單位或者機構,其遵守 X.509 標準,即無論是客戶端還是服務端都是使用CA來簽發證照。
根證照
根證照(root certificate)是屬於根證照頒發機構(CA)的公鑰證照。我們可以通過驗證 CA 的簽名從而信任 CA ,任何人都可以得到 CA 的證照(含公鑰),用以驗證它所簽發的證照(客戶端、服務端)。
它包含了公鑰和金鑰。
CA公鑰
進入cert目錄
openssl genrsa -out ca.key 2048
openssl genrsa
:生成RSA
私鑰,命令的最後一個引數,將指定生成金鑰的位數,如果沒有指定,預設512
CA祕鑰(證照)
openssl req -new -x509 -days 365 -key ca.key -out ca.pem
-x509
:證照檔案格式為x509,目前TLS預設只支援這種格式的證照-days 365
:證照有效期1年-out ca.pem
:生成的私鑰儲存到ca.pem
要填寫的資訊:
Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:ca.com
Email Address []:
- 生成的過程中會要求填一些資訊,除了Common Name要取一個容易區分的名字之外,其它都可以隨便填寫,我們在這裡將它填為ca.com.
目錄
grpc-tls/
├── configs
│ ├── cert # 存放證照相關的目錄
│ ├── ca.key
│ └── ca.pem
├── cmd
伺服器證照相關
伺服器key
# openssl genrsa -out server.key 2048
或者
openssl ecparam -genkey -name secp384r1 -out server.key
openssl genrsa
:生成RSA
私鑰,命令的最後一個引數,將指定生成金鑰的位數,如果沒有指定,預設512openssl ecparam
:生成ECC
私鑰,命令為橢圓曲線金鑰引數生成及操作,本文中ECC
曲線選擇的是secp384r1
生成 CSR(證照申請檔案)
CSR 是Cerificate Signing Request 的英文縮寫,為證照申請檔案,在伺服器私鑰的基礎上加上一些申請人的屬性資訊,比如我是誰,來自哪裡,名字叫什麼,證照適用於什麼場景等的資訊,然後帶上進行的簽名,發給CA(私下安全的方式傳送),帶上自己簽名的目的是為了防止別人篡改檔案。
openssl req -new -key server.key -out server.csr
要填寫的資訊:
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:domain.com
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:123456
An optional company name []:
- 生成的過程中會要求填一些資訊,除了Common Name要取一個容易區分的名字之外,其它都可以隨便填寫,我們在這裡將它填為domain.com;
- 密碼也是建議填寫;
- 注意和ca證照的不同。
基於 CA 簽發
使用CA的公鑰對申請檔案進行簽名
openssl x509 -req -sha256 -CA ca.pem -CAkey ca.key -CAcreateserial -days 3650 -in server.csr -out server.pem
-sha256
:生成的證照裡面使用sha256作為摘要演算法- 由於需要往生成的證照裡寫入簽名者的資訊,所以這裡需要ca.pem,因為只有這裡有CA的描述資訊,ca.key裡面只有公鑰的資訊。
目錄
grpc-tls/
├── configs
│ ├── cert # 存放證照相關的目錄
│ ├── ca.key
│ └── ca.pem
│ ├── server.csr
│ └── server.key
│ └── server.pem
├── cmd
客戶端證照相關
此生成的證照可用於瀏覽器、java、tomcat、golang等。
客戶端key
openssl ecparam -genkey -name secp384r1 -out client.key
生成CSR(證照申請書)
openssl req -new -key client.key -out client.csr
要填寫的資訊:
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:domain.com
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:123456
An optional company name []:
- Common Name要取一個容易區分的名字之外,填為domain.com,和伺服器的CSR保持一致;
- 密碼也是建議填寫;
- 注意和ca證照的不同。
基於CA簽發
openssl x509 -req -sha256 -CA ca.pem -CAkey ca.key -CAcreateserial -days 3650 -in client.csr -out client.pem
生成客戶端p12格式根證照
該證照用於匯入瀏覽器使用。
openssl pkcs12 -export -clcerts -in client.pem -inkey client.key -out client.p12
目錄
grpc-tls/
├── configs
│ ├── cert # 存放證照相關的目錄
│ ├── ca.key
│ └── ca.pem # 匯入瀏覽器使用
│ ├── server.csr
│ └── server.key
│ └── server.pem
│ ├── client.csr
│ └── client.key
│ └── client.pem
│ └── client.p12 # 匯入瀏覽器使用
├── cmd
證照如何驗證
下面以瀏覽器為例,說明證照的驗證過程:
在TLS握手的過程中,瀏覽器得到了網站的證照(.p12)
開啟證照,檢視是哪個CA簽名的這個證照(.p12)
在自己信任的CA庫中,找相應CA的證照(ca.pem),
用CA證照裡面的公鑰解密網站證照上的簽名,取出網站證照的校驗碼(指紋),然後用CA證照中摘要演算法(比如sha256)算出出網站證照的校驗碼,如果校驗碼和簽名中的校驗碼對的上,說明這個證照是合法的,且沒被人篡改過
讀出裡面的CN,對於網站的證照,裡面一般包含的是域名
檢查裡面的域名和自己訪問網站的域名對不對的上,對的上的話,就說明這個證照確實是頒發給這個網站的
到此為止檢查通過
如果瀏覽器發現證照有問題,一般是證照裡面的簽名者不是瀏覽器認為值得信任的CA,瀏覽器就會給出警告頁面,這時候需要謹慎,有可能證照被掉包了。如訪問12306網站,由於12306的證照是自己籤的名,並且瀏覽器不認為12306是受信的CA,所以就會給警告,但是一旦你把12306的根證照安裝到了你的瀏覽器中,那麼下次就不會警告了,因為你配置了瀏覽器讓它相信12306是一個受信的CA。
在瀏覽器中匯入證照
匯入證照
詳細步驟百度即可....
在
個人
匯入client.p12
證照
- 在
受信任的根證照頒發機構
匯入ca.pem
證照
修改域名
ca.pem
這個證照是發給domain.com的,而不是127.0.0.1,所以在C:\Windows\System32\drivers\etc\hosts
新增一記錄:
127.0.0.1 domain.com
測試完成之後記得手動將127.0.0.1 domain.com
從C:\Windows\System32\drivers\etc\hosts
裡面刪掉。
golang服務端
目錄
grpc-tls/
├── configs
│ ├── cert # 存放證照相關的目錄
│ ├── ca.key
│ └── ca.pem # 匯入瀏覽器使用
│ ├── server.csr
│ └── server.key
│ └── server.pem
│ ├── client.csr
│ └── client.key
│ └── client.pem
│ └── client.p12 # 匯入瀏覽器使用
├── cmd
│ ├── main.go
main.go
package main
import (
"crypto/tls"
"crypto/x509"
"github.com/gin-gonic/gin"
"io/ioutil"
"log"
"net/http"
"time"
)
func main() {
router := gin.Default()
router.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "test",
})
})
// 啟動https方式訪問
cert, err := tls.LoadX509KeyPair("./configs/cert/server.pem", "./configs/cert/server.key")
if err != nil {
log.Fatalf("tls.LoadX509KeyPair err: %v", err)
}
certPool := x509.NewCertPool()
ca, err := ioutil.ReadFile("./configs/cert/ca.pem")
if err != nil {
log.Fatalf("ioutil.ReadFile err: %v", err)
}
if ok := certPool.AppendCertsFromPEM(ca); !ok {
log.Fatalf("certPool.AppendCertsFromPEM err")
}
ReadTimeout := time.Duration(60) * time.Second
WriteTimeout := time.Duration(60) * time.Second
s := &http.Server{
Addr: ":8090",
Handler: router,
ReadTimeout: ReadTimeout,
WriteTimeout: WriteTimeout,
MaxHeaderBytes: 1 << 20,
TLSConfig:&tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: certPool,
},
}
s.ListenAndServeTLS("./configs/cert/server.pem","./configs/cert/server.key")
}
測試
在瀏覽器輸入https://domain.com:8090/test