第四章 加密SDK常见API接口设计
说实话,我接触过的加密SDK少说也有十几个了。每次对接新SDK,最先看的就是它的API长什么样。说白了,加密SDK的核心API就那么几个——初始化、加密、解密、签名、验签。这些接口设计得顺不顺,直接决定咱们集成的时候是省心还是抓狂。
这一章,我就把我这些年摸爬滚打总结出来的接口设计套路分享给你。你想想看,很多坑其实都是相通的。
4.1 初始化——SDK的“开门钥匙”
初始化接口是整个SDK的入口。我见过有的SDK把初始化做得特别重,要传证书、密钥、算法列表……结果客户一上手就懵了。我们团队以前做过一个项目,对接某厂商的加密卡,光初始化就写了200行配置,后来发现只需要一个sdk_init(config_path)就够了。
好的初始化接口应该长这样:
// 伪代码示例
int sdk_init(const char *config_file);
// 或者
int sdk_init(const SdkConfig *config);
我个人习惯把配置参数设计成结构体,这样扩展性好。比如:
| 参数 | 类型 | 说明 | 是否必填 |
|---|---|---|---|
| log_level | int | 日志级别,0-3 | 否,默认2 |
| key_store_path | string | 密钥存储路径 | 否,使用默认路径 |
| algorithm | string | 算法套件,如"AES-256-GCM" | 是 |
| thread_safe | bool | 是否启用线程安全模式 | 否,默认false |
4.2 加密与解密——成对出现的“亲兄弟”
加密和解密接口必须成对设计。你想想看,如果加密用的参数跟解密对不上,那数据就彻底废了。我见过最离谱的是一个SDK加密默认用PKCS7填充,解密却要求调用方自己指定填充模式——文档里只字未提,坑了三个外包团队。
标准接口大概是这个味道:
int encrypt(const unsigned char *plaintext, int pt_len,
unsigned char *ciphertext, int *ct_len,
const KeyHandle key, const EncParams *params);
int decrypt(const unsigned char *ciphertext, int ct_len,
unsigned char *plaintext, int *pt_len,
const KeyHandle key, const DecParams *params);
这里要注意几个点:
- 缓冲区长度:加密输出长度通常比明文长(因为有IV、Tag)。我建议让调用方先传NULL拿到长度,再分配内存。
- 错误码:别只返回0/-1,至少告诉我哪个参数错了。
- 上下文对象:有些SDK分两步——
encrypt_init+encrypt_update+encrypt_final。大文件流式处理时用得着,但小数据量别搞这么复杂。
4.3 签名与验签——验证身份的“指纹”
签名和验签比加密解密更敏感。为什么?因为签名一旦被伪造,整个信任链就断了。我参与过金融支付SDK的设计,对签名接口的要求是:必须明确返回签名值长度,且支持分步签名(尤其是硬件加密机场景)。
一个实用的签名接口:
int sign(const unsigned char *data, int data_len,
unsigned char *signature, int *sig_len,
const KeyHandle private_key);
int verify(const unsigned char *data, int data_len,
const unsigned char *signature, int sig_len,
const KeyHandle public_key);
嗯,这里要插一句:验签接口不要只返回成功/失败,最好返回具体的错误原因,比如“公钥不匹配”“签名已过期”。我曾经因为一个SDK验签失败只返回-1,跟客户吵了三天才知道是证书链没传全。
4.4 一张图看明白API调用流程
下面这张图是我自己画的,把加密SDK的常见API调用关系串起来了。你从上往下看,就清楚每个接口在什么时机调用了。
4.5 接口设计通用原则
最后,我把自己多年总结的几个原则列出来,你设计的时候可以参考:
- 对称性:加密和解密、签名和验签,参数结构尽量对称。你想想看,调用方对着两种接口,参数顺序一样,不容易出错。
- 显式声明参数:别搞隐式默认。比如IV到底由谁生成?算法模式是否默认CBC?全部明文写进参数文档。
- 线程安全:如果你的SDK被多线程调用,初始化时声明
thread_safe=true,或者提供独立的上下文句柄。 - 优雅降级:硬件加密不可用时,我是否允许退回到软件实现?设计一个
sdk_fallback()回调。 - 日志与调试:加一个可开关的调试模式,打印每次API调用的入参和出参。我在排查线上问题时,这个功能救过我好几次。
encrypt_ex_v2_with_mode_and_iv()这种,维护成本极高。我见过有的SDK一个加密功能有7个变体,文档比代码还难读。
好了,这一章我把自己踩过的坑和总结的套路都抖出来了。加密SDK的API设计,说白了就是“让人一眼能看懂,一调就能用”。