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