想知道什么是 mTLS(双向 TLS)?来吧,让我们用 Golang 和 OpenSSL 实现一个 mTLS。

介绍

TLS(安全传输层协议简称)为网络通信的应用程序提供必要的加密。HTTPS (超文本安全协议) 是 HTTP 的一种扩展,利用 TLS 实现安全性。TLS 技术要求 CA (证书颁发机构) 向服务颁发 X.509 数字证书,然后将该数字证书移交给服务的消费者,由其使用 CA 本身进行验证。mTLS 将同样的思想扩展到应用程序中,例如,在微服务中,提供者和消费者都需要向对方生成自己的证书。这些证书由双方使用各自的 CA 进行验证。一旦经过验证,服务器/客户端或提供者/使用者之间的通信就会安全地进行。

实现

第一步 - 构建一个简单的 HTTP 服务端和客户端

让我们先 在server.go 里创建一个简单的 HTTP 服务,当访问 8080 端口,请求 /hello 资源时回复 Hello, world!

package mainimport (
"io"
"log"
"net/http"
)func helloHandler(w http.ResponseWriter, r *http.Request) {
// Write "Hello, world!" to the response body
io.WriteString(w, "Hello, world!\n")
}func main() {
// Set up a /hello resource handler
http.HandleFunc("/hello", helloHandler)// Listen to port 8080 and wait
log.Fatal(http.ListenAndServe(":8080", nil))
}

客户端通过 8080 端口请求 /hello 资源,然后将响应体通过 stdout 打印。下面是 client.go 代码:

package mainimport (
"fmt"
"io/ioutil"
"log"
"net/http"
)func main() {
// Request /hello over port 8080 via the GET method
r, err := http.Get("http://localhost:8080/hello")
if err != nil {
log.Fatal(err)
}// Read the response body
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}// Print the response body to stdout
fmt.Printf("%s\n", body)
}

打开一个终端并运行服务端:

go run -v server.go

打开另外一个终端运行客户端:

go run -v client.go

你可以从客户端看到以下输出:

Hello, world!

第二步 - 生成和使用服务端证书

使用以下命令生成证书。该命令将创建一个有效期为 10 年的 2048 位密钥证书。此外,CN=localhost 说明该证书对 localhost 域是有效的。

openssl req -newkey rsa:2048 \-new -nodes -x509 \-days 3650 \-out cert.pem \-keyout key.pem \-subj "/C=US/ST=California/L=Mountain View/O=Your Organization/OU=Your Unit/CN=localhost"

你应该目录里面有 cert.pemkey.pem 证书了。

现在让我们启用服务器 HTTP 的 TLS,也就是 HTTPS。server.go 里面的 http.ListenAndServe(":8080", nil) 调用替换成 http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil) ,通过 8443 端口监听链接。同时提供前面生成的证书。

-// Listen to port 8080 and wait
-log.Fatal(http.ListenAndServe(":8080", nil))
+// Listen to HTTPS connections on port 8443 and wait
+log.Fatal(http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil))

你可以通过运行并在浏览器输入 https://localhost:8443/hello 来验证服务器是否工作。

现在我们更新 client.go 代码来通过 HTTPS 连接服务端。

-// Request /hello over port 8080 via the GET method
-r, err := http.Get("http://localhost:8080/hello")
+// Request /hello over HTTPS port 8443 via the GET method
+r, err := http.Get("https://localhost:8443/hello")

由于我们客户端还不知道证书,直接运行服务器会显示下面的错误:

http: TLS handshake error from [::1]:59436: remote error: tls: bad certificate

在客户端,你需要注意以下几点:

x509: certificate is not valid for any names, but wanted to match localhost

第三步- 向客户端提供证书

更新 client.go 代码读取之前生成的证书,代码如下:

-// Request /hello over HTTPS port 8443 via the GET method
-r, err := http.Get("https://localhost:8443/hello")+// Create a CA certificate pool and add cert.pem to it
+caCert, err := ioutil.ReadFile("cert.pem")
+if err != nil {
+log.Fatal(err)
+}
+caCertPool := x509.NewCertPool()
+caCertPool.AppendCertsFromPEM(caCert)
+
+// Create a HTTPS client and supply the created CA pool
+client := &http.Client{
+Transport: &http.Transport{
+TLSClientConfig: &tls.Config{
+RootCAs: caCertPool,
+},
+},
+}
+
+// Request /hello via the created HTTPS client over port 8443 via GET
+r, err := client.Get("https://localhost:8443/hello")

