生成证书

:::info // 生成根证书
openssl genrsa -out ca.key 2048
openssl req -sha256 -new -out ca.csr -key ca.key
openssl x509 -req -sha256 -in ca.csr -out ca.crt -signkey ca.key -CAcreateserial -days 3650

// 生成服务端证书
openssl genrsa -out server.key 2048
openssl req -sha256 -new -out server.csr -key server.key
openssl x509 -req -sha256 -in server.csr -out server.crt -signkey server.key -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650

// 生成客户端证书
openssl genrsa -out client.key 2048
openssl req -sha256 -new -out client.csr -key client.key
openssl x509 -req -sha256 -in client.csr -out client.crt -signkey client.key -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12

:::

服务端

  1. package main
  2. import (
  3. "crypto/tls"
  4. "crypto/x509"
  5. "github.com/gin-gonic/gin"
  6. "io/ioutil"
  7. "net/http"
  8. "time"
  9. )
  10. func main() {
  11. router := gin.Default()
  12. caCertData, err := ioutil.ReadFile("./ca.crt")
  13. if err != nil {
  14. panic(err)
  15. }
  16. rootCAs := x509.NewCertPool()
  17. rootCAs.AppendCertsFromPEM(caCertData)
  18. s := &http.Server{
  19. Addr: ":443",
  20. Handler: router,
  21. ReadTimeout: 10 * time.Second,
  22. WriteTimeout: 10 * time.Second,
  23. MaxHeaderBytes: 1 << 20,
  24. TLSConfig: &tls.Config{
  25. // 客户端需要提供证书
  26. ClientAuth: tls.RequireAndVerifyClientCert,
  27. // 用于验证客户端证书
  28. ClientCAs: rootCAs,
  29. },
  30. }
  31. router.GET("ping", func(context *gin.Context) {
  32. context.String(200, "%s", "pong")
  33. })
  34. //s.ListenAndServe()
  35. s.ListenAndServeTLS("./server.crt", "./server.key")
  36. }

客户端(失败)

  1. package main
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "strconv"
  8. )
  9. func main() {
  10. client := &http.Client{Transport: &http.Transport{
  11. TLSClientConfig: &tls.Config{
  12. InsecureSkipVerify: true,
  13. },
  14. }}
  15. rsp, err := client.Get("https://127.0.0.1:443/ping")
  16. if err != nil {
  17. panic(err)
  18. }
  19. defer rsp.Body.Close()
  20. length, err := strconv.Atoi(rsp.Header.Get("Content-Length"))
  21. if err != nil {
  22. panic(err)
  23. }
  24. var data = make([]byte, length)
  25. n, err := rsp.Body.Read(data)
  26. if err != nil && err != io.EOF {
  27. panic(err)
  28. }
  29. fmt.Println(string(data[:n]))
  30. }
  31. mutual-auth go run client-2.go
  32. panic: Get "https://127.0.0.1:443/ping": remote error: tls: bad certificate

成功

  1. package main
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. )
  8. func main() {
  9. cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
  10. if err != nil {
  11. panic(err)
  12. }
  13. tr := &http.Transport{
  14. TLSClientConfig: &tls.Config{
  15. // 因为用的是自签名证书,所以要跳过验证
  16. InsecureSkipVerify: true,
  17. Certificates: []tls.Certificate{cert},
  18. },
  19. }
  20. client := &http.Client{Transport: tr}
  21. rsp, err := client.Get("https://127.0.0.1:443/ping")
  22. if err != nil {
  23. panic(err)
  24. }
  25. defer rsp.Body.Close()
  26. var data = make([]byte, 1024)
  27. n, err := rsp.Body.Read(data)
  28. if err != nil && err != io.EOF {
  29. panic(err)
  30. }
  31. fmt.Println(string(data[:n]))
  32. }
  33. mutual-auth go run client-1.go
  34. pong