跳转至

HTTPS单向认证和双向认证

HTTPS单向认证和双向认证


需要安装好 go 1.15 以下的版本

HTTP

直接监听在 8080 端口

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi, This is an example of http service in golang!")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

1663208073858-260d3031-426c-4662-a6f2-d900695ed82c.png

HTTPS忽略服务端证书校验

用 ListenAndServeTLS 来替换掉 ListenAndServe,需要指定服务端的证书和私钥

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi, This is an example of https service in golang!")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServeTLS(":8081", "server.crt", "server.key", nil)
}

这时候会提示风险,接受风险后正常访问

1663208368579-a5647385-94f5-467c-8db7-252c9a6e723c.png

1663208365971-e73b4daa-8922-457a-a766-1d4a25bbcf13.png

接下来使用 go 写一个客户端来进行连接

(github上面下载的证书过期了,重新生成一下

openssl genrsa -out server.key 2048 #生成私钥

openssl req -new -x509 -key server.key -out server.crt -days 365 #生成证书

1663210660579-59606175-3caa-4933-a006-e144381e34c4.png

)

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    resp, err := http.Get("https://localhost:8081")
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

请求的时候报了一个错误,意思是客户端认为证书不是由知名的CA签发的

1663210804156-ac42d997-67a7-4fda-9742-112db78411d5.png

修改客户端的代码,略过对证书的校验

package main

import (
    "crypto/tls"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    }
    client := &http.Client{Transport: tr}
    resp, err := client.Get("https://localhost:8081")

    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

HTTPS对服务端证书进行校验

多数时候需要对服务端证书进行校验,而不是像上面那样忽略这个校验,通过校验我们可以确认:1、服务端传来的证书是由某个特定CA签发的(如果是self-signed,也无妨)2、服务端传来的数字证书没有被中途篡改过

接下来建立一个属于自己的CA

#生成一个ca私钥
openssl genrsa -out ca.key 2048
#生成一个ca证书
openssl req -x509 -new -nodes -key ca.key -subj "/CN=localhost" -days 5000 -out ca.crt
#生成服务端私钥
openssl genrsa -out server.key 2048
#生成服务端证书请求
openssl req -new -key server.key -subj "/CN=localhost" -out server.csr 
#用我们的ca私钥签发server的数字证书
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 5000

此时先用客户端加载ca证书

package main

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

func main() {
    pool := x509.NewCertPool()
    caCertPath := "ca.crt"

    caCrt, err := ioutil.ReadFile(caCertPath)
    if err != nil {
        fmt.Println("ReadFile err:", err)
        return
    }
    pool.AppendCertsFromPEM(caCrt)

    tr := &http.Transport{
        TLSClientConfig: &tls.Config{RootCAs: pool},
    }
    client := &http.Client{Transport: tr}
    resp, err := client.Get("https://localhost:8081")
    if err != nil {
        fmt.Println("Get error:", err)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

HTTPS双向校验

服务端对客户端的证书进行校验

#生成客户端私钥
openssl genrsa -out client.key 2048
#生成客户端证书请求
openssl req -new -key client.key -subj "/CN=localhost_cn" -out client.csr
#使用ca生成对客户端证书
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 5000

package main

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

type myhandler struct {
}

func (h *myhandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi, This is an example of http service in golang!\n")
}

func main() {
    pool := x509.NewCertPool()
    caCertPath := "ca.crt"

    caCrt, err := ioutil.ReadFile(caCertPath)
    if err != nil {
        fmt.Println("ReadFile err:", err)
        return
    }
    pool.AppendCertsFromPEM(caCrt)

    s := &http.Server{
        Addr:    ":8081",
        Handler: &myhandler{},
        TLSConfig: &tls.Config{
            ClientCAs:  pool,
            ClientAuth: tls.RequireAndVerifyClientCert,
        },
    }

    err = s.ListenAndServeTLS("server.crt", "server.key")
    if err != nil {
        fmt.Println("ListenAndServeTLS err:", err)
    }
}
package main

import (

    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    pool := x509.NewCertPool()
    caCertPath := "ca.crt"

    caCrt, err := ioutil.ReadFile(caCertPath)
    if err != nil {
        fmt.Println("ReadFile err:", err)
        return
    }
    pool.AppendCertsFromPEM(caCrt)

    cliCrt, err := tls.LoadX509KeyPair("client.crt", "client.key")
    if err != nil {
        fmt.Println("Loadx509keypair err:", err)
        return
    }

    tr := &http.Transport{
        TLSClientConfig: &tls.Config{
            RootCAs:      pool,
            Certificates: []tls.Certificate{cliCrt},
        },
    }
    client := &http.Client{Transport: tr}
    resp, err := client.Get("https://localhost:8081")
    if err != nil {
        fmt.Println("Get error:", err)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

通是通了,但是抓包看不到区别

参考:

experiments/gohttps at master · bigwhite/experiments

Go和HTTPS--1 - 腾讯云开发者社区-腾讯云

Go和HTTPS -2 - 腾讯云开发者社区-腾讯云

原文: https://www.yuque.com/hxfqg9/misc/ct4pg1