1. 程式人生 > 實用技巧 >go學習筆記 http2.0使用【SAN 和雙向認證】

go學習筆記 http2.0使用【SAN 和雙向認證】

簡單說一下我的環境 win7+go1.15.6,GO1.15 X509 不能用了,需要用到SAN證書,

證書

需要用到SAN證書,下面就介紹一下SAN證書生成。首先需要下載 OpenSSLhttp://slproweb.com/products/Win32OpenSSL.html

第1步:生成 CA 根證書

openssl genrsa -out ca.key 2048

openssl req -new -x509 -days 3650 -key ca.key -out ca.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:cn
State or Province Name (full name) [Some-State]:shanghai
Locality Name (eg, city) []:shanghai
Organization Name (eg, company) [Internet Widgits Pty Ltd]:custer
Organizational Unit Name (eg, section) []:custer
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:

第2步:用 openssl 生成 ca 和雙方 SAN 證書。

準備預設 OpenSSL 配置檔案於當前目錄

linux系統在 :/etc/pki/tls/openssl.cnf

Mac系統在:/System/Library/OpenSSL/openssl.cnf

Windows:安裝目錄下openssl.cfg 比如D:\Program Files\OpenSSL-Win64\bin\openssl.cfg

1:拷貝配置檔案到專案 然後修改

2:找到 [ CA_default ],開啟 copy_extensions = copy

3:找到[ req ],開啟 req_extensions = v3_req # The extensions to add to a certificate request

4:找到[ v3_req ],新增 subjectAltName = @alt_names

5:新增新的標籤 [ alt_names ] , 和標籤欄位

[ alt_names ]
DNS.1 = localhost
DNS.2 = *.custer.fun

這裡填入需要加入到 Subject Alternative Names 段落中的域名名稱,可以寫入多個。

第3步:生成服務端證書

openssl genpkey -algorithm RSA -out server.key
 
openssl req -new -nodes -key server.key -out server.csr -days 3650 -subj "/C=cn/OU=custer/O=custer/CN=localhost" -config ./openssl.cfg -extensions v3_req
 
openssl x509 -req -days 3650 -in server.csr -out server.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cfg -extensions v3_req

server.csr是上面生成的證書請求檔案。ca.pem/ca.key是CA證書檔案和key,用來對server.csr進行簽名認證。這兩個檔案在之前生成的。

第4步:生成客戶端證書

openssl genpkey -algorithm RSA -out client.key
 
openssl req -new -nodes -key client.key -out client.csr -days 3650 -subj "/C=cn/OU=custer/O=custer/CN=localhost" -config ./openssl.cfg -extensions v3_req
 
openssl x509 -req -days 3650 -in client.csr -out client.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cfg -extensions v3_req

現在 Go 1.15 以上版本的 GRPC 通訊,這樣就完成了使用自籤CA、Server、Client證書和雙向認證

Http2.0

Go的標準庫HTTP伺服器預設支援HTTP/2,支援標準庫的標準HTTP的全雙工通訊,雙向認證,即:伺服器認證客戶端,客戶端也認證伺服器。額外增加了服務端對客戶端的認證(紅色部分)。

我demo 喜歡把Server和Client放在一起, 程式碼如下:

package main
 
import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "time"
 
    "golang.org/x/net/http2"
)
 
func main() {
    go HttpServer()
    time.Sleep(1000)
    go HttpClient()
    var s string
    fmt.Scan(&s)
 
}
 
func HttpServer() {
    /*簡單方式*/
    /*
        server := &http.Server{
            Addr:         ":8080",
            ReadTimeout:  5 * time.Minute, // 5 min to allow for delays when 'curl' on OSx prompts for username/password
            WriteTimeout: 10 * time.Second,
            TLSConfig:    &tls.Config{ServerName: "localhost"},
        }
    */
    /*高階方式 使用ca.pem*/
    server := &http.Server{
        Addr:         ":8080",
        ReadTimeout:  5 * time.Minute, // 5 min to allow for delays when 'curl' on OSx prompts for username/password
        WriteTimeout: 10 * time.Second,
        TLSConfig:    getTLSConfig("localhost", "ca.pem", tls.ClientAuthType(tls.RequireAndVerifyClientCert)),
    }
    http.HandleFunc("/", handle)
    if err := server.ListenAndServeTLS("server.pem", "server.key"); err != nil {
        log.Fatal(err)
    }
}
func handle(w http.ResponseWriter, r *http.Request) {
    // Log the request protocol
    fmt.Printf("Server Got connection: %s\r\n", r.Proto)
    w.Write([]byte("Hello"))
}
func HttpClient() {
    clientCertFile := "client.pem"
    clientKeyFile := "client.key"
    caCertFile := "ca.pem"
    var cert tls.Certificate
    var err error
    if clientCertFile != "" && clientKeyFile != "" {
        cert, err = tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
        if err != nil {
            log.Fatalf("Error creating x509 keypair from client cert file %s and client key file %s", clientCertFile, clientKeyFile)
        }
    }
    caCert, err := ioutil.ReadFile(caCertFile)
    if err != nil {
        fmt.Printf("Error opening cert file %s, Error: %s", caCertFile, err)
    }
    caCertPool := x509.NewCertPool()
    caCertPool.AppendCertsFromPEM(caCert)
    /*http 1.1
    t := &http.Transport{
        TLSClientConfig: &tls.Config{
            Certificates: []tls.Certificate{cert},
            RootCAs:      caCertPool,
        },
    }
    */
    t := &http2.Transport{
        TLSClientConfig: &tls.Config{
            Certificates: []tls.Certificate{cert},
            RootCAs:      caCertPool,
        },
    }
 
    client := http.Client{Transport: t, Timeout: 15 * time.Second}
    resp, err := client.Get("https://localhost:8080/")
    if err != nil {
        fmt.Printf("Failed get: %s\r\n", err)
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("Failed reading response body: %s\r\n", err)
    }
    fmt.Printf("Client Got response %d: %s %s\r\n", resp.StatusCode, resp.Proto, string(body))
}
 
func getTLSConfig(host, caCertFile string, certOpt tls.ClientAuthType) *tls.Config {
    var caCert []byte
    var err error
    var caCertPool *x509.CertPool
    if certOpt > tls.RequestClientCert {
        caCert, err = ioutil.ReadFile(caCertFile)
        if err != nil {
            fmt.Printf("Error opening cert file %s error: %v", caCertFile, err)
        }
        caCertPool = x509.NewCertPool()
        caCertPool.AppendCertsFromPEM(caCert)
    }
 
    return &tls.Config{
        ServerName: host,
        ClientAuth: certOpt,
        ClientCAs:  caCertPool,
        MinVersion: tls.VersionTLS12, // TLS versions below 1.2 are considered insecure - see https://www.rfc-editor.org/rfc/rfc7525.txt for details
    }
}

執行結果如下:

D:\GoProject\src\main>go run main.go
Server Got connection: HTTP/2.0
Client Got response 200: HTTP/2.0 Hello