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

openssl ciphers

起手

收到某局接口计划升级国密通信(sm)的需求, 因现有后端使用 php5.4, 便从 php 开始

  1. 起个 centos:7 的 docker
  2. Babassl 下载/编译
#!/bin/sh
# prefix防止手滑make install了, 不要养成make install的习惯哦.
# enable-ntls为了以后可能要使用双证书通信, 不用再次编译
./config --prefix=/usr/local/babassl -fPIC -shared enable-ntls
make
  1. 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
anya wakuwakuwakuwaku

从开始到放弃

在上面测试没有问题之后, 根据业务代码又写了一段测试, 只不过是 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 测试一下~

babassl init ssl

编译出库

#!/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.

ssl handshake failbad key share

总结一下

虽然看下来很短, 但也折腾了 2-3 天, 开发机 tcpdump, 本地机开 wireshark, 跑测试, 编译等等...

最后还是万能的中间件, 又因为文档问题浪费大半天. 看源码, 翻遍了文档, 最后在提交更改的 commit 的测试用例找到了 一丝线索

本想向文档提个 pr, 点开 repo, 发现只有两条分支, 还是作罢

估计后面挺多相关接口会换国密通信, 还是小记录一哈

最后, go 真舒服啊~

相关文档

-- Fin --