https 原理分析进阶-模拟https通信过程

https,原理,分析,进阶,模拟,通信,过程 · 浏览次数 : 537

小编点评

生成证书时需要带简单的排版,例如以下内容: ``` server_name = lanpangziclient.com; server_addr = :443; server_certificate = ca_cert.crt; server_private_key = ca_private.key; server_tls_config = &tls.Config{ ClientAuth: tls.RequireAndVerifyClientCert, }; ``` 服务端和客户端代码请求时需要带上自己的证书,例如以下内容: ``` client_cert = client_csr.crt; client_key = client_private.key; client_tls_config = &tls.Config{ Certificates: []tls.Certificate{client_cert}, }; ```

正文

大家好,我是蓝胖子,之前出过一篇https的原理分析 ,完整的介绍了https概念以及通信过程,今天我们就来比较完整的模拟实现https通信的过程,通过这篇文章,你能了解到https核心的概念以及原理,https证书是如何申请的,以及如何用golang实现https通信,https双向认证。

本章代码已经上传到github

https://github.com/HobbyBear/codelearning/tree/master/httpsdemo

https原理回顾

在开始之前,让我们来了解下https相关的核心知识,可以作为上篇https原理分析的补充。学习一个东西一定要先知道为什么要用它,我总结了两点:

1,https 第一个好处是使原本的http明文传输变成了密文传输,增加了安全性。

2,https第二好处是采用数字证书来解决了身份认证问题,起码对端通信是经过ca认证的。

那么https又是通过什么技术来实现上述两点的呢?

数字证书原理

我先聊聊数字证书的实现原理,在https的握手阶段,服务端会发送自身的证书给客户端,客户端会去验证这个证书的有效性,有效性是这样保证的:

数字证书上会写明证书的签名算法和证书的签名,如下图所示
image.png

证书经过签名算法中指定的SHA-256算法将证书内容进行hash得到消息摘要,然后再将这个摘要值经过RSA算法用证书颁发机构的私钥进行加密就得到了证书的签名。

而客户端拿到这个证书就会用证书颁发机构的公钥去解密签名,然后按SHA-256算法也对证书内容进行hash,也得到一个消息摘要值,客户端就去比对自己计算的消息摘要和公钥解密签名得到的消息摘要是否一致,一致则说明证书未被篡改并且是证书颁发机构颁发的。

有同学可能会疑惑,证书颁发机构的公钥是从哪里获取的,证书颁发机构的公钥就在颁发机构其自身的证书里,如下图所示。

image.png

https密文加密原理

知道了数字证书的验证原理,我们来看看https通信中涉及到的加密过程,在https的握手阶段,服务端会选择一个与客户端都支持的密钥套件用于后续的加密,密钥套件一般会有如下组件:

  1. 密钥交换算法:用于在客户端和服务器之间安全地交换加密密钥。常见的密钥交换算法有RSA和Diffie-Hellman等。

  2. 对称加密算法:用于对通信数据进行加密和解密。常见的对称加密算法有AES、DES和3DES等。

  3. 摘要算法:用于生成和验证消息的完整性。常见的摘要算法有MD5和SHA-256等。

https采用非对称加密的方式交换密钥,然后使用对称加密的方式对数据进行加密,并且对消息的内容采用摘要算法得到消息摘要,这样对端在解密数据后可以通过相同的消息摘要算法对计算后的消息摘要和传过来的消息摘要进行对比,从而判断数据是否经过篡改。

具体步骤如下:

  1. 客户端向服务器发送一个初始的握手请求,该请求中包含了客户端支持的密码套件列表。
  2. 服务器收到握手请求后,会从客户端提供的密码套件列表中选择一个与自己支持的密码套件相匹配的套件。
  3. 服务器将选定的密码套件信息返回给客户端。
  4. 客户端收到服务器返回的密码套件信息后,会选择一个与服务器相匹配的密码套件。
  5. 客户端生成一个随机的对称加密密钥,并使用服务器的公钥对该密钥进行加密。
  6. 客户端将加密后的对称加密密钥发送给服务器。
  7. 服务器使用自己的私钥对接收到的加密的对称加密密钥进行解密。
  8. 客户端和服务器现在都拥有了相同的对称加密密钥,可以使用该密钥进行加密和解密通信数据。
  9. 客户端和服务器使用对称加密密钥对通信数据进行加密和解密,并使用摘要算法对数据进行完整性验证。

通过以上步骤,客户端和服务器可以建立一个安全的HTTPS连接,并使用密码套件来保护通信的安全性。

模拟证书颁发

接下来,我们就要开始实现下https的通信了,由于只是实验,我们不会真正的去为我的服务器去申请一个数字证书,所以我们暂时在本地用openssl来模拟下证书颁发的逻辑。

模拟根认证ca机构

我们知道证书颁发的机构是ca,而ca根证书是默认信任的,一般内置在浏览器和操作系统里,所以首先来生成一个根证书,并且让系统默认信任它。

先生成ca的私钥

