1. 项目概述:为什么要在CentOS 7上折腾国密HTTPS?
最近几年,但凡和“安全”、“信创”沾边的项目,国密算法(SM2/SM3/SM4)的集成几乎成了硬性要求。作为一名常年和服务器打交道的运维,我手头就接到过不少这样的任务:把现有的Web服务从国际通用的RSA/SHA256迁移到国密体系。一开始我也头疼,国际标准的OpenSSL用得好好的,为啥要换?但深入了解后才发现,这不仅是合规要求,更是技术自主可控的必然趋势。
简单来说,国密HTTPS就是用我们自己的密码算法套件,替换掉TLS/SSL协议中那些由国外制定的算法。核心是三个“SM”:SM2用于非对称加密和签名(替代RSA/ECDSA),SM3用于摘要(替代SHA256),SM4用于对称加密(替代AES)。在CentOS 7这个依然广泛存在于企业内网和传统业务环境中的稳定系统上实现它,GmSSL是目前最成熟、最受社区认可的开源工具箱。
这个项目,就是带你从零开始,在一台干净的CentOS 7服务器上,用GmSSL编译、配置、签发证书,最终搭建一个支持国密双证书体系的HTTPS服务。我会把每一步的操作意图、背后的原理,以及我踩过的所有坑都摊开来讲清楚。无论你是为了满足项目验收,还是单纯想学习国密技术的实操,这篇手记都能给你一份能“抄作业”的指南。
2. 环境准备与GmSSL源码编译
工欲善其事,必先利其器。在开始编译之前,我们需要一个干净、标准的CentOS 7环境。
2.1 系统基础环境配置
首先,确保你的CentOS 7系统是最小化安装,并且网络通畅。第一步永远是更新系统并安装必要的开发工具链。
# 1. 更新系统到最新状态 sudo yum update -y # 2. 安装编译所需的开发工具和库 sudo yum groupinstall -y "Development Tools" sudo yum install -y wget openssl openssl-devel perl perl-IPC-Cmd这里安装openssl和openssl-devel并不是为了用它,而是因为一些Perl脚本和系统工具可能会依赖OpenSSL的头文件和库路径,提前安装可以避免一些诡异的编译依赖错误。perl是GmSSL配置脚本所必需的。
接下来,我们创建一个独立的工作目录,避免文件散落各处。
mkdir -p ~/gmssl_build cd ~/gmssl_build2.2 获取与验证GmSSL源码
GmSSL的主仓库在GitHub上。我们直接克隆最新的稳定版本。这里有一个关键点:GmSSL 3.x版本是一个重要的里程碑,它实现了对国密双证书(签名证书和加密证书)的完整支持,与《GB/T 38636-2020》标准对齐。因此,我们优先选择3.x的版本。
# 克隆GmSSL官方仓库 git clone https://github.com/guanzhi/GmSSL.git cd GmSSL # 查看并切换到最新的稳定标签,例如 3.1.1 git tag -l | grep ^3. | sort -V | tail -5 # 查看最新的3.x版本 git checkout gmssl_3.1.1 # 以实际最新稳定版为准注意:直接从
master分支编译有时会碰到正在开发中的特性,可能导致不稳定。始终建议切换到带有版本号的标签(Tag)进行编译,这是生产环境部署的黄金法则。
源码下载后,我强烈建议你花一分钟时间验证一下文件的完整性。虽然从GitHub克隆通常很安全,但这是一个培养好习惯的机会:对比官方发布的校验和(如果提供的话)。
2.3 编译安装GmSSL详解
GmSSL的编译采用了经典的configure、make、make install三部曲,但其中有一些针对国密和安装路径的关键配置。
# 1. 运行配置脚本,指定安装前缀 ./config --prefix=/usr/local/gmssl3 --openssldir=/usr/local/gmssl3/ssl # 2. 编译源码(-j参数根据你的CPU核心数设定,可以加快速度) make -j$(nproc) # 3. 以root权限安装到系统目录 sudo make install现在,我们来拆解一下这几个命令背后的“为什么”:
--prefix=/usr/local/gmssl3:这是最关键的参数。它指定了GmSSL的安装根目录。将其安装在/usr/local下是一个标准做法,与系统自带的/usr/bin/openssl完全隔离,避免文件冲突。命名为gmssl3可以清晰区分版本。--openssldir:这个目录用于存放默认的配置文件openssl.cnf、证书、私钥等。将其放在前缀目录下,保持所有相关文件的集中管理。make -j$(nproc):$(nproc)命令会自动获取你CPU的逻辑核心数,然后启动并行编译任务,能极大缩短编译时间。如果你的服务器核心数较少(如1核),直接使用make即可。
编译安装完成后,GmSSL的可执行文件位于/usr/local/gmssl3/bin/gmssl。为了能在任何地方方便地使用它,我们需要将其路径加入到系统的PATH环境变量中,并更新动态链接库的缓存。
# 将GmSSL的bin目录添加到PATH echo 'export PATH=/usr/local/gmssl3/bin:$PATH' >> ~/.bashrc source ~/.bashrc # 更新系统的动态库搜索路径 echo '/usr/local/gmssl3/lib' | sudo tee /etc/ld.so.conf.d/gmssl3.conf sudo ldconfig最后,验证安装是否成功:
gmssl version你应该能看到类似GmSSL 3.1.1 - ...的输出。同时,运行which gmssl应该显示/usr/local/gmssl3/bin/gmssl。这一步成功,标志着我们的“武器”已经锻造完毕。
3. 国密数字证书的生成与管理
有了GmSSL,接下来就要打造HTTPS的“身份证”——数字证书。国密体系与RSA体系一个显著不同是双证书机制:一份是签名证书(用于身份认证),一份是加密证书(用于密钥交换)。我们需要依次生成根CA、服务端的签名证书和加密证书。
3.1 创建私有根证书颁发机构(CA)
在内部测试或开发环境中,我们不需要向公共CA购买证书,可以自己扮演CA。这需要先创建一个自签名的根证书。
首先,为我们的CA创建工作目录并初始化:
mkdir -p ~/sm2_ca/{certs, private, newcerts} cd ~/sm2_ca touch index.txt echo 1000 > serialindex.txt是证书数据库,serial文件提供下一个证书的序列号。
接下来,生成根CA的私钥和自签名证书。这里使用SM2算法。
# 生成根CA的SM2私钥,使用素数域参数(-param sm2p256v1) gmssl ecparam -genkey -name sm2p256v1 -out private/ca.key # 为私钥加上密码保护(可选但推荐) gmssl ec -aes256 -in private/ca.key -out private/ca_encrypted.key # 使用私钥生成自签名的根证书 gmssl req -new -x509 -days 3650 -key private/ca.key -out certs/ca.crt \ -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=Dev/CN=My SM2 Root CA"参数解析与注意事项:
-param sm2p256v1:指定使用国密SM2的椭圆曲线参数。这是标准参数,必须指定。-aes256:用AES-256加密私钥文件,这样私钥本身会受密码保护。生产环境中务必使用!执行命令后会提示你输入密码。-subj:指定证书主题信息。C是国家,ST是省,L是城市,O是组织,OU是部门,CN是通用名称(对于CA,通常是一个描述性名称)。-days 3650:证书有效期10年。根CA通常设置较长的有效期。
实操心得:私钥文件(
.key)是最高机密。生成后,立即将其权限设置为仅所有者可读:chmod 400 private/ca.key。加密后的私钥在每次使用时都需要输入密码,虽然麻烦但安全。
3.2 生成服务器双证书
服务器需要一对证书:签名证书和加密证书。它们使用同一个SM2密钥对,但在证书的“密钥用法”扩展上有所不同。
第一步:生成服务器SM2密钥对
gmssl ecparam -genkey -name sm2p256v1 -out server.key第二步:创建证书签名请求(CSR)
CSR包含了公钥和主体信息,需要为签名和加密用途各生成一份。关键在于配置文件中的扩展项。
首先,准备一个配置文件server.cnf:
[ req ] default_bits = 256 distinguished_name = req_distinguished_name req_extensions = v3_req [ req_distinguished_name ] countryName = CN stateOrProvinceName = Beijing localityName = Beijing organizationName = MyServer Inc. commonName = server.example.com # 你的服务器域名或IP [ v3_req ] basicConstraints = CA:FALSE keyUsage = digitalSignature, keyCertSign # 签名证书用法 # 对于加密证书,keyUsage应为 keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [ alt_names ] DNS.1 = server.example.com IP.1 = 192.168.1.100 # 你的服务器IP然后,分别生成两个CSR。注意,我们通过命令行临时覆盖keyUsage来区分它们:
# 1. 生成用于签名的CSR gmssl req -new -key server.key -out server_sign.csr -config server.cnf \ -subj "/C=CN/ST=Beijing/L=Beijing/O=MyServer Inc./CN=server.example.com" # 2. 生成用于加密的CSR。这里通过-addext参数动态添加加密用途的扩展项。 gmssl req -new -key server.key -out server_enc.csr -config server.cnf \ -subj "/C=CN/ST=Beijing/L=Beijing/O=MyServer Inc./CN=server.example.com" \ -addext "keyUsage = keyEncipherment, dataEncipherment"第三步:使用根CA签发证书
现在,用我们自己的根CA来签署这两个CSR。
# 签发签名证书 gmssl ca -config /usr/local/gmssl3/ssl/openssl.cnf -in server_sign.csr \ -out server_sign.crt -days 365 -extensions v3_req \ -cert certs/ca.crt -keyfile private/ca.key # 签发加密证书 gmssl ca -config /usr/local/gmssl3/ssl/openssl.cnf -in server_enc.csr \ -out server_enc.crt -days 365 \ -cert certs/ca.crt -keyfile private/ca.key执行ca命令时,可能会提示你输入根CA私钥的保护密码(如果你之前加密了的话)。还会提示你是否确认签发证书,输入y即可。
至此,你得到了以下关键文件:
server.key:服务器SM2私钥。server_sign.crt:服务器签名证书。server_enc.crt:服务器加密证书。certs/ca.crt:根CA证书。
你可以使用以下命令查看证书的详细信息,验证其类型和有效期:
gmssl x509 -in server_sign.crt -text -noout | grep -A 1 "Key Usage" gmssl x509 -in server_enc.crt -text -noout | grep -A 1 "Key Usage"签名证书的Key Usage应包含Digital Signature,而加密证书应包含Key Encipherment。
4. 在Nginx上配置国密HTTPS服务
证书齐备,现在让我们在Web服务器上启用它们。这里以最流行的Nginx为例。
4.1 安装与编译支持GmSSL的Nginx
CentOS 7默认的yum仓库里的Nginx不支持国密。我们需要下载Nginx源码,并动态链接到我们刚安装的GmSSL库进行编译。
# 回到工作目录,下载Nginx稳定版源码 cd ~/gmssl_build wget https://nginx.org/download/nginx-1.24.0.tar.gz tar -zxvf nginx-1.24.0.tar.gz cd nginx-1.24.0 # 配置编译选项,关键是指定GmSSL的路径 ./configure --prefix=/usr/local/nginx_gmssl \ --with-http_ssl_module \ --with-openssl=/usr/local/gmssl3 \ --with-openssl-opt="--prefix=/usr/local/gmssl3" \ --with-stream \ --with-stream_ssl_module # 编译并安装 make -j$(nproc) sudo make install配置参数解读:
--with-http_ssl_module:启用HTTP SSL模块,这是HTTPS的基础。--with-openssl:指定OpenSSL/GmSSL源码路径。这里指向GmSSL的安装目录,Nginx的编译脚本会自动查找其中的头文件和库。--with-stream_ssl_module:启用TCP流SSL模块,方便后续扩展如国密TCP代理等。
安装完成后,Nginx的可执行文件位于/usr/local/nginx_gmssl/sbin/nginx。同样,建议将其软链接到/usr/sbin/或将路径加入PATH。
4.2 Nginx国密SSL深度配置
接下来是核心步骤:配置Nginx使用SM2双证书。将之前生成的证书和私钥文件(server.key,server_sign.crt,server_enc.crt,ca.crt)拷贝到一个安全的目录,例如/usr/local/nginx_gmssl/conf/sm2_certs/。
然后,编辑Nginx的主配置文件/usr/local/nginx_gmssl/conf/nginx.conf,在http块内添加一个server配置:
http { ... server { listen 443 ssl; server_name server.example.com; # 你的域名或IP # 国密双证书配置 ssl_certificate /usr/local/nginx_gmssl/conf/sm2_certs/server_sign.crt; ssl_certificate_key /usr/local/nginx_gmssl/conf/sm2_certs/server.key; ssl_enc_certificate /usr/local/nginx_gmssl/conf/sm2_certs/server_enc.crt; ssl_enc_certificate_key /usr/local/nginx_gmssl/conf/sm2_certs/server.key; # 指定国密密码套件,优先级从高到低 ssl_ciphers ECDHE-SM2-WITH-SM4-SM3:ECDHE-SM2-WITH-SM4-128-SM3; ssl_prefer_server_ciphers on; # 协议版本设置,建议禁用不安全的旧协议 ssl_protocols TLSv1.2 TLSv1.3; # 会话缓存优化 ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 其他安全头部(可选但推荐) add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"; location / { root html; index index.html index.htm; } } }配置关键点解析:
- 双证书指令:这是国密配置的核心。
ssl_certificate和ssl_certificate_key对应签名证书;ssl_enc_certificate和ssl_enc_certificate_key对应加密证书。注意,它们使用的是同一个私钥server.key。 - 密码套件
ssl_ciphers:这里指定了国密的密码套件。ECDHE-SM2-WITH-SM4-SM3是使用SM2进行密钥交换,SM4进行对称加密,SM3进行消息认证的完整国密套件。ssl_prefer_server_ciphers on;确保服务器端的套件优先级更高。 - 协议版本:
TLSv1.2是支持国密算法的最低安全版本,TLSv1.3也已被GmSSL支持。务必禁用SSLv3和TLSv1.0/1.1。 - 权限:确保Nginx进程用户(通常是
nginx或nobody)有权限读取证书和私钥文件。
4.3 启动、验证与防火墙配置
配置完成后,启动或重载Nginx:
# 测试配置文件语法 sudo /usr/local/nginx_gmssl/sbin/nginx -t # 如果测试成功,启动Nginx sudo /usr/local/nginx_gmssl/sbin/nginx # 如果Nginx已在运行,则重载配置 sudo /usr/local/nginx_gmssl/sbin/nginx -s reload接下来,我们需要验证服务是否真的在国密算法下工作。由于主流浏览器(如Chrome, Firefox)默认不支持国密套件,我们需要使用GmSSL自带的s_client工具进行测试。
# 使用GmSSL的s_client连接本地服务,指定国密套件 echo -n | /usr/local/gmssl3/bin/gmssl s_client -connect localhost:443 \ -cipher ECDHE-SM2-WITH-SM4-SM3 \ -sign_cert server_sign.crt -sign_key server.key \ -enc_cert server_enc.crt -enc_key server.key \ -CAfile ca.crt如果连接成功,你会在输出中看到详细的握手信息,包括“Cipher is ECDHE-SM2-WITH-SM4-SM3”这样的字样,以及证书链的验证结果。
最后,别忘了配置服务器的防火墙,开放443端口:
sudo firewall-cmd --permanent --add-port=443/tcp sudo firewall-cmd --reload5. 客户端适配与国密浏览器测试
服务端搭好了,但用户怎么访问呢?这就需要国密浏览器。常见的如“赢达信”国密浏览器、“红莲花”浏览器等。它们内置了国密算法支持。
客户端测试步骤:
- 导入根CA证书:这是最关键的一步。你必须将自签名的根CA证书(
ca.crt)导入到国密浏览器的“受信任的根证书颁发机构”存储中。否则,浏览器会因不信任你的CA而显示连接不安全。 - 访问HTTPS站点:在浏览器地址栏输入
https://你的服务器IP或域名。 - 查看证书详情:点击地址栏的锁图标,查看证书信息。你应该能看到证书的签名算法是
sm2sign-with-sm3,公钥算法是SM2。这证明你成功建立了国密HTTPS连接。
注意事项:在真实的生产环境中,如果是对公网提供服务,你需要向国家认可的商用CA机构(如CFCA)申请签发的国密服务器证书。自签名证书仅用于测试和内部环境。商用CA的根证书已经预置在国密浏览器中,无需手动导入。
6. 实战排坑指南:从编译失败到连接超时
在这一部分,我汇总了从编译GmSSL到配置Nginx过程中,最可能遇到的几个“坑”及其解决方案。这些都是我亲身踩过,或者社区里反复出现的问题。
6.1 编译与安装类错误
问题1:configure时提示perl: command not found或缺少编译器。
- 原因:系统是最小化安装,缺少必要的开发工具。
- 解决:确保已执行
sudo yum groupinstall -y "Development Tools"和sudo yum install -y perl。
问题2:make编译时失败,报错涉及-m64或某些汇编文件。
- 原因:常见于32位与64位环境混用,或者某些依赖的汇编代码对特定CPU架构支持不佳。
- 解决:尝试在
./config时添加no-asm参数禁用汇编优化:./config --prefix=... no-asm。这可能会降低性能,但能提高兼容性。
问题3:安装后运行gmssl version提示libgmssl.so.x: cannot open shared object file。
- 原因:系统没有找到GmSSL的动态链接库。
- 解决:确保已执行
sudo ldconfig,并且/etc/ld.so.conf.d/gmssl3.conf文件内容正确指向了/usr/local/gmssl3/lib。
6.2 证书生成与签发错误
问题4:使用gmssl ca命令时报错,提示找不到./demoCA/index.txt或无法创建新证书。
- 原因:
openssl.cnf配置文件中的CA路径设置与你的实际目录不符,或者你没有初始化CA目录结构。 - 解决:
- 仔细检查你执行
gmssl ca命令时使用的-config参数指向的配置文件路径。 - 确保你在正确的目录下创建了
index.txt和serial文件。最稳妥的方法是像我之前那样,创建一个独立的CA目录(如~/sm2_ca),并在其中初始化所有文件,然后在该目录下执行签发命令。
- 仔细检查你执行
问题5:生成的证书在浏览器中提示“证书密钥用法不符合要求”。
- 原因:签名证书和加密证书的
Key Usage扩展项设置错误。签名证书必须包含digitalSignature,加密证书必须包含keyEncipherment。 - 解决:严格按照3.2节中的步骤,通过
-addext参数或不同的配置文件,为两种CSR明确指定正确的keyUsage。使用gmssl x509 -in cert.crt -text -noout仔细核对生成证书的“X509v3 Key Usage”字段。
6.3 Nginx配置与连接错误
问题6:Nginx启动失败,报错SSL_CTX_use_enc_certificatefailed。
- 原因:这是最典型的国密双证书配置错误。Nginx在编译时没有正确链接到支持国密双证书接口的GmSSL库,或者配置文件中的
ssl_enc_certificate指令写错了路径。 - 解决:
- 确认Nginx编译链接正确:运行
/usr/local/nginx_gmssl/sbin/nginx -V,查看输出中是否包含--with-openssl=/usr/local/gmssl3。如果没有,说明编译时未链接GmSSL,需要重新编译。 - 检查证书路径和权限:确保
ssl_enc_certificate指令指向的server_enc.crt文件存在,且Nginx进程用户有读取权限。 - 验证证书格式:使用
gmssl x509 -in server_enc.crt -text确认这是一个有效的X.509证书。
- 确认Nginx编译链接正确:运行
问题7:GmSSLs_client测试能通,但国密浏览器无法连接,或显示“不安全的连接”。
- 原因:
- 根证书未导入:这是99%的原因。自签名的CA证书必须手动导入到浏览器的信任库。
- 证书主题不匹配:浏览器访问的域名(或IP)与证书中的
Common Name (CN)或Subject Alternative Name (SAN)不匹配。 - 防火墙/安全组:服务器的443端口未对客户端开放。
- 解决:
- 将
ca.crt文件安全地传输到客户端机器,并详细按照国密浏览器的帮助文档,将其导入到“受信任的根证书颁发机构”。 - 检查服务器证书的SAN字段是否包含了客户端使用的访问地址(DNS名或IP)。可以使用
gmssl x509 -in server_sign.crt -text -noout | grep -A 5 "Subject Alternative Name"查看。 - 在服务器上使用
sudo ss -tlnp | grep :443确认Nginx正在监听443端口,并使用curl -I https://localhost(在服务器本地)或从客户端网络使用telnet命令测试端口连通性。
- 将
问题8:性能问题,HTTPS连接速度慢。
- 原因:SM2算法在某些硬件上的计算效率可能不如优化多年的RSA。此外,没有启用会话复用(Session Resumption)或OCSP装订(OCSP Stapling)也会导致每次握手都进行完整的密钥交换。
- 解决:
- 启用会话缓存:如配置中所示,
ssl_session_cache和ssl_session_timeout已经配置,这能显著提升重复连接的速度。 - 考虑硬件加速:如果对性能要求极高,可以调研支持国密算法硬件加速的SSL加速卡或支持相关指令集的服务器CPU。
- 优化Nginx配置:调整工作进程数(
worker_processes)、连接数(worker_connections)等参数,匹配服务器硬件。
- 启用会话缓存:如配置中所示,
整个搭建过程,从环境准备到最终浏览器访问,是一个环环相扣的系统工程。最容易出问题的环节往往不是编译,而是证书链的信任建立(CA导入)和Nginx与GmSSL的兼容性配置。按照上述步骤,耐心排查,你一定能成功搭建起属于自己的国密HTTPS服务。这不仅是完成一个技术任务,更是对国密标准和技术栈一次深入的理解和实践。