这里,我们读取 cert.pem 文件并在创建客户端时提供根 CA 。运行客户端现在应该可以成功显示以下内容:

Hello, world!

最后一步 - 启用 mTLS

在客户端,读取并提供密钥对作为客户端证书。

+// Read the key pair to create certificate
+cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
+if err != nil {
+log.Fatal(err)
+}...-// Create a HTTPS client and supply the created CA pool
+// Create a HTTPS client and supply the created CA pool and certificate
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
+Certificates: []tls.Certificate{cert},
},
},
}

在服务端,我们创建一个类似于 CA 池 ,并将其提供给 TLS 配置,来作为验证客户端证书的权威。我们还对服务器证书使用相同的密钥对。

-// Listen to HTTPS connections on port 8443 and wait
-log.Fatal(http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil))+// Create a CA certificate pool and add cert.pem to it
+caCert, err := ioutil.ReadFile("cert.pem")
+if err != nil {
+log.Fatal(err)
+}
+caCertPool := x509.NewCertPool()
+caCertPool.AppendCertsFromPEM(caCert)
+
+// Create the TLS Config with the CA pool and enable Client certificate validation
+tlsConfig := &tls.Config{
+ClientCAs: caCertPool,
+ClientAuth: tls.RequireAndVerifyClientCert,
+}
+tlsConfig.BuildNameToCertificate()
+
+// Create a Server instance to listen on port 8443 with the TLS config
+server := &http.Server{
+Addr:      ":8443",
+TLSConfig: tlsConfig,
+}
+
+// Listen to HTTPS connections with the server certificate and wait
+log.Fatal(server.ListenAndServeTLS("cert.pem", "key.pem"))

先 运行 server.go 然后运行 client.go,然后你可以在客户端上看到如下一条成功的消息:

Hello, world!

完整代码 最终, server.go 代码如下:

package mainimport (
"crypto/tls"
"crypto/x509"
"io"
"io/ioutil"
"log"
"net/http"
)func helloHandler(w http.ResponseWriter, r *http.Request) {
// Write "Hello, world!" to the response body
io.WriteString(w, "Hello, world!\n")
}func main() {
// Set up a /hello resource handler
http.HandleFunc("/hello", helloHandler)// Create a CA certificate pool and add cert.pem to it
caCert, err := ioutil.ReadFile("cert.pem")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)// Create the TLS Config with the CA pool and enable Client certificate validation
tlsConfig := &tls.Config{
ClientCAs: caCertPool,
ClientAuth: tls.RequireAndVerifyClientCert,
}
tlsConfig.BuildNameToCertificate()// Create a Server instance to listen on port 8443 with the TLS config
server := &http.Server{
Addr:      ":8443",
TLSConfig: tlsConfig,
}// Listen to HTTPS connections with the server certificate and wait
log.Fatal(server.ListenAndServeTLS("cert.pem", "key.pem"))
}

client.go 代码如下:

package mainimport (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"log"
"net/http"
)func main() {
// Read the key pair to create certificate
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
if err != nil {
log.Fatal(err)
}// Create a CA certificate pool and add cert.pem to it
caCert, err := ioutil.ReadFile("cert.pem")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)// Create a HTTPS client and supply the created CA pool and certificate
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
Certificates: []tls.Certificate{cert},
},
},
}// Request /hello via the created HTTPS client over port 8443 via GET
r, err := client.Get("https://localhost:8443/hello")
if err != nil {
log.Fatal(err)
}// Read the response body
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}// Print the response body to stdout
fmt.Printf("%s\n", body)
}

结论

Golang 让实现 mTLS 变得非常容易,而且不到 100 行代码。

我很想听听你的意见。

原文信息

原文地址:https://venilnoronha.io/a-step-by-step-guide-to-mtls-in-go

原文作者:Venil Noronha

本文永久链接:https://github.com/gocn/translator/blob/master/2021/w44_A_step_by_step_guide_to_mTLS_in_Go.md

译者:朱亚光

校对:

想要了解关于 Go 的更多资讯,还可以通过扫描的方式,进群一起探讨哦~

