首页 > 其他分享 >https 原理分析进阶-模拟https通信过程

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

时间:2023-06-28 14:12:15浏览次数:51  
标签:进阶 证书 ca req https com 模拟 csr

大家好,我是蓝胖子,之前出过一篇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采用非对称加密的方式交换密钥,然后使用对称加密的方式对数据进行加密,并且对消息的内容采用摘要算法得到消息摘要,这样对端在解密数据后可以通过相同的消息摘要算法对计算后的消息摘要和传过来的消息摘要进行对比,从而判断数据是否经过篡改。

模拟证书颁发

接下来,我们就要开始实现下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的双向认证。

标签:进阶,证书,ca,req,https,com,模拟,csr
From: https://www.cnblogs.com/hobbybear/p/17511245.html

相关文章

  • Android架构开发进阶指南,Android Jetpack Compose强化实战
    在我们日常的开发中,如果所有人各自为战,没有统一规范,久而久之,项目代码会变得混乱且后续难以维护。而这时,许多人就会使用Jetpack这个由多个库组成的套件,来减少样板代码,以便将精力更多的集中于重要的编码工作上面。统一的架构模式通常会给我们带来诸多好处,如:统一开发规范,使得代码整洁......
  • 【面试实战】面试官面试模拟训练【一】
    求职者自我介绍【30s——60s】面试官您好,我是一名寻求Java开发岗位的求职者。我拥有三年的Java开发经验,熟悉Java、Spring框架、数据库等相关技术。在过去的工作中,我参与了多个项目的开发和维护,积累了丰富的编码和解决问题的经验。我熟悉并且能够独立完成需求分析、系统设计、编......
  • 接口测试(2)--HTTP协议,HTTPS协议
    HTTP协议:是超文本传输协议,它主要规定了在互联网中传输数据时的标准。主要用于定义客户端与web端服务器通讯的格式。HTTP协议工作于客户端-服务端上。浏览器作为HTTP客户端,通过URL向,HTTP服务端即WEB服务器,发送所有请求。HTTP特点:支持客户端/服务器模式,简单快速,灵活,无连接,无......
  • 强化学习从基础到进阶-常见问题和面试必知必答[7]:深度确定性策略梯度DDPG算法、双延迟
    强化学习从基础到进阶-常见问题和面试必知必答[7]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解1.核心词汇深度确定性策略梯度(deepdeterministicpolicygradient,DDPG):在连续控制领域经典的强化学习算法,是深度Q网络在处定性”表示其输出的是一个确定的动作,......
  • 强化学习从基础到进阶--案例与实践[7.1]:深度确定性策略梯度DDPG算法、双延迟深度确定
    强化学习从基础到进阶--案例与实践[7.1]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解项目实战1、定义算法1.1定义模型!pipuninstall-yparl!pipinstallparlimportparlimportpaddleimportpaddle.nnasnnimportpaddle.nn.functionalasFcl......
  • 强化学习从基础到进阶-常见问题和面试必知必答[7]:深度确定性策略梯度DDPG算法、双延迟
    强化学习从基础到进阶-常见问题和面试必知必答[7]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解1.核心词汇深度确定性策略梯度(deepdeterministicpolicygradient,DDPG):在连续控制领域经典的强化学习算法,是深度Q网络在处定性”表示其输出的是一个确定的动作,可......
  • 强化学习从基础到进阶–案例与实践[7.1]:深度确定性策略梯度DDPG算法、双延迟深度确定
    强化学习从基础到进阶--案例与实践[7.1]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解项目实战1、定义算法1.1定义模型!pipuninstall-yparl!pipinstallparlimportparlimportpaddleimportpaddle.nnasnnimportpaddle.nn.functionalasFcla......
  • 6.26 模拟赛小记
    A.生成字符串(syoj.1761)洛谷 P6191[USACO09FEB]BullsAndCowsS首先单独统计只有0-1个1的答案;另所求序列由"1000"这样的形式再加一个1构成,设当前统计的有i个1,此时序列长度j为(k+1)*(i-1)+1。如果所需序列长度已经超过限制便跳出。否则计算$$C^......
  • 【考后总结】6 月多校国赛模拟赛 6
    6.27冲刺国赛模拟25T1简单计数不是古典概型所以不能方案数相除。考虑枚举第一个选择的位置\(i\),这样分成两个独立的区间,只关心\(k\)所在的一个,转移方程:\[f_{n,k}=\dfrac{1}{n-1}\left([k<n]+[k>1]+\sum_{i>k}f_{i-1,k}+\sum_{i<k-1}f_{n-(i+1),k-(i+1)}\right)\]前缀和......
  • 【考后总结】6 月西安多校国赛模拟赛 3
    6.17冲刺国赛模拟20T1树染色容易发现每种方案都可以变成没有交边的链剖分,在此基础上的方案数是每个链顶的深度,考虑DP。直接DP大致是维护\(\prod(\proda+\prodb)\timesdep_{top}\),发现这个东西非常不好转移,转移时需要枚举叶子,复杂度不优秀。改为设\(f_{i,0/1}\)表......