部署国密证书时遇到 OpenSSL 版本不支持的问题,核心原因是Nginx、Apache 等 Web 服务器自带的 OpenSSL 库是国际标准版,不包含对国密算法(SM2/SM3/SM4)的支持。
解决这个问题,不能仅仅升级系统自带的 OpenSSL,而是要替换成支持国密算法的 OpenSSL 分支(如 GmSSL、铜锁/Tongsuo),并基于这个新库重新编译你的 Web 服务器(如 Nginx)。
下面是完整的排查与升级指南。
第一步:版本检查与问题定位
首先,确认你的环境确实存在国密支持问题。请执行以下检查:
1. 检查当前 OpenSSL 版本
```bash
openssl version -a
```
关键点:查看输出中是否包含 `GMSSL`、`Tongsuo` 等字样。如果没有,说明你用的是国际版 OpenSSL,不支持国密。
版本陷阱:即使版本号是 `1.1.1` 或更高,也不代表支持国密。例如,`OpenSSL 1.1.1k` 虽然后续版本在理论上支持,但实际使用中可能因缺少 `sm2p256v1` 曲线的 OID 映射而报错。
更早的版本如 `OpenSSL 1.0.2` 则是完全不支持国密,必须升级。
2. 检查 Web 服务器 (以 Nginx 为例) 链接的 OpenSSL
```bash
nginx -V 2>&1 | grep -i openssl
```
关键点:查看 `built with OpenSSL` 后面的路径。如果指向 `/usr/lib`、`/usr/local/ssl` 等系统目录,说明 Nginx 链接的是系统自带的国际版 OpenSSL。
3. 识别常见报错信息
Nginx 启动失败:报错 `SSL_CTX_use_certificate failed`。
OpenSSL 命令报错:执行 `openssl x509 -in <证书文件> -text -noout` 时,出现 `Unknown named curve` 或 `unable to load certificate` 等错误。
CSR 生成失败:申请证书时,用 `openssl ecparam -name sm2p256v1` 生成 SM2 密钥后,生成的 CSR 签名算法不符合国密标准(应使用 `sm2sign-with-SM3`)。
第二步:解决方案(升级与重新编译)
确认是 OpenSSL 不支持国密后,请按以下步骤操作。
方案一:编译安装国密版 OpenSSL(推荐 GmSSL 或 铜锁/Tongsuo)
这是最核心的一步,你需要下载、编译并安装一个支持国密的 OpenSSL 分支。
1. 下载源码:从 GmSSL或铜锁/Tongsuo的官方仓库或网站下载源码包。
2. 编译安装:以 GmSSL 为例,通常的安装步骤是解压、配置、编译和安装。
```bash
# 示例:解压并编译安装 GmSSL
tar -xzf gmssl_openssl_xxx.tar.gz
cd gmssl_openssl_xxx
./config --prefix=/usr/local/gmssl # 建议安装到独立目录,避免与系统版本冲突
make
make install
```
3. 验证安装:安装后,用绝对路径执行版本检查,确认输出包含国密标识。
```bash
/usr/local/gmssl/bin/openssl version -a
```
方案二:基于国密 OpenSSL 重新编译 Web 服务器(以 Nginx 为例)
有了国密版 OpenSSL,你需要用它来重新编译你的 Web 服务器。
1. 获取 Nginx 源码:下载与当前生产环境相同或相近版本的 Nginx 源码。
2. 修改编译配置:
进入 Nginx 源码目录,编辑 `auto/lib/openssl/conf` 文件。
将文件中类似 `$OPENSSL/.openssl/include` 的路径,修改为 `$OPENSSL/include`(即去掉 `.openssl`)。
3. 配置并编译:
```bash
./configure --prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-openssl=/usr/local/gmssl \ # 指向你刚安装的国密 OpenSSL 源码或安装目录
--with-cc-opt="-I /usr/local/gmssl/include" \ # 指定头文件路径
--with-ld-opt="-L /usr/local/gmssl/lib" # 指定库文件路径
make && make install
```
4. 验证新 Nginx:编译安装后,再次运行 `nginx -V`,确认 `built with OpenSSL` 路径已指向你的国密版 OpenSSL 目录。
方案三(备选):使用铜锁/Tongsuo 处理库共存问题
如果你的环境复杂,同时依赖了国际版和国密版 OpenSSL,可能会遇到符号冲突等问题。此时可以考虑使用铜锁/Tongsuo。它在设计上更好地考虑了与 OpenSSL 的共存问题,可以作为过渡方案。
第三步:常见错误与排查
1. `SSL_CTX_use_certificate failed`
原因:Web 服务器底层的 OpenSSL 库不支持国密算法。
解决:请严格按照方案一和方案二操作,重新编译 Web 服务器。
2. `Unknown named curve` / `unable to load certificate`
原因:OpenSSL 无法识别 SM2 算法使用的椭圆曲线 `sm2p256v1`。
解决:确保你使用的 `openssl` 命令来自国密版 OpenSSL 的安装目录(如 `/usr/local/gmssl/bin/openssl`),而不是系统的国际版。
3. `undefined reference to 'EVP_sm4_cbc'`
原因:编译 Nginx 时,链接器没有正确链接国密版 OpenSSL 的库文件。
解决:在 `./configure` 阶段,确保 `--with-ld-opt` 参数正确指定了国密版 OpenSSL 的库路径(如 `-L /usr/local/gmssl/lib`)。
第四步:验证与测试
部署完成后,务必进行验证。
1. 命令行验证:使用国密版 OpenSSL 的 `s_client` 工具测试与服务器的国密握手。
```bash
/usr/local/gmssl/bin/openssl s_client -connect your.domain.com:443 -cipher ECDHE-SM2-SM4-CBC-SM3
```
2. 浏览器验证:使用支持国密的浏览器(如密信浏览器、零信浏览器等)访问你的网站,查看证书信息是否正确,小锁图标是否安全。
3. 检查证书链:确保服务器配置的证书链完整,顺序为“服务器证书 → 中间证书 → 根证书”。
总结
部署国密证书的核心思路是:用“国密版OpenSSL”替换“国际版OpenSSL”,并基于前者重新编译Web服务器。这不是一次简单的软件升级,而是一次针对密码库和应用程序的深度改造。请务必按照上述步骤,耐心操作并验证。