golang实现openssl自签名双向认证

第一步:生成CA、服务端、客户端证书

1. 生成CA根证书
  • 生成CA证书私钥
bash 复制代码
openssl genrsa -out ca.key 4096
  • 创建ca.conf 文件
bash 复制代码
[ req ]
default_bits       = 4096
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = JiangSu
localityName                = Locality Name (eg, city)
localityName_default        = NanJing
organizationName            = Organization Name (eg, company)
organizationName_default    = Sheld
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = localhost
  • 生成根证书签发申请文件(csr文件)
bash 复制代码
openssl req \
  -new \
  -sha256 \
  -out ca.csr \
  -key ca.key \
  -config ca.conf
  • 生成自签发根证书(crt文件)
bash 复制代码
openssl x509 \
    -req \
    -days 3650 \
    -in ca.csr \
    -signkey ca.key \
    -out ca.pem
2. 生成服务端证书
  • 生成服务端私钥
bash 复制代码
openssl genrsa -out server.key 2048
  • 创建 server.conf 文件
bash 复制代码
[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name
req_extensions     = req_ext

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = JiangSu
localityName                = Locality Name (eg, city)
localityName_default        = NanJing
organizationName            = Organization Name (eg, company)
organizationName_default    = Sheld
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = localhost    # 此处尤为重要,需要用该服务名字填写到客户端的代码中

[ req_ext ]
subjectAltName = @alt_names

[alt_names]
DNS.1   = localhost
IP.1      = 127.0.0.1
  • 生成服务端签发申请文件(csr文件)
bash 复制代码
openssl req \
  -new \
  -sha256 \
  -out server.csr \
  -key server.key \
  -config server.conf
  • 使用CA证书签署服务器证书
bash 复制代码
openssl x509 \
  -req \
  -days 3650 \
  -CA ca.pem \
  -CAkey ca.key \
  -CAcreateserial \
  -in server.csr \
  -out server.pem \
  -extensions req_ext \
  -extfile server.conf
3. 生成客户端证书
  • 生成客户端私钥
bash 复制代码
openssl genrsa -out client.key 2048
  • 创建 client.conf 文件
bash 复制代码
[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = JiangSu
localityName                = Locality Name (eg, city)
localityName_default        = NanJing
organizationName            = Organization Name (eg, company)
organizationName_default    = Sheld
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = localhost
  • 生成客户端端签发申请文件(csr文件)
bash 复制代码
openssl req \
  -new \
  -sha256 \
  -out client.csr \
  -key client.key \
  -config client.conf
  • 使用CA证书签署客户端器证书
bash 复制代码
openssl x509 \
    -req \
    -days 3650 \
    -CA ca.pem \
    -CAkey ca.key \
    -CAcreateserial \
    -in client.csr \
    -out client.pem

第二步:golang 服务端、客户端代码编写

1. 项目目录结构
2. 服务端代码:server.go
go 复制代码
package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"net/http"
	"os"
)

type MyHandler struct {
}

func (h *MyHandler) ServeHTTP(w http.ResponseWriter,
	r *http.Request) {
	fmt.Fprint(w, "Hello, HTTPS!")
}

func main() {
	pool := x509.NewCertPool() // 创建证书池

	caCertPath := "./cert/ca.pem"
	caCrt, err := os.ReadFile(caCertPath) // 读取本地CA证书文件
	if err != nil {
		fmt.Println("ReadFile err:", err)
		return
	}

	pool.AppendCertsFromPEM(caCrt) // 将证书添加到证书池中

	s := &http.Server{ // 创建 HTTP服务器实例,并设置服务器的监听地址(本例中是127.0.0.1:8088)、处理器(即上面定义的myhandler结构体)、以及TLS配置。
		Addr:    "127.0.0.1:8088",
		Handler: &MyHandler{},
		TLSConfig: &tls.Config{
			ClientCAs:  pool,                           // 指定客户端需要验证的CA证书池(即上面创建的pool)
			ClientAuth: tls.RequireAndVerifyClientCert, // 要求客户端在发送请求时必须携带证书
		},
	}

	err = s.ListenAndServeTLS("./cert/server.pem", "./cert/server.key")
	if err != nil {
		fmt.Println("ListenAndServeTLS err:", err)
	}
}
3. 客户端代码:client.go
go 复制代码
package main

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

func main() {
	pool := x509.NewCertPool()    // 创建 x509.CertPool,用于存储CA证书
	caCertPath := "./cert/ca.pem" // 从文件中读取CA证书的内容
	caCrt, err := os.ReadFile(caCertPath)
	if err != nil {
		fmt.Println("ReadFile err:", err)
		return
	}
	pool.AppendCertsFromPEM(caCrt)                                               // 将CA证书添加到CertPool中
	cliCrt, err := tls.LoadX509KeyPair("./cert/client.pem", "./cert/client.key") //加载客户端证书和私钥
	if err != nil {
		fmt.Println("Loadx509keypair err:", err)
		return
	}
	tr := &http.Transport{ // 创建一个http.Transport,并配置TLS相关信息
		TLSClientConfig: &tls.Config{
			RootCAs:      pool,                      // 设置根证书池
			Certificates: []tls.Certificate{cliCrt}, // 设置客户端证书和私钥
		},
	}
	client := &http.Client{Transport: tr}             // 创建一个http.Client,并设置Transport为上面创建的Transport对象
	resp, err := client.Get("https://127.0.0.1:8088") // 使用创建的Client发送GET请求
	if err != nil {
		fmt.Println("Http Get error:", err)
		return
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body) // 读取并打印响应体的内容
	fmt.Println(string(body))
}

第三步:验证

1. 运行服务端
bash 复制代码
go run ./server/server.go
2. 运行客户端

运行客户端

bash 复制代码
go run ./client/client.go
相关推荐
萧鼎43 分钟前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸44 分钟前
【一些关于Python的信息和帮助】
开发语言·python
疯一样的码农44 分钟前
Python 继承、多态、封装、抽象
开发语言·python
^velpro^1 小时前
数据库连接池的创建
java·开发语言·数据库
秋の花1 小时前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端1 小时前
第六章 7.0 LinkList
java·开发语言·网络
可峰科技1 小时前
斗破QT编程入门系列之二:认识Qt:编写一个HelloWorld程序(四星斗师)
开发语言·qt
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
全栈开发圈1 小时前
新书速览|Java网络爬虫精解与实践
java·开发语言·爬虫
面试鸭1 小时前
离谱!买个人信息买到网安公司头上???
java·开发语言·职场和发展