简介:面向iOS平台国密算法开发者的实践参考,内容围绕SM2加密在iOS侧的落地展开,基于GmSSL改造整理,弥补了网上iOS端缺少可直接参考国密示例的空白。作者在C语言基础较弱、现有实现代码杂乱且缺少注释的条件下反复踩坑,因此特意把源码做了精简,并同时梳理了工程配置,将调试过程中容易出错的判断点和易漏环节保留在相应位置,方便后来者借鉴。资源包共6个文件,包含3个C源码、1个头文件以及2个Visual C++工程文件(dsp/dsw),整体仅12KB,可快速导入查看算法实现与调用方式。目前已有323人浏览学习,特别适合需要在iOS项目中引入国密算法,或者希望理解GmSSL中SM2、SM4实现逻辑的客户端开发者。拿到手后,建议优先阅读源码中的上下文结构与密钥处理流程,工程文件则展示了基本的编译组织方式,便于对照自己的项目做接入调整,从而减少重复排错时间。
1. 为什么 iOS 项目做国密改造时,绕不开 OpenSSL 里的 SM2 与 SM4
做 iOS 客户端的国密改造,多半绕不开三个组合词:SM2、SM4 和 OpenSSL。金融、能源、政务类 App 要求把原来的 RSA+AES 换成国密算法,而 iOS 自带的 Security framework 和 CommonCrypto 既不认 SM2 曲线,也不提供 SM4 分组接口,唯一现实的路就是把 OpenSSL 编进 App,用它的 EVP 接口去调国密算法。于是很多人会去搜类似 SM4.zip 的资源包,希望一次性拿到能在 iOS 上跑起来的源码或静态库。这个包本身不是重点,重点是你拿到之后能不能确认它真的编进了国密模块、能不能在传输层和服务端互通、踩了填充和密文格式的坑之后还愿不愿意继续用下去。这篇文章把这些事一次性讲清楚,新手能照着把 SM2/SM4 在 iOS 上跑通,熟手能直接抄参数和排查思路。
2. 选型与集成:SM2、SM3、SM4 的分工,以及 OpenSSL 怎么进 iOS
2.1 先把三个算法角色分清楚:非对称、摘要、对称
国密算法不是单个算法,是一套组合。SM2 是非对称算法,基于 256 比特椭圆曲线,承担签名、验签、加密解密和密钥交换;SM3 是杂凑算法,输出 256 比特摘要,相当于 SHA-256 的角色;SM4 是对称分组算法,分组长度 128 比特、密钥长度 128 比特,也就是 16 字节,承担业务报文的加解密。三者写进同一个方案里,各自干各自的事:SM2 用来做身份认证和协商临时密钥,SM3 用来做完整性校验,SM4 用来加密真正的大报文。
选型时最常犯的错是拿 SM2 去加密大报文。SM2 加密适合百字节级的数据,比如 32 字节的 SM4 密钥、一段用户身份信息,一旦明文到 KB 级,计算开销和密文膨胀会让移动端体验明显变差。SM2 密文由 C1、C3、C2 三段组成,C1 是椭圆曲线点,至少 65 字节,C3 是 SM3 摘要 32 字节,C2 和明文等长,所以 1KB 明文密文会比明文多出接近 100 字节。这也是混合方案存在的理由:SM2 只传递密钥,SM4 加密业务流。你手里如果有一个经过裁剪的 iOS OpenSSL 库,先用openssl list -cipher-algorithms | grep SM4确认里面有 sm4-cbc,再用openssl list -public-key-algorithms | grep SM2确认 SM2 曲线存在,这两条过了,才值得继续集成。
| 算法 | 类型 | 密钥/参数 | 典型用途 |
|---|---|---|---|
| SM2 | 非对称(256比特椭圆曲线) | 私钥 32 字节 | 签名验签、密钥协商、短数据加密 |
| SM3 | 杂凑,输出 256 比特 | 无 | 完整性校验、SM2 签名摘要 |
| SM4 | 对称分组,分组 128 位 | 密钥 16 字节 | 业务报文加解密 |
为什么优先选 OpenSSL 而不是 GmSSL 或者商密 SDK?OpenSSL 1.1.1 起把 SM2、SM3、SM4 收进了主线代码,你不需要额外找补丁包,社区验证量大,出问题能搜到现成案例。GmSSL 的国密实现更全,但 iOS 场景下多数团队不熟悉它的构建体系。商用密码 SDK 往往绑定特定硬件或 CA 体系,不适合直接嵌进 App。我一般建议:先拿 OpenSSL 1.1.1 的 iOS 静态库跑通全部流程,再评估是否需要换商密库。
2.2 iOS 集成 OpenSSL 的常见做法:源码编译或预编译库,以及国密需要哪些编译开关
iOS 上不能用 Homebrew 直接装 OpenSSL,拿到的 .dylib 也只能在 macOS 上跑。常见做法是下载 OpenSSL 1.1.1 源码,用交叉编译脚本编出libcrypto.a和libssl.a,然后拖进 Xcode 工程。这个过程 Windows 上装了 openssl 之后也没法复用,因为 iOS 的 SDK 和架构体系不同,必须走Configure加交叉编译。
# 以 OpenSSL 1.1.1 源码目录为例,编译 iOS arm64 真机静态库 export SDK_PATH=$(xcrun --sdk iphoneos --show-sdk-path) ./Configure ios64-cross no-shared no-asm \ --prefix=$(pwd)/build/ios-arm64 \ --cross-compile-prefix=$(xcrun --sdk iphoneos --find clang)- \ no-tests no-apps make -j8 make install_sw逻辑说明:ios64-cross是 OpenSSL 源码里针对 iOS 64 位设备的 Configure target,它会自动带上-arch arm64和 iPhoneOS 的 sysroot。no-shared是为了生成.a静态库,App 打包更省事,也避免 iOS 不允许加载动态库的审核问题。no-asm不是必须项,但有些交叉编译环境里汇编优化代码会和 Xcode 的汇编器不兼容,关掉更稳。no-apps是不生成openssl命令行工具,no-tests是不跑测试套件,这两个开关就是网上说的“openssl 轻量版”的关键,能把库体积压缩不少。编完之后会在build/ios-arm64/lib下得到libcrypto.a,libssl.a这个文件 Swift 工程也能直接桥接使用。
参数怎么调?如果你要支持模拟器,就再编一个iossimulator-xcruntarget,架构是x86_64或arm64,最后用lipo -create合成胖二进制。如果 App 里还要用 SM4 的 ECB 模式,不需要额外开开关,默认就带。真正需要留意的是no-asm,某些时候 openssl 升级 windows 或换 Xcode 版本后,汇编代码报undeclared一类错误,关掉汇编就安静了,代价是加解密速度掉一些,但移动端业务报文量级通常感受不到。
2.3 先用 openssl 命令行验证算法,再往 iOS 代码里搬
集成前的自检顺序,我一般分三步。第一步用命令行确认 SM4 加解密能通;第二步确认 SM3 摘要能算;第三步确认 SM2 密钥对能生成。这三步全在 iOS 工程之外完成,能帮你把“算法问题”和“工程集成问题”隔离开。
# 1. SM4-CBC 加解密自检,密钥和 IV 各 16 字节 openssl enc -sm4-cbc -K 0123456789ABCDEF0123456789ABCDEF \ -iv 00000000000000000000000000000000 \ -in plain.txt -out cipher.bin openssl enc -d -sm4-cbc -K 0123456789ABCDEF0123456789ABCDEF \ -iv 00000000000000000000000000000000 \ -in cipher.bin -out decrypted.txt # 2. SM3 摘要 openssl dgst -sm3 plain.txt # 3. 生成 SM2 密钥对 openssl ecparam -genkey -name SM2 -out sm2_pri.pem openssl ec -in sm2_pri.pem -pubout -out sm2_pub.pem命令行里-K和-iv后面跟十六进制字符串,0123456789ABCDEF0123456789ABCDEF正好 32 个 hex 字符,对应 16 字节密钥。SM4 的密钥和 IV 长度都是 16 字节,这是写 iOS 代码时最容易忽略的硬约束,比 AES-128 多一位都不行。openssl dgst -sm3输出的是 64 位 hex 字符串,也就是 32 字节,可以在后续做跨端校验时当作参照。SM2 密钥对用ecparam -genkey -name SM2生成,因为 SM2 本身是椭圆曲线算法,OpenSSL 里归入 EC 体系,曲线名就叫SM2。
这三条命令跑通之后,再进 Xcode 写代码。否则一旦 iOS 侧跑不出来,你分不清是库没编对、头文件没引入,还是算法参数不对。命令行这一步还能当成后端的参照实现,后面做端到端测试时,用同一组密钥和明文分别跑命令行和 iOS 代码,结果一致才算真正通。
3. 用 EVP 接口在 iOS 上跑通 SM4 加解密:最小代码与参数讲解
3.1 SM4-CBC 加密的最小 Objective-C 代码
EVP 接口是 OpenSSL 的统一加密入口,SM4 只是其中一个 cipher,写法上跟 AES 几乎没有区别。下面的代码可以直接放到一个 iOS 工具类里,头文件引入<openssl/evp.h>和<openssl/sm4.h>,但实际调用只需要 EVP 接口,不需要直接操作 SM4 的结构体。
- (NSData *)sm4CBCEncrypt:(NSData *)plainText key:(NSData *)key iv:(NSData *)iv { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); int outLen = 0, finalLen = 0; // 密文最大长度 = 明文长度 + 一个分组长度 NSMutableData *cipher = [NSMutableData dataWithLength:plainText.length + EVP_MAX_BLOCK_LENGTH]; EVP_EncryptInit_ex(ctx, EVP_sm4_cbc(), NULL, key.bytes, iv.bytes); EVP_EncryptUpdate(ctx, cipher.mutableBytes, &outLen, plainText.bytes, (int)plainText.length); EVP_EncryptFinal_ex(ctx, cipher.mutableBytes + outLen, &finalLen); EVP_CIPHER_CTX_free(ctx); cipher.length = outLen + finalLen; return cipher; }逻辑说明:EVP_EncryptInit_ex第三个参数传NULL表示采用默认实现,EVP_sm4_cbc()是 OpenSSL 1.1.1 起提供的 cipher 工厂函数,如果静态库里没编 SM4,链接阶段会直接报EVP_sm4_cbc符号找不到。EVP_EncryptUpdate处理分段明文,outLen是本次写入的密文长度。EVP_EncryptFinal_ex负责处理尾部填充,返回值要加到最终长度上。
参数说明:SM4-CBC 的key和iv都必须是 16 字节,多字节或少字节会在EVP_EncryptInit_ex阶段报错。填充默认是 PKCS#7,也就是说明文不足 16 字节倍数时自动补齐,解密端也必须用同样的填充规则。如果服务端用 Java 或 .NET 实现,一定要确认对方用的是 PKCS7Padding 还是 PKCS5Padding,SM4 分组是 16 字节,两者在这儿可以等价,但实现细节略有差别。解密函数写法几乎一样,把Encrypt换成Decrypt即可。注意EVP_DecryptFinal_ex返回 0 时说明填充校验失败,常见原因是密钥、IV 或密文被改动过,这是排查“iOS 解不开服务端数据”的第一检查点。
3.2 SM2 加密为什么必须走 EVP_PKEY_CTX,而不是简单 Encrypt
SM2 加密跟 RSA 的 EVP 写法不是一回事。SM2 虽然也走EVP_PKEY_encrypt,但它需要额外的上下文设置:把密钥别名改成 SM2 类型,设置用户 ID,而且输出不是简单的“密文比明文长一点”的字节流。看下面的代码,注意EVP_PKEY_CTX_set_group_name这一步就是很多人漏掉后报operation not supported的根源。
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL); if (!ctx) return; // OpenSSL 1.1.1 需要用这行把 EC 密钥声明为 SM2 类型 EVP_PKEY_set_alias_type(pkey, EVP_PKEY_SM2); if (EVP_PKEY_encrypt_init(ctx) <= 0) { EVP_PKEY_CTX_free(ctx); return; } // 用户 ID,默认是 1234567812345678,建议显式声明 unsigned char uid[] = "1234567812345678"; EVP_PKEY_CTX_set1_id(ctx, uid, 16); size_t outLen = 0; EVP_PKEY_encrypt(ctx, NULL, &outLen, plaintext, plaintextLen); // 这里 outLen 会返回密文所需长度,再分配内存调用第二次逻辑说明:EVP_PKEY_set_alias_type是 1.1.1 的特殊要求。OpenSSL 把 SM2 归入 EC 大类,如果不显式声明,EVP_PKEY_encrypt_init只会把它当成普通 ECDSA 密钥,直接报不支持加密。OpenSSL 3.0 之后不再建议用别名函数,而是推荐在EVP_PKEY_CTX_new_from_name阶段就指定SM2,但 1.1.1 仍然广泛用于 iOS 静态库,这段代码的兼容性更实用。EVP_PKEY_CTX_set1_id设置的是 SM2 签名和加密时用的用户 ID,国家标准里默认值是1234567812345678,但服务端如果设置过自定义 ID,这里必须一致,否则验签和解密全挂。
SM2 加密输出不是裸密文,而是一个由 C1、C3、C2 三段组成的结构。C1 是椭圆曲线点,未压缩形式是04 || x || y,长度 65 字节;C3 是 SM3 摘要,32 字节;C2 是密文主体,和明文等长。OpenSSL 的EVP_PKEY_encrypt输出的正是 C1||C3||C2 拼接结果,不需要你手动处理。跨端对接时,部分 Java 库默认输出的是 C1||C2||C3,顺序不一样会导致 iOS 端解密失败,这个坑留到第 4 章详细展开。
3.3 混合方案:SM2 协商密钥 + SM4 加密业务报文
实际业务不会用 SM2 直接加密整条业务报文,除非报文只有几百字节。我经手的客户端改造基本都采用混合方案,流程如下表。
| 阶段 | 算法 | 作用 |
|---|---|---|
| 握手 | SM2 签名 + 验签 | 客户端证明身份 |
| 密钥协商 | SM2 加密 / 解密 | 客户端生成临时 SM4 密钥,用服务端公钥加密传输 |
| 业务传输 | SM4-CBC | 密文传输业务数据 |
| 完整性校验 | SM3 | 对关键字段生成摘要,防止篡改 |
这种设计的好处是 SM4 密钥每次会话都变,就算某次通信被抓包,下次密钥也换了。客户端只需要持有 SM2 私钥和服务端 SM2 公钥,服务端持有自己的私钥和客户端公钥,不需要额外的证书体系。密钥协商时,客户端生成 16 字节随机数作为 SM4 密钥,用服务端公钥做 SM2 加密后传过去,服务端解出 SM4 密钥,后续所有业务报文都用 SM4-CBC 加解密。
我这里要提醒一个实现细节:SM4 密钥的生成要用SecRandomCopyBytes或RAND_bytes,不要用NSData randomDataWithLength这类可能基于系统时间种子的生成器。密钥协商的安全强度完全依赖随机数质量,国密改造如果随机源不过关,算法选得再好也白搭。iOS 上推荐直接用 OpenSSL 的RAND_bytes,它底层会走 Secure Enclave 的熵源,省心且合规。
4. iOS 国密改造避坑:OpenSSL 版本、填充与跨端互通的 5 个高频故障
4.1 现象:openssl 命令行能加解密,iOS 代码却解不出来
openssl enc -sm4-cbc跑通,说明算法本身没问题,但 iOS 代码解出来全是乱码或直接返回失败。原因几乎都是填充模式不一致。命令行默认按 PKCS#7 填充,而业务方如果自研了无填充实现,或者 Java 端用了NoPadding,两边对不齐。另一个常见情况是密钥或 IV 的字节序被转了一次字符串,比如服务端把 16 字节 key 用 UTF-8 转成字符串再 Hex 编码,长度变成 44 字节,iOS 端直接解码就错了。解决方法是先用一段 16 字节定长数据做联调,两边把 key、iv 打印成 hex 逐字节比对,再讨论填充策略。我习惯在联调文档里明确写三件事:密钥 16 字节、填充 PKCS#7、密文 Base64 传输。
4.2 现象:编译报错rsa_sslv23_paddingundeclared
这个错误在 OpenSSL 升级或更换 SDK 时特别常见。报错信息里带openssl/rsa.h和undeclared,很多人以为是自己代码写错了,其实是头文件和库版本不匹配。OpenSSL 1.1.0 起删除了大量旧版宏定义,如果你下载的 openssl 包是 1.0.2 时代的东西,或者把不同版本的头文件和.a混用了,就会在编译期报这个错。解决方法是统一版本:源码编译时 Configure 脚本的 prefix 目录别跟系统/usr/local混在一起,工程里 Header Search Path 和 Library Search Path 都指向你自己编译的build/ios-arm64目录,不要用系统自带的 openssl 头文件。另外,某些网上打包好的 SM4.zip 资源包里,.a是 1.1.1 编的,头文件却是 1.0.2 的,这种混搭最容易触发该报错。
4.3 现象:SM2 加密结果每次都不一样
第一次用 SM2 加密同一段数据,发现两次输出完全不同,有人会怀疑是随机数出问题或者算法实现有 bug。这不是 bug。SM2 加密算法里每一步都会生成随机椭圆曲线点 C1,随机点不同,最终密文就不同。服务端解密端只要能正确解密,就说明算法工作正常。真正要测的是“解密还原”,而不是“密文一致性”。做自动化测试时,不要断言两次 SM2 加密结果相等,而是要断言两次解密结果相等。这也是 SM2 和 RSA 加密直观感受上的最大差异,很多第一次接触国密的开发都会被这个吓到。
4.4 现象:Android/iOS 跨端互通时 SM2 解密失败
App 客户端同时有 Android 和 iOS 时,两边各调各的国密库,最常遇到的现象是 iOS 端用 OpenSSL 加密的数据,Android 端解不开,或者反过来。根本原因是 SM2 密文的组包格式不同。国家标准里 SM2 密文格式允许 C1C2C3 和 C1C3C2 两种顺序,OpenSSL 用的是 C1C3C2,部分 Java 库用 C1C2C3,还有的库把 C1 存成 ASN.1 DER 编码而不是裸字节。解决方法是约定一种标准的线上格式,我一般建议统一成C1||C3||C2,C1 用未压缩点格式,也就是04开头、共 65 字节,整体再从字节流做 Base64。同理,SM4 解密失败时先检查两边 CBC 的 IV 是否一致,很多 SDK 会把 IV 拼到密文头里,而 OpenSSL 的 EVP 接口不会帮你自动拆。
4.5 现象:iOS 包体因为 OpenSSL 变大,审核被问
OpenSSL 全量静态库体积不小,arm64 真机加 arm64 模拟器合成胖二进制后,通常能把 App 撑大 10MB 以上。解决方法是裁剪编译选项。前面第 2 章的 Configure 命令里,no-tests no-apps能砍掉命令行和测试代码,再加no-ssl no-tls1之类可以进一步去掉 TLS 协议栈,如果 App 只需要国密加解密,甚至可以不编libssl.a,只留libcrypto.a。我见过一个金融 App 只用了 SM2 签名和 SM4 加解密,裁剪后libcrypto.a从 8MB 降到 3MB 左右。另外注意,iOS 延迟升级到新系统或者换 Xcode 后,如果旧的.a里没有包含 arm64e 切片,部分新机型会在启动时崩溃,重新编译一次即可解决。这属于“后悔药”级别的常规操作,不用慌,但要提前安排进发版流程。
5. 端到端业务落地:SM2 签名、SM4 加密通信与联调参数
5.1 在 iOS 里做 SM2 签名与验签
签名是国密改造里最先落地的功能,登录、支付等关键操作都要求客户端用 SM2 私钥签名。OpenSSL 的 EVP 接口对 SM2 签名的写法比较直接,注意摘要算法必须用 SM3。
- (NSData *)sm2Sign:(NSData *)data withPrivateKey:(EVP_PKEY *)pkey { EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); EVP_PKEY_set_alias_type(pkey, EVP_PKEY_SM2); // SM2 签名的摘要算法必须与 SM3 绑定 EVP_DigestSignInit(mdctx, NULL, EVP_sm3(), NULL, pkey); unsigned char uid[] = "1234567812345678"; EVP_PKEY_CTX_set1_id(EVP_MD_CTX_get_pkey_ctx(mdctx), uid, 16); EVP_DigestSignUpdate(mdctx, data.bytes, data.length); size_t sigLen = 0; EVP_DigestSignFinal(mdctx, NULL, &sigLen); NSMutableData *sig = [NSMutableData dataWithLength:sigLen]; EVP_DigestSignFinal(mdctx, sig.mutableBytes, &sigLen); sig.length = sigLen; EVP_MD_CTX_free(mdctx); return sig; }逻辑说明:EVP_DigestSignInit的第三个参数传EVP_sm3(),指定签名摘要算法。SM2 签名标准要求先做 SM3 摘要再签名,如果这里传了EVP_sha256(),签名能算出来但服务端验签一定失败。EVP_PKEY_CTX_set1_id设置用户 ID,签名和验签必须用同一个 ID,否则同样失败。EVP_DigestSignFinal第一次调用返回签名长度,第二次调用写入签名数据,这是 OpenSSL EVP 接口的固定两段式写法。
验签的代码几乎是对称的,EVP_DigestVerifyInit加EVP_DigestVerifyUpdate加EVP_DigestVerifyFinal,只需要把私钥换成公钥。需要注意签名输出的格式:OpenSSL 输出的 SM2 签名是 DER 编码的r || s结构,某些后端库会把 r、s 分开拼接成 64 字节,跨端时要确认好格式再联调。密钥加载用的是PEM_read_bio_PrivateKey,跟 RSA 私钥加载方式相同,只是内部曲线是 SM2。
5.2 与服务端联调:绕不开的 ID、C1C3C2 顺序与 Base64
联调时最容易出问题的不是算法本身,而是两边的参数没有对齐。我在这里列出联调前必须确认的五个参数,每一条都对不上就等着翻车:第一,SM2 用户 ID 字符串,默认是1234567812345678,但很多业务方会自定义;第二,SM2 密文格式,统一用C1||C3||C2,不要用 ASN.1 封装;第三,C1 是否带04前缀,OpenSSL 输出带,部分库会去掉,接收端要统一处理;第四,SM4 填充模式,全部用 PKCS#7;第五,传输层编码,密文统一 Base64,不要在代码里偷懒用 hex。
如果你看到“netcore 国密 sm2 解密”这类需求,本质上是同一套参数在 .NET 侧实现有差异。.NET 的 BouncyCastle 和 OpenSSL 对 SM2 密文结构体定义不太一致,OpenSSL 输出的是内存连续结构,而 BouncyCastle 的SM2Cipher里 C1、C3、C2 是分开的三段,需要手动拼接。联调时我习惯先不做 Base64,直接用文件交换二进制的明文和密文,一步步确认每个阶段输入输出是否一致,最后再切到线上传输格式。
5.3 端到端验证:命令行、iOS、服务端三方对齐
端到端验证不是简单把 iOS 加密的数据发给服务端,用服务端解密成功就完了。我做国密联调时,至少分三组对照测试。第一组:固定 key、IV、明文,用命令行和 iOS 分别跑 SM4 加密,比对密文 hex 是否完全一致。第二组:用同一对 SM2 密钥和同一段明文,命令行生成密文,iOS 解密;iOS 生成密文,命令行解密,双向验证。第三组:服务端解密之后,对解出的 SM4 密钥重新做一次签名,iOS 验签通过,确认整个链路没有中间人改包。
抓包验证这一步也很关键。iOS 连上代理抓一次请求,重点看传输层字段:SM4 密文在每次请求里是否都变化,SM2 签名是否每次都不同,这两个“变化”恰恰说明算法正常。如果抓到的密文固定不变,说明那边用的是硬编码 key,不是随机协商的,这种实现就算算法合规也谈不上安全。我一般还会在代码里临时打一条日志,把协商出的 SM4 密钥打印出来,联调完再关掉,虽然不合规,但确实是排查跨端问题最快的办法。
6. 上线前的最后一件事:用测试向量和性能数据守住生产底线
国密改造做完功能,别急着提测,先用标准测试向量验证一遍实现正确性。SM4 的标准向量可以在 GB/T 32907 里找到,固定密钥0123456789ABCDEFFEDCBA9876543210,固定明文,加密结果按标准比对。SM2 的签名验签可以用openssl dgst -sm3 -sign生成参考签名,再在 iOS 端验签。这些测试向量比联调数据更可靠,因为联调时服务端有可能和你错得一致,标准向量则不会骗人。
性能验证按真实业务量级来。我测过一个低配 iPhone 上跑 SM2 签名,约 2 到 5 毫秒一次,SM4 加解密 1KB 数据约 10 微秒级别,瓶颈基本在网络和序列化上。如果业务方要求 SM4 每请求都重新协商密钥,性能点会落在 SM2 加密而不是 SM4 上,这时候要评估是否需要复用会话密钥。回归测试里加一条“密文不一致但解密成功”的断言,确保以后没人误改随机数逻辑。上线前再检查一遍静态库是否包含模拟器切片,避免 CI 用模拟器跑测试时链接失败。
我现在的习惯是:每改一次国密相关代码,先把openssl dgst -sm3的命令行结果记到测试备注里,再跑 iOS 用例。单测可以骗人,标准向量不会。国密改造最容易出问题的从来不是算法本身,而是两端把“默认值”当成“约定值”。所有参数都显式声明、逐项对齐,这趟改造才算真正落地。希望帮到你。
本文还有配套的精品资源,点击获取