『每周译Go』手把手教你用 Go 实现一个 mTLS相关推荐

  1. 『每周译Go』开启并发模式

    在这篇文章中,我将介绍在 Go 中使用基本并发模式和原生原语来构建并发应用程序的一些最佳实践.模式本身适用于任何语言,但对于这些示例,我们将使用 Go. 可以下载本文的源码配合阅读. git clon ...

  2. 『每周译Go』Go 语言的 goroutine 性能分析

    本文档最后一次更新时所用的 Go 版本是 1.15.6,但是大多数情况下,新老版本都适用. 描述 Go 运行时在一个称为 allgs 简单切片追踪所有的 goroutines.这里面包含了活跃的和死亡 ...

  3. 『每周译Go』Go sync map 的内部实现

    目录 引言 a. 简单介绍并发性及其在此上下文中的应用 sync.RWMutex 和 map 一起使用的问题 介绍 sync.Map a. 在哪些场景使用 sync.Map? sync.Map 实现细 ...

  4. 『每周译Go』那些年我使用Go语言犯的错

    原文地址:https://henvic.dev/posts/my-go-mistakes/ 原文作者:Henrique Vicente 本文永久链接:https://github.com/gocn/t ...

  5. 『每周译Go』Go 语言中的插件

    很多年以前我就开始写一系列关于插件的文章:介绍这些插件在不同的系统和编程语言下是如何设计和实现的.今天这篇文章,我打算把这个系列扩展下,讲讲 Go 语言中一些插件的例子. 需要提醒的是,本系列头几篇的 ...

  6. 『每周译Go』写了 50 万行 Go 代码后,我明白这些道理

    原文地址:https://blog.khanacademy.org/half-a-million-lines-of-go/ 原文作者:Kevin Dangoor 本文永久链接:https://gith ...

  7. 『每周译Go』GitHub 为 Go 社区带来供应链安全功能

    Go 国际社区从一开始就拥抱 GitHub ( GitHub 即是 Go 代码协作的地方也是发布包的地方) 使得 Go 成为 如今 GitHub 上排名前 15 的编程语言.我们很高兴地宣布 GitH ...

  8. 『每周译Go』Uber 的 API 网关架构

    原文地址:https://eng.uber.com/architecture-api-gateway/ 原文作者:Madan Thangavelu, Abhishek Parwal, Rohit Pa ...

  9. 『每周译Go』Google:12 条 Golang 最佳实践

    这是直接总结好的 12 条,详细的再继续往下看: 先处理错误避免嵌套 尽量避免重复 先写最重要的代码 给代码写文档注释 命名尽可能简洁 使用多文件包 使用 go get 可获取你的包 了解自己的需求 ...

最新文章

  1. cannot find package “github.com/json-iterator/go“cannot find package “github.com/modern-go/reflect2“
  2. matlab的exec程序,C++调用Matlab画图的一段程序
  3. python 文本相似度_【机器学习】使用gensim 的 doc2vec 实现文本相似度检测
  4. Classpath entry org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER will not be exported or published. Runtim
  5. .Net----Remoting 激活 激活方式
  6. Linux下支持的视频文件格式,linux下视频格式转换与视频合并
  7. 阿里巴巴Java开发手册(泰山版)
  8. 计算机软硬件逻辑等价性是指,南航计算机组成原理复习ppt.ppt
  9. 拉格朗日乘数法,一种计算条件极值的方式
  10. Win10 如何将40G大文件极致压缩
  11. 谨防Wi-Fi时代的***屠城
  12. Jenkins部署到远程服务器
  13. JavaFX开发教程——快速入门FX
  14. 在Element UI中表格根据数据动态变化显示表格的内容
  15. 盘点国内6大抗DDOS攻击服务商
  16. Vue缓存路由(keep-alive)以及新的生命周期
  17. Gabor滤波器为什么能实现频率域加窗
  18. MTK平台修改开机动画,开机logo
  19. HTTP协议演进与各版本特性
  20. 不知道化什么妆?AI美妆助手上线!为你提供最合适的建议

热门文章

  1. 电影分区发行新模式创造“中国电影市场的新增量”
  2. uni-app监听窗口尺寸变化事件和隐藏键盘
  3. 【预测模型】基于Logistic混沌映射改进麻雀算法改进BP神经网络实现数据预测matlab源码
  4. α测试测试与β测试的区别
  5. linux 时间与bios,Linux操作系统时间与BIOS硬件时间
  6. S5PV210 android4.0 在Sate210的上的调试情况报告
  7. MTKCam3流程学习 - openCamera
  8. 如何在hosts文件添加自己想要解析的网站?及修改hosts的作用
  9. SQL面试题:经典排名问题(相同分数是否要并列,排名是否有间隔)
  10. 如何模拟苹果官网高端大气上档次的产品视频演示