openssl genpkey -algorithm RSA -out ca_private.key 

然后生成ca的证书请求

openssl req -new -key ca_private.key -out ca_csr.csr

生成ca证书

openssl x509 -req -in ca_csr.csr -signkey ca_private.key -out ca_cert.crt

我用的是mac系统,所以我这里演示下mac系统如何添加证书信任,

打开钥匙串应用-> 将证书拖进登录那一栏 -> 右击证书点击显示简介-> 将信任那一栏改为始终信任

image.png

模拟ca机构向服务器颁发证书

生成 服务器自身的私钥

openssl genpkey -algorithm RSA -out final_private.key

接着就是生成证书请求,和前面生成证书请求不同,因为目前主流浏览器都要求证书需要设置subjectAltName,如果没有设置SAN会报证书错误。

所以我们要换种方式生成证书请求,首先创建一个文件,比如我创建一个san.txt的文件

[req]
default_bits = 4096
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
countryName = country
stateOrProvinceName = province
localityName = city
organizationName = company name
## 换成自己的域名
commonName = lanpangzi.com  
[v3_req]
subjectAltName = @alt_names
[alt_names]
## 换成自己的域名
DNS.1=*.lanpangzi.com
DNS.2=*.lanpangzi2.com

到时候上述文件只需要更换为自己的域名即可。由于我的域名设置为了.lanpangzi.com 和.lanpangzi2.com,所以我还要改下本地的hosts文件。

## /etc/hosts
127.0.0.1 www.lanpangzi2.com
127.0.0.1       www.lanpangzi.com

接着生成服务器证书请求

openssl req -new -key final_private.key -out final_csr.csr -config san.txt -sha256

生成服务器证书

openssl x509 -req -days 365 -in final_csr.csr -CA ca_cert.crt -CAkey ca_private.key -set_serial 01 -out final_csr.crt -extfile san.txt -extensions v3_req

golang实现https服务验证证书

经过了上述步骤后算是生成了一个由ca机构颁发的证书,然后我们用golang代码实现一个https服务器。需要为https服务器传入证书以及服务器自身的私钥。

func main() {  
   http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {  
      fmt.Fprintf(w, "Hello, World!\n")  
   })  
   fmt.Println(http.ListenAndServeTLS(":443",  
      "./final_csr.crt",  
      "./final_private.key", nil))  
}

接着实现下客户端代码

func main() {  
   client := &http.Client{Transport: tr}  
   resp, err := client.Get("https://www.lanpangzi.com")  
   if err != nil {  
      fmt.Println("Get error:", err)  
      return  
   }  
   defer resp.Body.Close()  
   body, err := ioutil.ReadAll(resp.Body)  
   fmt.Println(string(body))  
}

启动服务端和客户端后能看到服务正常返回了。

/private/var/folders/yp/g914gkcd54qdm5d0qyc9ljm00000gn/T/GoLand/___go_build_codelearning_httpsdemo_client
Hello, World!

说明证书配置已经成功,而客户端验证证书的逻辑已经在本文开始讲解了。

golang实现https双向认证

上述代码只是实现了https的单向认证,即客户端对服务端的域名进行认证,在某些情况下,服务端也需要检验客户端是否合法,所以下面我们就来看下如何用golang实现双向认证的。首先我们还是要用ca位客户端颁发一个证书。

模拟ca机构向客户端颁发证书

生成 服务器自身的私钥

openssl genpkey -algorithm RSA -out client_private.key

创建一个san_client.txt的文件

[req]
default_bits = 4096
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
countryName = country
stateOrProvinceName = province
localityName = city
organizationName = company name
## 换成自己的域名
commonName = lanpangziclient.com  
[v3_req]
subjectAltName = @alt_names
[alt_names]
## 换成自己的域名
DNS.1=*.lanpangziclient.com
DNS.2=*.lanpangziclient2.com

到时候上述文件只需要更换为自己的域名即可。由于我的域名设置为了.lanpangzi.com 和.lanpangzi2.com,所以我还要改下本地的hosts文件。

## /etc/hosts
127.0.0.1 www.lanpangziclient2.com
127.0.0.1       www.lanpangziclient.com

接着生成服务器证书请求

openssl req -new -key client_private.key -out client_csr.csr -config san_client.txt -sha256

生成服务器证书

openssl x509 -req -days 365 -in client_csr.csr -CA ca_cert.crt -CAkey ca_private.key -set_serial 01 -out client_csr.crt -extfile san_client.txt -extensions v3_req

服务端和客户端需要做下改动,服务端默认不会去校验客户端身份,但是现在改成强制校验

func main() {  
  
   s := &http.Server{  
      Addr: ":443",  
      Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {  
         fmt.Fprintf(w, "Hello, World!\n")  
      }),  
      TLSConfig: &tls.Config{  
         ClientAuth: tls.RequireAndVerifyClientCert,  
      },  
   }  
  
   fmt.Println(s.ListenAndServeTLS("./final_csr.crt",  
      "./final_private.key"))  
}

客户端代码请求时需要带上自己的证书

