go-https的简单实现

2022-07-22 17:06:44 浏览数 (2)

HTTPS的一种简单实现

生成密钥、证书

第一步,为服务器端和客户端准备公钥、私钥

生成服务器端私钥

代码语言:txt复制
openssl genrsa -out server.key 1024

生成服务器端公钥

代码语言:txt复制
openssl rsa -in server.key -pubout -out server.pem

生成客户端私钥

代码语言:txt复制
openssl genrsa -out client.key 1024

生成客户端公钥

代码语言:txt复制
openssl rsa -in client.key -pubout -out client.pem

第二步,生成 CA 证书

生成 CA 私钥

代码语言:txt复制
openssl genrsa -out ca.key 1024

X.509 Certificate Signing Request (CSR) Management.

代码语言:txt复制
openssl req -new -key ca.key -out ca.csr

X.509 Certificate Data Management.

代码语言:txt复制
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt

在执行第二步时

Common Name (e.g. server FQDN or YOUR name) []: 这一项,是最后可以访问的域名,为了方便测试,写成 test.com

如果想通过IP直接访问而不想通过域名,在创建服务端证书时:用如下语句:

代码语言:txt复制
openssl genrsa -out server.key 2048
 
openssl req -new -key server.key -subj "/CN=192.168.1.10" -out server.csr
 
echo subjectAltName = IP:192.168.1.10 > extfile.cnf
 
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -extfile extfile.cnf -out server.crt -days 5000

第三步,生成服务器端证书和客户端证书

服务器端需要向 CA 机构申请签名证书,在申请签名证书之前依然是创建自己的 CSR 文件

代码语言:txt复制
openssl req -new -key server.key -out server.csr

向自己的 CA 机构申请证书,签名过程需要 CA 的证书和私钥参与,最终颁发一个带有 CA 签名的证书

代码语言:txt复制
openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt

client 端

代码语言:txt复制
openssl req -new -key client.key -out client.csr

client 端到 CA 签名

代码语言:txt复制
openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in client.csr -out client.crt

第四步,客户端机器添加test.com域名

代码语言:txt复制
sudo vim /etc/hosts

添加服务端的ip信息对应的域名:192.168.1.100 test.com

源码实现

服务端代码

代码语言:javascript复制
package main

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

type myhandler struct {
}

func (h *myhandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    w.Write([]byte("This is an example servern"))
}

func getHandler(w http.ResponseWriter, r *http.Request)  {
    fmt.Fprintf(w, "hello, this is https get ")
}

func postHandler(w http.ResponseWriter, r *http.Request)  {
    body, _ := ioutil.ReadAll(r.Body)
    r.Body.Close()
    body_str := string(body)
    fmt.Println(body_str)
    //ret, _ := json.Marshal(user)
    ret := "{"code":200}"
    fmt.Fprint(w, string(ret))
}

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)
    
    server := http.NewServeMux()
    server.HandleFunc("/get",getHandler)
    server.HandleFunc("/post",postHandler)
    
    s := &http.Server{
        Addr:    ":8088",
        //Handler: &myhandler{},
        Handler: server,
        TLSConfig: &tls.Config{
            ClientCAs:  pool,
            ClientAuth: tls.RequireAndVerifyClientCert,
        },  
    }
    
    log.Printf("About to listen on 10443.Go to https://127.0.0.1:8088")
    err = s.ListenAndServeTLS("server.crt", "server.key")
    if err != nil {
        log.Fatal(err)
    }   
}

客户端代码

代码语言:javascript复制
package main

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

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},
            InsecureSkipVerify: true,//客户端关闭对服务端的验证
        },
    }
    client := &http.Client{Transport: tr}
    jsonStr := "{"name":"wang","age":29}"
    req := bytes.NewBuffer([]byte(jsonStr))
    body_type := "application/json;charset=utf-8"
    resp, err := client.Post("https://test.com:8088/post",body_type,req)
    if err != nil {
        fmt.Println("Get error:", err)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

0 人点赞