3. 加密SDK架构设计:模块划分、核心组件与API设计原则
这一章,我们来聊聊加密SDK的骨架。
前面几章我们知道了为什么要做字段级加密,也了解了整体升级流程。但说到底,SDK内部到底长什么样?我刚开始接触这类SDK时,也踩过不少坑。最典型的一个:把所有加密逻辑都塞在一个类里,结果上线一周就发现,想换加密算法都得改核心代码,测试量巨大。
后来我才明白,一个好的加密SDK,架构一定要清晰。说白了,就是要把「变」的部分和「不变」的部分拆开。
3.1 整体架构:三层两通道模型
我自己习惯把一个字段级加密SDK分为三层。你想想看,上层是业务方调用的API,中间是核心引擎处理加解密逻辑,底层是密钥管理和数据持久化。这三层之间通过两个通道交互:一个负责数据,一个负责元数据。
嗯,还得考虑网络不通怎么办。所以本地缓存是必须的。
你任何一个模块的修改,都不应该导致其他模块跟着改动。这听起来简单,但能做到的SDK其实不多。
下面这张图,就是我逐渐打磨出来的一套架构。它在我参与过的几个项目里都跑得很稳,你可以做个参考。
3.2 三层架构详解
3.2.1 API接入层
这一层是SDK的门面。我做过的项目里,调用方式五花八门:有Java直调、有走HTTP网关的、有通过Spring Boot Starter自动装配的。如果每一套都写一套独立逻辑,维护成本会直接爆炸。
解决办法是什么?统一抽象一个核心接口。这一层只做一件事:接收外部请求,校验参数格式,然后转成内部的数据结构,交给下一层处理。
3.2.2 核心引擎层
这一层是真正的「大脑」。它不关心你从哪来,也不关心你的密钥怎么存。它只用来执行加密、解密、重加密这些操作。
里面有几个关键模块,我一个个说。
- 算法路由分发器: 根据元数据决定用AES还是SM4,用哪种填充模式。说白了,它就是根据「你是谁」来选择「怎么办」。
- 版本兼容处理器: 这个我单独拎出来说。升级SDK后,旧版本加密的数据怎么处理?我遇到过一个大坑:客户端升级后,发现读不到旧密文了。后来我才加上版本协商逻辑,当检测到旧版本密文时,走一回兼容解密链路。
- 异常与降级策略: 加密服务挂了怎么办?我们团队的规定是:加密失败必须抛异常,不能静默返回明文;但解密失败可以降级(配置降级策略),保证业务连续性。
3.2.3 存储与密钥层
这一层是基石。底层做不好,上层再漂亮也是白搭。
密钥管理模块是最敏感的。我建议密钥从不落盘——除非你用的是HSM(硬件安全模块)做本地加密。像我们团队,密钥全都通过远程密钥服务获取,且每次获取都要校验签名。本地只缓存加密后的密钥片段,不缓存明文密钥。
总结下来, 数据持久化层的核心职责包括:
- 存储加密后的字段密文(通常是VARCHAR(MAX)类型)
- 存储元数据版本号字段(比如一个表字段带一个版本标签)
- 存储密钥的唯一标识(主密钥ID)
3.3 API设计原则:六字真言
接口设计这块,我有一套很简单的原则,就六个字:「对称、幂等、降级」。
| 原则 | 说明 | 我的实践 |
|---|---|---|
| 对称 | 加密和解密的接口参数结构必须一致 | encrypt(data) → decrypt(data) 不用调整参数顺序或类型 |
| 幂等 | 同样的入参,重复调用结果不变 | 特别是重加密接口,必须保证多次执行后密文一致 |
| 降级 | 异常时有明确的回退策略 | 我习惯在接口文档里写明:哪些异常抛给调用方,哪些内部吞掉走降级 |
3.4 元数据与版本管理机制
这块是架构里容易被忽视的。其实元数据管理好了,版本升级才能平滑。
元数据里存什么?最少要包含:字段标识、加密算法类型、密钥ID、版本号、是否启用。每次SDK升级,版本号跟着变。解密时,根据元数据里的版本号,自动选择对应的解密逻辑。什么叫自动化升级?这就是关键。
核心观点: 字段级加密SDK的架构设计,本质上是在做「依赖倒置」。上层依赖的是抽象层(核心引擎的接口),不是具体实现。只要你遵循这个原则,版本升级就是替换一个模块的事情,不需要改调用方代码。
我个人习惯在第一版设计中就把元数据管理做成可插拔的。后续如果你想换密钥存储方式、或者换一种元数据读取来源(比如从数据库切到Redis),只需要替换一个模块的注册实现,其他地方完全不需要动。
嗯,怎么样?这一套架构如果你看懂了,后面讲具体升级回滚流程时,你就能明白为什么有些步骤必须做,为什么有些步骤可以跳过。