Golang(十一)TLS 相關知識(二)OpenSSL 生成證書

0. 前言

1. OpenSSL 簡介

  • OpenSSL 是一個開源項目,其組成主要包括三個組件:
    • openssl:多用途的命令行工具
    • libcrypto:加密算法庫
    • libssl:加密模塊應用庫,實現了ssl及tls
  • OpenSSL 主要用於祕鑰證書管理、對稱加密和非對稱加密

1.1 指令

  • 經常使用指令包括:genrsa、req、x509

1.1.1 genrsa

  • 主要用於生成私鑰,選擇算法、加密私鑰使用的對稱加密密碼和祕鑰長度
  • 基本用法:openssl genrsa [args] [numbits]
[args]:
    args1 對生成的私鑰文件是否要使用加密算法進行對稱加密: 
        -des : CBC模式的DES加密 
            -des3 : CBC模式的3DES加密 
            -aes128 : CBC模式的AES128加密 
        -aes192 : CBC模式的AES192加密 
        -aes256 : CBC模式的AES256加密 
    args2 對稱加密密碼
        -passout passwords
        其中passwords爲對稱加密(des、3des、aes)的密碼(使用這個參數就省去了console交互提示輸入密碼的環節) 
    args3 輸出文件
        -out file : 輸出證書私鑰文件 
[numbits]: 密鑰長度,理解爲私鑰長度 
  • 生成一個 2048 位的 RSA 私鑰,並用 des3 加密(密碼爲 123456),保存爲 server.key 文件:openssl genrsa -des3 -passout pass:123456 -out server.key 1024

1.1.2 req

  • req 的基本功能主要有兩個:生成證書請求和生成自簽名證書(固然這並非其所有功能,可是這兩個最爲常見)
  • 基本用法:openssl req [args] outfile
[args]
    args1 是輸入輸入文件格式:-inform arg
        -inform DER 使用輸入文件格式爲 DER
        -inform PEM 使用輸入文件格式爲 PEM
    args2 輸出文件格式:-outform arg   
        -outform DER 使用輸出文件格式爲 DER
        -outform PEM 使用輸出文件格式爲 PEM
    args3 是待處理文件 
        -in inputfilepath
    args4 待輸出文件
        -out outputfilepath
    args5 用於簽名待生成的請求證書的私鑰文件的解密密碼
        -passin passwords       
    args6 用於簽名待生成的請求證書的私鑰文件
        -key file
    args7 指定輸入密鑰的編碼格式 -keyform arg  
        -keyform DER
        -keyform NET
        -keyform PEM
    args8 生成新的證書請求 
        -new
    args9 輸出一個 X509 格式的證書,簽名證書時使用 
        -x509
    args10 使用 X509 簽名證書的有效時間  
        -days // -days 3650 有效期 10 年
    args11 生成一個 bits 長度的 RSA 私鑰文件,用於簽發,與-key互斥,生成證書請求或者自簽名證書時自動生成密鑰,而後生成的密鑰名稱由 -keyout 參數指定
        -newkey rsa:bits 
    args12 設置 HASH 算法-[digest],指定對建立請求時提供的申請者信息進行數字簽名時指定的 hash 算法
        -md5
        -sha1 // 高版本瀏覽器開始不信任這種算法
        -md2
        -mdc2
        -md4
    args13 指定 openssl 配置文件,不少內容不容易經過參數配置,能夠指定配置文件
        -config filepath   
    args14 顯示格式 txt(用於查看證書、私鑰信息)
        -text
  • 利用私鑰生成證書請求 CSR:openssl req -new -key server.key -out server.csr
  • 利用私鑰生成自簽名證書 CRT:openssl req -new -x509 -days 3650 -key ca.key -out ca.crt

1.1.3 x509

  • x509 能夠實現顯示證書的內容、轉換其格式、給 CSR 簽名等X.509證書的管理工做
  • 基本用法:openssl x509 [args]
