IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 【C++】使用libhv实现一个双向证书认证的Https客户端(附带Go实现的服务端) -> 正文阅读

[网络协议]【C++】使用libhv实现一个双向证书认证的Https客户端(附带Go实现的服务端)


libhv是一个国产的网络库,在v1.2.5版本提供了SSL客户端支持,可以便携地实现双向认证。

实现

客户端

废话不多说,先show the code:

#include "hv/http_client.h"
using namespace hv;

int main() {
    // 创建Client
    int ret;
    http_client_t *cli = http_client_new(NULL, 8080, 1); // 这里参数填什么无所谓
    if(cli == NULL) {
        printf("Error: cli is null\n");
        return 0;
    }

    // 设置客户端证书
    hssl_ctx_opt_t *ssl_opt = new hssl_ctx_opt_t;
    ssl_opt->verify_peer = 1; // 认证对方身份
    ssl_opt->endpoint = HSSL_CLIENT;
    ssl_opt->ca_path = NULL; // 可以为一个证书目录,参见https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_load_verify_locations.html
    ssl_opt->ca_file = "cert/ca.crt"; // CA证书
    ssl_opt->crt_file = "cert/client.crt"; // 客户端证书
    ssl_opt->key_file = "cert/client_rsa_private.pem"; // 客户端私钥
    hssl_ctx_t ctx = hssl_ctx_new(ssl_opt); // 生成一个hssl_ctx
    if(ctx == NULL) {
        printf("Error: ctx is null\n");
        return 0;
    }
    // !重点!
    ret = http_client_set_ssl_ctx(cli, ctx); // 将上述hssl_ctx与之前创建的Client绑定
    if (ret != 0) {
        printf("Cert Error: %s : %d\n", http_client_strerror(ret), ret);
        return 0;
    }
    
    // 创建请求
    HttpRequest req;
    req.method = HTTP_POST;
    req.url = "https://127.0.0.1:8080/echo";
    req.headers["Connection"] = "keep-alive";
    req.body = "This is a test request.";
    req.timeout = 10;

    // 发送请求
    HttpResponse resp;
    ret = http_client_send(cli, &req, &resp);

    // 解析
    if (ret != 0) {
        printf("Request Failed: %s : %d\n", http_client_strerror(ret), ret);
    } else {
        printf("%d %s\r\n", resp.status_code, resp.status_message());
        printf("%s\n", resp.body.c_str());
    }

    http_client_del(cli);
}

Https服务端

服务端的实现就更简单了,也不是本文的重点,所以用Go来实现一下:

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

const (
	CACertPath     = "cert/ca.crt"
	ServerCertPath = "cert/server.crt"
	ServerKeyPath  = "cert/server_rsa_private.pem"
)

func handler(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return
	}
	fmt.Fprintf(w, "Get From Client: %s", string(body))
}

func main() {
	// 设置服务端证书
	pool := x509.NewCertPool()
	crt, err := ioutil.ReadFile(CACertPath) // CA证书
	if err != nil {
		log.Fatalln("读取证书失败!", err.Error())
	}
	pool.AppendCertsFromPEM(crt)
	http.HandleFunc("/echo", handler)
	s := &http.Server{
		Addr: ":8080",
		TLSConfig: &tls.Config{
			ClientCAs:  pool,
			ClientAuth: tls.RequireAndVerifyClientCert, // 重点!要求检验客户端证书
		},
	}
	// 启动服务,参数分别为:服务端证书、服务端私钥
	log.Fatal(s.ListenAndServeTLS(ServerCertPath, ServerKeyPath))
}

原理

双向认证基本原理

如果已经清楚双向认证原理的同学,建议直接跳过这一节。

双向认证,顾名思义,客户端和服务器端都需要验证对方的身份,可以使得连接更加安全,在建立Https连接的过程中,握手的流程比单向认证多了几步。
单向认证的过程:客户端从服务器端下载服务器端公钥证书进行验证,然后生成对称密钥用以加密数据、建立安全通信通道。
双向通信的过程:除了客户端需要从服务器端下载服务器的公钥证书进行验证外,服务端还会要求客户端提供证书,因此客户端还需要把客户端的公钥证书上传到服务器端给服务器端进行验证,等双方都认证通过了,才开始建立安全通信通道进行数据传输。

