记一下升级国密通信的过程

起手
收到某局接口计划升级国密通信(sm)的需求, 因现有后端使用 php5.4, 便从 php 开始
- 起个 centos:7 的 docker
- Babassl 下载/编译
#!/bin/sh
# prefix防止手滑make install了, 不要养成make install的习惯哦.
# enable-ntls为了以后可能要使用双证书通信, 不用再次编译
./config --prefix=/usr/local/babassl -fPIC -shared enable-ntls
make
- php 编译 (使用了 7.2, 第一个坑)
#!/bin/sh
# 将原有配置拿出来
php -i | grep -i configure
# 指定openssl目录为babassl, 再configure/make
./configure ...
make
一切看着很顺利, 立马写了个测试
$host = '127.0.0.1';
$port = 4433;
$path = '';
$transport = 'tlsv1.3';
$ssl = [
'ssl' => [
'cafile' => $path . 'ca.pem',
'local_cert' => $path . 'your.pem',
'local_pk' => $path . 'your.key',
'verify_peer' => false,
'passphrase' => 'your password',
]
];
$ssl_context = stream_context_create($ssl);
$server = stream_socket_server($transport . '://' . $host . ':' . $port, $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $ssl_context);
print "Server started at $host:$port\n";
// 之后就是正常socket通信...
用 babassl 编译出来的 openssl 测试一下, 没有问题~
./babassl/apps/openssl s_client -connect 127.0.0.1:4433 -tls1_3 -ciphersuites TLS_SM4_GCM_SM3 -pass pass:your_password -key your.key -cert your.pem -CAfile ca.pem

从开始到放弃
在上面测试没有问题之后, 根据业务代码又写了一段测试, 只不过是 stream_socket_server
换成 stream_socket_client
, 添加 ciphers => TLS_SM4_GCM_SM3
啪, 直接 false 无任何报错
打开 wireshark, 在 tcp 握手后, 直接就挥手了. 太棒辣, 从代码里面就开始崩了呢(为啥 server 就能有效呢). 开始排查问题, 跑一下 php -r "var_dump(stream_get_transports());"
, 发现只有 tlsv1.2, 便升级到了 7.4, 查看了下支持 tlsv1.3 了, 再跑一下
啪, 直接 false 无任何报错
Why?? 开 wireshark 也是同样结果, 只能怀疑不能简单的替换 openssl 为 Babassl, php 识别不了 cipher 导致直接退出了, 大概要改 ext/openssl 里面相关代码
换个方向, 写个扩展? 考虑到线上 php 挺旧, 想了一想便放弃了
中间件
俗话说得好, 没有什么加个中间件是解决不了的, 如果有, 再加一个 (喂?!
因为 Babassl 使用的是 C, 自然也就沿用回来了, 准备一下 gcc 环境, 写下代码, 编译成静态库, 起个 go 测试一下~

编译出库
#!/bin/sh
gcc -c demo.c -I../../babassl/include -L../../babassl -fPIC -shared -lssl -lcrypto -ldl
# 编译成静态库给go, 可能线上环境编译不了Babassl的动态库
ar -crv libdemo.a demo.o
go 利用 import "C"
引用 C 代码
/*
#cgo CFLAGS: -I./demo -I../babassl/include
#cgo LDFLAGS: -L./demo -ldemo -L../babassl -lssl -lcrypto -ldl
#include <stdio.h>
#include <stdlib.h>
#include <demo.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
*/
import "C"
// 之后就可以C.<方法/变量>直接使用了, #define的macro无法使用~
代码写完后, 编译即可(使用静态编译)
#!/bin/sh
go build -o ../executable -ldflags '-extldflags "-static" -L ./src/demo'
小坑
// 官方文档客户端只需要现在两行, 后来才在文档的右下角, 很小的一块看见文档是8.2的~
SSL_CTX_set_cipher_list(ctx, "TLS_SM4_GCM_SM3");
SSL_CTX_enable_sm_tls13_strict(ctx);
// 但在8.3.0之后的版本, 强制tls1.3后, 会验证SM2签名算法
// ref: https://github.com/BabaSSL/BabaSSL/commit/61fab104aa51bf47724a169993ff080570dc1316#diff-d31fea45b0617f395f40b055ea91c58e5adb62423cb7afce81720a0a447208b9R1732
// 所以, 需要添加下面一句~
SSL_CTX_set1_groups_list(ctx, "SM2");
如果不加上 SM2 一行, 握手阶段 ServerHello 后, 会报bad key share
.

总结一下
虽然看下来很短, 但也折腾了 2-3 天, 开发机 tcpdump, 本地机开 wireshark, 跑测试, 编译等等...
最后还是万能的中间件, 又因为文档问题浪费大半天. 看源码, 翻遍了文档, 最后在提交更改的 commit 的测试用例找到了 一丝线索
本想向文档提个 pr, 点开 repo, 发现只有两条分支, 还是作罢
估计后面挺多相关接口会换国密通信, 还是小记录一哈
最后, go 真舒服啊~
相关文档
-- Fin --