[args]
    args1 是輸入輸入文件格式:-inform arg
        -inform DER 使用輸入文件格式爲 DER
        -inform PEM 使用輸入文件格式爲 PEM
    args2 輸出文件格式:-outform arg
        -outform DER 使用輸出文件格式爲 DER
        -outform PEM 使用輸出文件格式爲 PEM
    args3 是待處理 X509 證書文件
        -in inputfilepath
    args4 待輸出 X509 證書文件
        -out outputfilepath
    args5 代表輸入文件是一個「請求籤發證書文件(CSR)」,等待進行簽發
        -req
    args6 簽名證書的有效時間  
        -days // -days 3650 有效期 10 年
    args7 指定用於簽發請求證書的根 CA 證書
        -CA arg
    args8 根 CA 證書格式(默認是 PEM)
        -CAform arg
    args9 指定用於簽發請求證書的 CA 私鑰證書文件
        -CAkey arg
    args10 指定根 CA 私鑰證書文件格式(默認爲 PEM 格式)
        -CAkeyform arg
    args11 指定序列號文件(serial number file)
        -CAserial arg
    args12 若是序列號文件(serial number file)沒有指定,則自動建立它
        -CAcreateserial
    args12 設置 HASH 算法-[digest],指定對建立請求時提供的申請者信息進行數字簽名時指定的 hash 算法
        -md5
        -sha1 // 高版本瀏覽器開始不信任這種算法
        -md2
        -mdc2
        -md4
  • 使用根 CA 證書 ca.crt 和私鑰 ca.key 對「請求籤發證書」 server.csr 進行簽發,生成 x509 格式證書:openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out serverx509.crt

2. 具體使用

2.1 生成 RSA 祕鑰對

  • 使用 genrsa 生成 RSA 祕鑰對:openssl genrsa -out server.key 2048

2.2 生成身份證申請

  • 使用 req 命令,以以前的 server.key 爲輸入,生成一個身份證申請(CSR)文件:openssl req -nodes -new -key server.key -subj "/CN=localhost" -out server.csr
  • CSR 的公鑰從 server.key 中提取,域名是 localhost
  • 若是啓動 https 服務,使用這個 CSR 簽署的 CRT,客戶端必須訪問 localhost 才能訪問到這個 HTTPS 服務
  • 關於配置多個域名和 IP 的 CSR,後面會介紹

2.3 生成數字證書

  • 使用 x509 使用指定私鑰 server,key 簽署 server.csr,輸出數字證書( CRT):openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
  • 此處使用自身的私鑰簽署 CSR

2.4 HTTPS 驗證

  • 生成證書後,咱們能夠編寫一個 Golang 的 https 服務驗證剛剛生成的證書
  • 服務端代碼以下:
package main

import (
    "io"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
        io.WriteString(w, "hello, world!\n")
    })
    if e := http.ListenAndServeTLS("0.0.0.0:5200", "/home/ao/Documents/certs/review/server.crt",
        "/home/ao/Documents/certs/review/server.key", nil); e != nil {
        log.Fatal("ListenAndServe: ", e)
    }
    //if e := http.ListenAndServe("0.0.0.0:5200", nil); e != nil {
    //    log.Fatal("ListenAndServe: ", e)
    //}
}
View Code
  • 客戶端代碼以下:
package main

import (
    "crypto/tls"
    "crypto/x509"
    "io"
    "io/ioutil"
    "log"
    "net/http"
    "os"
)

func loadCA(caFile string) *x509.CertPool {
    pool := x509.NewCertPool()

    ca, err := ioutil.ReadFile(caFile)
    if err != nil {
        log.Fatal("ReadFile: ", err)
    }
    pool.AppendCertsFromPEM(ca)
    return pool
}

func main() {
    //c := &http.Client{
    //    Transport: &http.Transport{
    //        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    //    }}
    c := &http.Client{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{RootCAs: loadCA("/home/ao/Documents/certs/review/server.crt")},
        }}

    resp, err := c.Get("https://localhost:5200")
    if err != nil {
        log.Fatal("http.Client.Get: ", err)
    }

    defer resp.Body.Close()
    io.Copy(os.Stdout, resp.Body)
}
View Code
  • 啓動服務端:go run server.go
  • 啓動客戶端:go run client.go