func main() {  
   cliCrt, err := tls.LoadX509KeyPair("./client_csr.crt", "./client_private.key")  
   if err != nil {  
      fmt.Println("Loadx509keypair err:", err)  
      return  
   }  
   tr := &http.Transport{  
      TLSClientConfig: &tls.Config{  
         Certificates: []tls.Certificate{cliCrt},  
      },  
   }  
   client := &http.Client{Transport: tr}  
   resp, err := client.Get("https://www.lanpangzi.com")  
   if err != nil {  
      fmt.Println("Get error:", err)  
      return  
   }  
   defer resp.Body.Close()  
   body, err := ioutil.ReadAll(resp.Body)  
   fmt.Println(string(body))  
}

这样就完成了一个https的双向认证。

与https 原理分析进阶-模拟https通信过程相似的内容:

https 原理分析进阶-模拟https通信过程

> 大家好,我是蓝胖子,之前出过一篇[https的原理分析](https://mp.weixin.qq.com/s?__biz=MzU3NjY5MjY2Ng==&mid=2247486417&idx=1&sn=cb0355782e2a79d8f4c1908381b3aef7&chksm=fd1143

k8s容器互联-flannel host-gw原理篇

k8s容器互联-flannel host-gw原理篇 容器系列文章 容器系列视频 简析host-gw 前面分析了flannel vxlan模式进行容器跨主机通信的原理,但是vxlan模式需要对数据包进行额外的封包解包处理,带来的开销较大。 所以flannel提供了另外一种纯3层转发的通信模式,叫做h

docker网络 bridge 与overlay 模式

转载请注明出处: 1.bridge网络模式 1.1工作原理: 在Bridge模式中,Docker通过创建一个虚拟网络桥接器(bridge)将容器连接到主机上的物理网络接口。每个容器都会被分配一个IP地址,使得它们可以相互通信,并且可以与主机进行通信。 Docker的Bridge网络模式是默认的网络配

GAN!生成对抗网络GAN全维度介绍与实战

> 本文为生成对抗网络GAN的研究者和实践者提供全面、深入和实用的指导。通过本文的理论解释和实际操作指南,读者能够掌握GAN的核心概念,理解其工作原理,学会设计和训练自己的GAN模型,并能够对结果进行有效的分析和评估。 > 作者 TechLead,拥有10+年互联网服务架构、AI产品研发经验、团队管

华为云 UCS GitOps:轻松交付多集群云原生应用

摘要:使用华为云 UCS GitOps 配置管理来交付您的多云应用。 本文分享自华为云社区《华为云 UCS GitOps:轻松交付多集群云原生应用》,作者:华为云云原生团队。 随着业务的全球化发展和应用多元化部署的趋势,越来越多的客户选择通过混合云、多云模式来进行业务部署。选择多云进行部署可以提高部

Llama2-Chinese项目:2.2-大语言模型词表扩充

因为原生LLaMA对中文的支持很弱,一个中文汉子往往被切分成多个token,因此需要对其进行中文词表扩展。思路通常是在中文语料库上训练一个中文tokenizer模型,然后将中文tokenizer与LLaMA原生tokenizer进行合并,最终得到一个扩展后的tokenizer模型。国内Chinese

前端如何实现将多页数据合并导出到Excel单Sheet页解决方案|内附代码

本文由葡萄城技术团队于博客园原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 前端与数据展示 前后端分离是当前比较盛行的开发模式,它使项目的分工更加明确,后端负责处理、存储数据;前端负责显示数据.前端和后端开发人员通过接口进行数据的交换。因此前端

[转帖]Nginx为什么快到根本停不下来?

Nginx 以其高性能,稳定性,丰富的功能,简单的配置和低资源消耗而闻名。本文从底层原理分析 Nginx 为什么这么快! Nginx 的进程模型 Nginx 服务器,正常运行过程中: 多进程:一个 Master 进程、多个 Worker 进程。Master 进程:管理 Worker 进程。对外接口:

4.1 应用层Hook挂钩原理分析

InlineHook 是一种计算机安全编程技术,其原理是在计算机程序执行期间进行拦截、修改、增强现有函数功能。它使用钩子函数(也可以称为回调函数)来截获程序执行的各种事件,并在事件发生前或后进行自定义处理,从而控制或增强程序行为。Hook技术常被用于系统加速、功能增强、等领域。本章将重点讲解Hook是如何实现的,并手动封装实现自己的Hook挂钩模板。首先我们来探索一下Hook技术是如何实现的,如下

Web攻防--Java_SQL注入--XXE注入-- SSTI模板注入--SPEL表达式注入

预编译 编译器在编译sql语句时,会依次进行词法分析、语法分析、语义分析等操作, 预编译技术会让数据库跳过编译阶段,也就无法就进行词法分析,关键字不会被拆开,注入语句也就不会被识别为SQL的关键字,从而防止恶意注入语句改变原有SQL语句本身逻辑。 Java_JDBC注入 在使用JDBC进行数据库操作