SDK架构设计:模块划分与功能定义
做合规治理SDK,说白了就是把一堆密码算法、密钥管理、日志审计、合规策略这些东西,拧成一个好用的工具包。我刚开始接手SM4集成时,第一反应是——“直接调库不就完了?”后来才发现,如果没有清晰的模块划分,后面维护起来简直是噩梦。
这一章,我们就来聊聊怎么给这个SDK“切蛋糕”。不搞花架子,直接上架构。
整体架构分层
我个人习惯把SDK分成三层:
- 接口层:对外暴露API,用户调用的入口。要求简单、稳定、文档清晰。
- 核心层:真正的干活模块。SM4加密/解密、密钥派生、模式选择、填充处理。这部分代码要高度内聚,尽量不依赖外部库。
- 治理层:合规相关逻辑。密钥生命周期管理、安全策略检查、日志审计、符合国密标准(GM/T 0002、GM/T 0003等)的校验。这部分是我踩坑最多的地方。
你想想看,如果把治理逻辑散落在核心算法里,到时候改一条合规要求,得把整个SDK翻一遍。所以分层一定要清晰。
架构原则: 接口层只做参数校验和转发,核心层只做密码运算,治理层做所有跟「合规」有关的决策。三层之间通过定义好的回调接口或上下文结构体通信。
模块划分:六个核心模块
根据我在几个项目中迭代出来的经验,SM4合规SDK至少需要以下六个模块。我用一个表格来说明每个模块的职责和关键点。
| 模块名称 | 核心功能 | 个人避坑提醒 |
|---|---|---|
| SM4引擎 | 实现SM4加密/解密,支持ECB、CBC、CTR、GCM等模式,以及PKCS7/PKCS5填充 | GCM模式下认证标签长度必须固定为16字节,否则很多合规检测会挂。我以前吃过这个亏。 |
| 密钥管理 | 生成、导入、导出、更新、销毁密钥;支持密钥层级结构(主密钥/工作密钥) | 密钥销毁时一定要做内存擦除(memset_s),否则敏感数据可能残留。 |
| 模式选择器 | 根据业务场景自动选择最安全的密码模式,并提供手动覆盖接口 | 别让用户随便选ECB模式——ECB在合规检测里通常是高危。我建议默认强制CBC或GCM。 |
| 合规校验器 | 校验密钥强度、算法参数、随机数质量、模式安全性是否符合国密标准 | 随机数生成必须用硬件TRNG或国密标准的DRBG,不能用rand()。 |
| 日志审计 | 记录加密/解密/密钥操作的时间、结果、用户ID、设备指纹,支持导出 | 日志不能包含明文密钥!我曾经见过有同事把密钥base64后记到日志里…… |
| 适配桥接 | 适配不同操作系统(Linux/Windows/RTOS)、不同硬件平台(x86/ARM/RISC-V)的底层密码接口 | 桥接层要抽象出统一的底层接口,否则换一个硬件平台就要改核心代码。 |
模块间协作关系
光有模块还不够,得知道它们怎么配合。我画了一张结构图帮你理解数据流:
设计小技巧: 六个模块都通过「抽象接口」与API层交互。这样如果以后要换底层SM4实现(比如从软实现换成硬件加速),只需要改SM4引擎模块的内部实现,API层和其他模块完全不用动。我在实际项目中这么搞,换过一次硬件平台,两天就搞定了。
功能定义:接口粒度要细
功能定义的时候,最容易犯的错误是把接口做得过于「万能」。举个例子:
// 反面教材——一个函数做所有事
int sm4_do_everything(
int operation, // 0加密 1解密 2密钥生成 ...
const uint8_t *key,
const uint8_t *iv,
const uint8_t *in, size_t in_len,
uint8_t *out, size_t *out_len,
int mode, // 0 ECB 1 CBC ...
int padding // 0无 1PKCS7
);
这种接口看着好像「灵活」,但谁用谁想骂。参数太多、语义模糊、容易传错。我建议拆分成多个细粒度函数:
// 加密接口
int sm4_cbc_encrypt(const sm4_ctx_t *ctx,
const uint8_t *plain, size_t plain_len,
uint8_t *cipher, size_t *cipher_len);
// 解密接口
int sm4_cbc_decrypt(const sm4_ctx_t *ctx,
const uint8_t *cipher, size_t cipher_len,
uint8_t *plain, size_t *plain_len);
// 密钥生成
int sm4_keygen(sm4_ctx_t *ctx, const uint8_t *seed, size_t seed_len);
重要提醒: 每个函数都要做NULL指针和长度校验,别指望调用方传的都是合理参数。我曾经在一个项目里因为没检查输入指针为空,导致线上模块直接崩溃——那晚的on-call电话,你懂的。
治理层的特殊设计
治理层是SDK的「门卫」。每次密码操作之前,它都要问三个问题:
- 密钥合规吗? 密钥长度是否128位?是否来自合法的密钥源?有没有过期?
- 算法参数合规吗? 模式是否在白名单里?IV长度是否正确?填充方式是否符合要求?
- 环境合规吗? 随机数质量是否达标?日志是否已开启?是否处于安全模式?
把这些检查逻辑统一放在合规校验器里,其他地方完全不用操心。这是我从一次合规审计中悟出来的——原来审计老师只看这一个模块就能判断整个系统是否合规。所以治理层的代码质量,某种意义上决定了你SDK的「命」。
治理层关键指标: 每次密码操作的合规检查耗时控制在1ms以内(纯软件环境)。如果超过这个值,说明你有不必要的全量扫描,应该考虑缓存或增量校验。
模块间的数据流
最后,我列一下典型的数据流顺序。这对你搭建实际代码架构很有帮助:
- 用户调用API(如
sm4_encrypt)。 - API层解析参数,调用合规校验器。
- 合规校验器检查密钥是否有效、模式是否允许、环境是否安全。
——如果失败,直接返回错误码,日志审计模块记录失败事件。 - 合规通过,API层调用密钥管理模块获取实际密钥。
- 密钥管理返回工作密钥(如果是层级结构,需要先解密主密钥得到工作密钥)。
- API层拿着密钥和参数,调用SM4引擎执行加密。
- SM4引擎运算完成,返回结果。
- API层调用日志审计模块,记录成功操作。
- 最终结果返回给用户。
这个流程里,每个步骤都可能被适配桥接层拦截——比如在RTOS上,很可能没有文件系统来存日志,这时候适配桥接就要把日志写入环形缓冲区或者通过串口输出。
我的习惯: 在开发初期,先把适配桥接层做成一个空函数(直接返回成功),等核心逻辑测试通过后再实现具体的平台适配。这样能先验证算法和治理逻辑的正确性,再处理平台差异。不然两个事情搅在一起,debug会非常痛苦。
好了,模块划分和功能定义就聊到这儿。记住:好的SDK架构就像一栋房子的梁柱,后面所有的工作都在这上面搭建。设计阶段多想10分钟,编码阶段就能省下10小时。