$ go run client.go 
hello, world!
  • 改變客戶端請求域名爲 127.0.0.1 時,客戶端結果以下:
$ go run client.go
2019/09/30 15:11:41 http.Client.Get: Get https://127.0.0.1:5200: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs
exit status 1
  • 服務端輸出以下:
2019/09/30 15:11:41 http: TLS handshake error from 127.0.0.1:33596: remote error: tls: bad certificate
  • 若客戶端保持 127.0.0.1 不變,改變 http.Transport 的 TLSClientConfig 爲 insecure 配置時,能夠正常返回:
$ go run client.go
2019/09/30 15:11:41 http.Client.Get: Get https://127.0.0.1:5200: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs
exit status 1
  •  此時就像瀏覽器保持信任網站證書,繼續選擇瀏覽的動做是同樣的

2.5 配置域名爲 IP

  • 上述咱們使用 localhost,做爲 CSR 裏的域名,致使請求時必須使用域名
  • 若咱們使用本地 IP 做爲域名呢:openssl req -nodes -new -key server.key -subj "/CN=127.0.0.1" -out server.csr
  • 此時客戶端請求 https://127.0.0.1:5200 返回:
$ go run client.go
2019/09/30 15:19:24 http.Client.Get: Get https://127.0.0.1:5200: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs
exit status 1
  • 啓用 insecure 時正常返回
  • 此處說明,咱們不能簡單經過 -subj "/CN=[IP]" 實現 CSR 中包含域名
  • 從新生成 CSR 和 CRT
$ openssl genrsa -out server.key 2048
$ echo subjectAltName = IP:127.0.0.1 > extfile.cnf
$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -extfile extfile.cnf -out server.crt
  • 再次測試發現,請求 127.0.0.1 時能夠了

2.6 不使用自簽名證書

  • 上述咱們使用自簽名證書,下面咱們嘗試模擬一個 CA 簽署證書:
  • 首先生成 CA 的祕鑰和自簽名證書,中間不生成 CSR:
$ openssl genrsa -out ca.key 2048
$ openssl req -x509 -new -nodes -key ca.key -days 10000 -out ca.crt -subj "/CN=localhost.ca.com"
  • 生成私鑰、證書,並使用 CA 簽名:
$ openssl genrsa -out server.key 2048
$ openssl req -new -key server.key -subj "/CN=127.0.0.1" -out server.csr
$ openssl x509 -req -sha256 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -extfile extfile.cnf
  • 經測試,正確

2.7 CSR 配置多個域名和 IP

  • 上述提到 -subj 配置域名只能指定單個域名或者 IP
  • 下面介紹一下多域名 CSR 配置
  • 新增一個配置文件:
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req

[req_distinguished_name]
countryName = CN
countryName_default = CN
stateOrProvinceName = Beijing
stateOrProvinceName_default = Beijing
localityName = Beijing 
localityName_default = Beijing
organizationName = WangAo
organizationName_default = WangAo
organizationalUnitName = Dev 
organizationalUnitName_default = Dev 
commonName = test.openssl.com
commonName_default = test.openssl.com
commonName_max = 64

[v3_req]
basicConstraints = CA:TRUE
subjectAltName = @alt_names

[alt_names]
DNS.1 = test.openssl.com
IP.1 = 127.0.0.1
IP.2 = 10.0.2.15
  • 咱們爲服務制定了一個域名兩個 IP,地址
  • 下面利用以前的 CA,從新簽署數字證書:
$  openssl genrsa -out server.key 2048
$  openssl req -nodes -new -key server.key -out server.csr -subj "/CN=test.openssl.com"
$ openssl x509 -req -sha256 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -extensions v3_req -extfile openssl.cnf
  • CA 簽署數字證書時制定了 -extensions 和 -extfile
  • 分別向 127.0.0.一、10.0.2.15 和 test.openssl.com 請求,都可以成功

3. 小結 

  • 本篇內容介紹了 OpenSSL 基本使用和生成數字證書的方式以及驗證證書的程序編寫
  • 歡迎各位批評指正

3. 參考文獻

相關文章
相關標籤/搜索