因此最基本的一点:服务端要保存服务端的证书和私钥,并在建立连接时将证书发给客户端;同样的,客户端也要保存客户端的证书和私钥,并在建立连接时将证书发给服务端。

libhv客户端实现原理

那么libhv是咋做到将一个hssl_ctx绑定到http_client便可以实现双向认证(其实也就是向服务端提供自己的证书认证)的呢?

以下内容皆基于libhv v1.2.5发布版

首先,我们使用hssl_ctx_new函数来初始化了一个hssl_ctx,而在libhv中,hssl_ctx是一个携带了证书、私钥等的SSL上下文。

对着源码,在ssl/openssl.c:16行,声明了其初始化函数hssl_ctx_new:可以看到,该函数执行了Openssl的SSL初始化工作,并且,若初始化时填入了param,也就是hssl_ctx_opt_t,则hssl_ctx会使用你所设置的CA证书来执行SSL_CTX_load_verify_locations、使用证书文件来执行SSL_CTX_use_certificate_file、使用私钥文件来执行SSL_CTX_use_PrivateKey_file,并随后校验证书及私钥,同时,若设置hssl_ctx_opt_t->verify_peer != 0则会使SSL验证模式设为SSL_VERIFY_PEER

熟悉OpenSSL的同学应该已经发现了,上述过程,正是客户端设置己方证书、私钥并要求对方提供证书,也就是双向认证的初始化流程。

随后,我们使用http_client_set_ssl_ctx函数来将该hssl_ctx与之前创建的HttpClient绑定,在该函数(http/client/http_client.cpp:114)中,将HttpClient的ssl_ctx字段设为了该hssl_ctx,而该字段可以在http_client_connect(http/client/http_client.cpp:429)等函数中发现:若该字段不为NULL且为https请求,则会使用此前设置的hssl_ctx来接管整个客户端连接、通信过程,也就是使用hssl_read、hssl_write等函数来代替原本的recv、send等函数,使得整个通信信道被hssl_ctx所管理的SSL连接接管。

而由于此前已经在hssl_ctx_new时设置了己方的证书、私钥,并且SSL连接已接管,因此,在服务端要求客户端提供证书认证时,自然而然地,由OpenSSL处理了相关逻辑,正确地提供了己方证书,完成双向认证。

附加说明

这里着重说明一下,为了正常使用libhv的SSL认证功能,需要在安装libhv时附加Openssl选项,这是我的编译安装命令:

# 安装openssl
sudo apt install openssl libssl-dev

# 安装带有openssl的libhv
git clone https://github.com/ithewei/libhv.git
cd libhv
./configure --with-openssl
make
sudo make install

如何编译引用libhv库的c++代码,想必看这篇文章的同学应该都会吧?我就不附上了。

生成证书及私钥:

# 生成CA密钥及其自签名证书
openssl req -newkey rsa:2048 -nodes -keyout ca_rsa_private.pem -x509 -days 365 -out ca.crt -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CA/emailAddress=youremail@qq.com"

# 生成服务器密钥及待签名证书
openssl req -newkey rsa:2048 -nodes -keyout server_rsa_private.pem  -out server.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=SERVER/emailAddress=youremail@qq.com"
# 使用CA证书及密钥对服务器证书进行签名:
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca_rsa_private.pem -CAcreateserial -out server.crt

# 生成客户端密钥及待签名证书
openssl req -newkey rsa:2048 -nodes -keyout client_rsa_private.pem -out client.csr -subj "/C=CN/ST=GD/L=SZ/O=COM/OU=NSP/CN=CLIENT/emailAddress=youremail@qq.com"
# 使用CA证书及密钥对客户端证书进行签名:
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca_rsa_private.pem -CAcreateserial -out client.crt
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 12:47:24  更:2022-04-04 12:47:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:24:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码