大家好,我是蓝胖子,之前出过一篇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的握手阶段,服务端会发送自身的证书给客户端,客户端会去验证这个证书的有效性,有效性是这样保证的:

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

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

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

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

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系统如何添加证书信任,

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

模拟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通信过程的更多相关文章

  1. HTTPS 原理分析——带着疑问层层深入

    HTTPS 随着 HTTPS 建站的成本下降,现在大部分的网站都已经开始用上 HTTPS 协议.大家都知道 HTTPS 比 HTTP 安全,也听说过与 HTTPS 协议相关的概念有 SSL .非对称加 ...

  2. HTTPS原理分析

    引言 随着互联网安全意识的普遍提高,对安全要求稍高的应用中,HTTPS的使用是很常见的,所以,无论是即时通讯IM还是其它应用,在网络安全意识增强的今天,很多场景下使用HTTPS是肯定没错的.对于即时通 ...

  3. https原理及tomcat配置https方法

    一. 什么是HTTPS 在说HTTPS之前先说说什么是HTTP,HTTP就是我们平时浏览网页时候使用的一种协议.HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不 ...

  4. (HTTPS)-https原理及tomcat配置https方法

    一. 什么是HTTPS 在说HTTPS之前先说说什么是HTTP,HTTP就是我们平时浏览网页时候使用的一种协议.HTTP协议传 输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常 ...

  5. https原理和如何配置https

    参考:https://blog.51cto.com/11883699/2160032 上面说的已经很好地,我这里简单做个总结: 在网上我们做数据交互时候一般用的http协议,但是这种方式会使得交互内容 ...

  6. Charles的HTTPS抓包方法及原理分析

    原文地址:http://www.jianshu.com/p/870451cb4eb0 背景 作为移动平台的RD,项目开发过程中一项比较重要的甩锅技能——抓包应该大家都比较熟悉了,毕竟有些bug可能是由 ...

  7. 【转】HTTPS系列干货(一):HTTPS 原理详解

    HTTPS系列干货(一):HTTPS 原理详解 前言 HTTPS(全称:HyperText Transfer Protocol over Secure Socket Layer),其实 HTTPS 并 ...

  8. web压测工具http_load原理分析

    一.前言 http_load是一款测试web服务器性能的开源工具,从下面的网址可以下载到最新版本的http_load: http://www.acme.com/software/http_load/ ...

  9. Mybatis 原理分析

    对于入门程序的流程分析 使用过程 读配置文件 读取配置文件时绝对路径和相对路径(web工程部署后没有src路径)都有一定问题,实际开发中一般有两种方法 使用类加载器,它只能读取类路径的配置文件 使用S ...

  10. 从wireshake分析http和https的通信过程

    参考文章: Wireshark基本介绍和学习TCP三次握手 [技术流]Wireshark对HTTPS数据的解密 Wireshark/HTTPS Journey to HTTP/2 以TCP/IP协议为 ...

随机推荐

  1. IBM Cloud Computing Practitioners 2019 (IBM云计算从业者2019)Exam答案

    Cloud Computing Practitioners 2019 IBM Cloud Computing Practitioners 2019 (IBM云计算从业者2019)Exam答案,加粗的为 ...

  2. IO流中「线程」模型总结

    目录 一.基础简介 二.同步阻塞 1.模型图解 2.参考案例 三.同步非阻塞 1.模型图解 2.参考案例 四.异步非阻塞 1.模型图解 2.参考案例 五.Reactor模型 1.模型图解 1.1 Re ...

  3. python标准模块之subprocess

    subprocess --- 子进程管理 源代码: Lib/subprocess.py 写在前面: 感觉也就这俩有用: subprocess.run() subprocess.Popen()   w下 ...

  4. 8.redis存储token以及springboot整合Jwt

    1.总结: 昨天主要是下载安装和使用redis去存储token,但在创建redis的新建的时候出现了host异常,原因是没有将服务中的进程关掉,再重新打开redis的server,再打开cli: 回顾 ...

  5. 【vue3-element-admin】ESLint+Prettier+Stylelint+EditorConfig 约束和统一前端代码规范

    前言 本文介绍 vue3-element-admin 如何通过ESLint 检测 JS/TS 代码.Prettier 格式化代码.Stylelint 检测 CSS/SCSS 代码和配置 EditorC ...

  6. JS逆向实战13——某市公共资源交易中心Cookie混淆加密

    "本文地址:https://www.cnblogs.com/zichliang/p/17346860.html 目标网站 aHR0cDovL2xkZ2d6eS5obmxvdWRpLmdvdi ...

  7. 【总结】从++i思考计算机原子性和线程安全

    在C++中,++i被认为是一种原子性操作,即不可分割的.不可中断的整体.它能够确保对变量的修改完整且正确,从而避免了数据竞争等问题,提高了程序的并发性和可靠性.然而,有些人可能会将原子性和线程安全混淆 ...

  8. 突破tls/ja3新轮子

    我之前的文章介绍了SSL指纹识别 https://mp.weixin.qq.com/s/BvotXrFXwYvGWpqHKoj3uQ 很多人来问我BYPass的方法 主流的BYPASS方法有两大类: ...

  9. Prism Sample 20-NavigateToExistingViews

    上一个例子介绍了INavigationAware中的OnNavitationTo,这次是第二个实现函数. IsNavitationTarget,这个名字有点误导,真实的作用是,当从其它页面导航至本页面 ...

  10. docker无法启动,报错grpc: addrConn.createTransport failed to connect to {unix:///run/containerd/containerd.

    docker无法启动,报错.k8s的pod镜像加载失败. 解法方法: 删除/var/lib/docker/和/var/lib/containerd/ 这两个文件夹,重起docker服务. 问题完美解决 ...