探索GmSSL+Nginx实践及原理-CSDN博客
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
前言
随着大国崛起步伐的迈进敏感单位的数据安全问题越发受到重视数据的加密安全传输尤为重要对于安全问题国家自研加密算法提供了有力的保障。
作为信创行业的国有企业十分有必要在网络通信中使用国密算法加密通信保障客户数据安全做出客户放心满意的产品。
本文将介绍如何制作出支持国密算法的nginx及相关原理。
GmSSL
介绍
- 是由北京大学自主开发的国产商用密码开源库。
- 实现了对国密算法、标准和安全通信协议的全面功能覆盖支持包括移动端在内的主流操作系统和处理器。
- 支持密码钥匙、密码卡等典型国产密码硬件。
- 提供功能丰富的命令行工具及多种编译语言编程接口。
它在国密算法中主要两个作用提供库、生成证书。
版本问题
- 根据 github 分支可以看出版本有master(也称为 v3)、GmSSL-v2、GmSSL-v1。
- GmSSL 起始是由 OpenSSL 开源项目变更而来相当于 OpenSSL 的一个分支。v2 版本及以前版本提供和 OpenSSL 相同的 API可以很好的兼容依赖 OpenSSL API的应用比如兼容 nginx、tomcat 等。
- 但是最新版 v3 版本采用了新设计的架构和API不再兼容 OpenSSL API的应用。
- 为了体现 v3 版本的适用性团队开源出了基于 Nginx 1.21.0 修改的 Nginx-with-GmSSLv3部署启动后 tls 流程我是没跑通的。
- v3 版本还有一个重要变更支持TLS 1.3而 v2 版本只支持 TLS 1.2。
安装
下载代码
GmSSL Github 代码下载地址
我们切换分支使用 GmSSL-v2。
编译安装
git clone https://github.com/guanzhi/GmSSL.git
cd GmSSL
# 指定 gmssl 安装路径
./config --prefix=/usr/local/gmssl
# 编译安装
make && make install
# 检查安装成功否
/usr/local/gmssl/bin/gmssl version
错误处理提示
- 出现
.so
文件找不到的报错这表示动态链接库找不到这种情况要么某库未安装去yum
安装一下要么是需要库已经装了但是路径不在当前系统的LIB
路径里需要export LD_LIBRARY_PATH=${库的路径}:$LD_LIBRARY_PATH
- 使用
/usr/local/gmssl/bin/gmssl
命令大概率会出现undefined symbol: xxx
的错误。这说明找到库了但找错了可能找到默认库路径里去了而默认库里恰好存在同名的so
文件比如 libcrypto.so.1.1、libssl.so.1.1 文件是 gmssl 所需的但默认库路径也有的。添加环境变量export LD_LIBRARY_PATH=/usr/local/gmssl/lib:$LD_LIBRARY_PATH
即可解决。
Nginx 安装
下载代码
nginx 下载
这里提供下载的 1.22.0 版本你也可以选择合适的版本下载并解压。
编译安装
准备
在刚解压的路径vim 编辑nginx/auto/lib/openssl/conf
文件
将文中内容
CORE_INCS="$CORE_INCS $OPENSSL/.openssl/include"
CORE_DEPS="$CORE_DEPS $OPENSSL/.openssl/include/openssl/ssl.h"
CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libssl.a"
CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libcrypto.a"
修改为
CORE_INCS="$CORE_INCS $OPENSSL/include"
CORE_DEPS="$CORE_DEPS $OPENSSL/include/openssl/ssl.h"
CORE_LIBS="$CORE_LIBS $OPENSSL/lib/libssl.a"
CORE_LIBS="$CORE_LIBS $OPENSSL/lib/libcrypto.a"
执行安装
./configure --with-pcre \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_stub_status_module \
--with-http_auth_request_module \
--with-http_slice_module \
--with-mail \
--with-threads \
--with-file-aio \
--with-stream \
--with-mail_ssl_module \
--with-stream_ssl_module \
--with-openssl="${gmssl 安装路径}" \
--with-cc-opt="-I${gmssl 安装路径}/include" \
--with-ld-opt="-lm"
# 编译安装
make && make install
上述命令可以看出with-openssl
是指定 openssl 安装路径的但我们实际需要填写安装 gmssl 的路径。
证书
TLS 介绍
tls 是传输层安全协议其“简单化”的握手流程如下
- Client 向 Server 请求建立连接client hello。
- Server 将自己的证书发送给 Clientserver hello。
- Client 验证证书然后使用证书中的公钥加密后续通信的对称密钥将加密内容发送给 Server。
- Server 收到后解密获得对称密钥且将用该对称密钥上层数据进行加解密。
- 自此 TLS 握手完成接下来使用对称密钥进行通信。
真实的 tls 握手流程会比上述复杂主要是为了防止降级攻击、重放攻击等攻击手段。
上述握手过程中有几点需要说明
- tls 中加密方式包含非对称加密、对称加密两种。
- tls 中使用非对称加密的主要目的只是为了安全“护送”对称密钥。真正数据是使用对称密钥加密使用非对称密钥太耗时了。
- 我们并不能使用公钥直接“护送”对称密钥因为客户端分不清公钥是“哪家”的。万一 DNS 被劫持了邪恶服务器顶替正常服务器客户端收到了邪恶服务器的公钥和邪恶服务器撩起来就麻烦了。为了防止这种情况需要对公钥签名标明这个公钥是“哪家”的。
- 所谓签名就是将签名所需内容公钥、域名、所有者等信息发给 CA 机构公认信任的机构CA 机构会验证信息正确性然后将信息做个hash再将这个 hash 值和签名所需内容放在一起最后 CA 机构用自己的私钥对放在一起的信息进行加密这个过程就是签名加密后的内容就是证书。现在再想一下上述的邪恶服务器还敢顶替正常服务器用自己的证书和客户握手吗肯定不敢了它自己的证书可在 CA 机构备过案了呀。
- 如果不是 CA 机构签发的证书是私人签发的客户端会有警告并提示是否继续前往。
证书介绍
格式
其实证书就两种格式二进制、文本格式。
常见的证书后缀包括.der、.cer、.pem、.crt、.csr、.pfx、.p12、.jks等。
der、cer二进制格式只保存证书不保存私钥。pfx、p12二进制格式同时包含证书和私钥。pem、crt、csr 一般是文本格式。
Gmssl 生成证书指令
概述
指令及开发文档gmssl 命令及开发文档。
可以使用 Gmssl 发布私人证书gmssl指令繁多我们只需理解相关步骤指令含义即可相关步骤包含生成私钥、生成证书签名请求文件、生成证书。
生成私钥
gmssl ecparam -genkey -name sm2p256v1 -noout -out root.key
- gmssl 官方说法
ecparam
和genpkey
命令都可以用来生成私钥但实际上genpkey
不可用。 -name
表示选择一条椭圆曲线来生成私钥可以通过gmssl ecparam -list_curves
查看内建了哪些椭圆曲线。- 我们使用
-name sm2p256v1
就行加密算法相关不用深究。
生成证书签名请求文件
gmssl req -new -key root.key -out root.csr -subj "/C=CN/ST=BeiJing/L=BeiJing/O=公司名/CN=tom/emailAddress=邮箱"
- 生成 root.csr 证书签名请求文件为什么生成root.csr它的作用是什么我们在签名的时候需要提供公钥、域名、所有者等信息给 CA 机构root.csr 就是这些信息合在一起的一个文本。所以可以知道csr文件里是含有公钥的。
-subj
表示主题使用该指令就不用按照提示手动填入相关信息。
生成证书
/usr/local/gmssl/bin/gmssl x509 -req -days 3650 -sm3 -in root.csr -extfile openssl.cnf -extensions v3_ca -signkey root.key -out root.crt
- 使用根CA私钥对
.crt
文件加密签名。 - 这里的
-extfile
指定的openssl.cnf
配置文件其实就是 openssl 安装路径下的配置文件拷贝出来并添加上以下配置
# 这个配置必加否则 gmssl 会报错的。openssl 则无需。
[ v3enc_req ]
basicConstraints = CA:FALSE
keyUsage = keyAgreement, keyEncipherment, dataEncipherment
查看证书内容
/usr/local/gmssl/bin/gmssl x509 -in server.crt -text -noout
证书生成
生成脚本如下
#!/bin/bash
# 生成CA证书
/usr/local/gmssl/bin/gmssl ecparam -genkey -name sm2p256v1 -noout -out root.key
/usr/local/gmssl/bin/gmssl req -new -key root.key -out root.csr -subj "/C=CN/ST=BeiJing/L=BeiJing/O=公司名/CN=tom/emailAddress=邮箱"
/usr/local/gmssl/bin/gmssl x509 -req -days 3650 -sm3 -in root.csr -extfile openssl.cnf -extensions v3_ca -signkey root.key -out root.crt
# Server签名证书
/usr/local/gmssl/bin/gmssl ecparam -genkey -name sm2p256v1 -noout -out server.key
/usr/local/gmssl/bin/gmssl req -new -SM3 -key server.key -out server.csr -subj "/C=CN/ST=BeiJing/L=BeiJing/O=公司名/CN=tom/emailAddress=邮箱"
/usr/local/gmssl/bin/gmssl x509 -req -SM3 -days 3650 -in server.csr -extfile openssl.cnf -extensions v3_req -CA root.crt -CAkey root.key -set_serial 1000000001 -out server.crt
# Server加密证书
/usr/local/gmssl/bin/gmssl ecparam -genkey -name sm2p256v1 -noout -out server_en.key
/usr/local/gmssl/bin/gmssl req -new -SM3 -key server_en.key -out server_en.csr -subj "/C=CN/ST=BeiJing/L=BeiJing/O=公司名/CN=tom/emailAddress=邮箱"
/usr/local/gmssl/bin/gmssl x509 -req -SM3 -days 3650 -in server_en.csr -extfile openssl.cnf -extensions v3enc_req -CA root.crt -CAkey root.key -set_serial 1000002001 -out server_en.crt
这里除了创建根CA密钥对逻辑上只需再创建一个证书来使用就行可我们脚本为什么创建了两个呢后面再说。
Nginx 证书配置
在nginx.conf
里对应的“server 块”中配置
ssl_certificate "/usr/local/nginx/conf/tls/server.crt";
ssl_certificate_key "/usr/local/nginx/conf/tls/server.key";
ssl_certificate "/usr/local/nginx/conf/tls/server_en.crt";
ssl_certificate_key "/usr/local/nginx/conf/tls/server_en.key";
需要配置两个证书也就是上面脚本创建两个的原因。
如果 Nginx 中只配置了单个证书那么你抓包查看 tls 流程就会发现client hello
后服务端返回一个handshake failed
错误。此时你会被这个哲学问题困扰很久就和当初我一样下面解释原因
- 正常 tls 中使用公钥可以用来验证签名和加解密。但是国密里不行它规范要求分为验证签名的公钥和加解密的公钥两种公钥所以这里得配两个同时国密浏览器也做了相应适配。
- 如果是 C++ 写的“国密客户端”可能需要调用 GmSSL 提供的 API 了。
完结
感谢阅读完结
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |