资讯动态

铜锁/Tongsuo API 参数实战指南:一次讲清密码函数的上下文、密钥与坑点

发布时间:2026/10/30 4:37:50 来源:尧图企业网站定制
铜锁/Tongsuo API 参数实战指南一次讲清密码函数的上下文、密钥与坑点【免费下载链接】Tongsuo铜锁/Tongsuo is a Modern Cryptographic Primitives and Protocols Library项目地址: https://gitcode.com/GitHub_Trending/to/Tongsuo铜锁Tongsuo是一个现代密码学原语与协议库本文带你把 API 调用中最高频的参数问题讲透EVP_MD_CTX / EVP_CIPHER_CTX 上下文怎么管SM3、AES-256-GCM、RSA/SM2 的密钥与 IV 长度怎么配失败时错误码怎么查。读完你可以直接写出可编译的集成代码。动手之前先把上下文对象这层壳搞懂铜锁的 EVP 接口有一个统一的套路先 new 一个上下文在它上面干活最后 free。所有状态算法元数据、中间缓冲区、结果长度都挂在上下文对象上而不是函数全局变量里——这就是它在多线程下能用、且同一个进程里可以同时跑多路加密的原因。两类最核心的上下文EVP_MD_CTX消息摘要哈希上下文类型定义见 include/openssl/types.hEVP_CIPHER_CTX对称加解密上下文类型定义见 include/openssl/types.h生命周期三步走以哈希上下文为例EVP_MD_CTX *ctx EVP_MD_CTX_new(); // 创建失败返回 NULL // ... 用它做计算 ... EVP_MD_CTX_free(ctx); // 用完释放传 NULL 也安全函数原型说明创建EVP_MD_CTX *EVP_MD_CTX_new(void);见 include/openssl/evp.h释放void EVP_MD_CTX_free(EVP_MD_CTX *ctx);见 include/openssl/evp.h重置int EVP_MD_CTX_reset(EVP_MD_CTX *ctx);换算法复用同一对象见 include/openssl/evp.h这里会踩的坑上下文不是线程共享资源。每个线程或每条并发加解密任务持有自己的上下文别把全局一个 ctx当性能优化。另外free之后指针悬空二次使用是典型的 use-after-free。场景 A我要做哈希Init → Update → Final 三连怎么串哈希的调用链是固定三段任何一步返回非 1 都应中断后续调用int sm3_or_sha256(const unsigned char *data, size_t len, const EVP_MD *md, unsigned char *out, unsigned int *outlen) { EVP_MD_CTX *ctx EVP_MD_CTX_new(); if (ctx NULL) return 0; /* type 参数就是算法选择器EVP_sm3() 或 EVP_sha256() */ if (!EVP_DigestInit_ex(ctx, md, NULL) || !EVP_DigestUpdate(ctx, data, len) || !EVP_DigestFinal_ex(ctx, out, outlen)) { EVP_MD_CTX_free(ctx); return 0; } EVP_MD_CTX_free(ctx); return 1; }三个函数的完整签名均来自 include/openssl/evp.hint EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl); int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt); int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s);参数类型含义约束或推荐值ctxEVP_MD_CTX *摘要上下文必须来自EVP_MD_CTX_new()typeconst EVP_MD *算法选择器EVP_sm3()GB/T 32905-201632 字节摘要或EVP_sha256()32 字节摘要implENGINE *硬件引擎句柄通常传NULL走内置实现d / cntconst void */size_t待摘要数据及其长度可多次 Update 分段喂数据md / sunsigned char */unsigned int *输出缓冲区与长度缓冲区至少 32 字节SM3 与 SHA-256 同为 256 位算法选择器定义在 include/openssl/evp.hEVP_sha256()在第 966 行EVP_sm3()在第 979 行。选型上需要满足国密合规GB/T 32905-2016用 SM3对等方只认国际算法用 SHA-256。两者摘要长度相同缓冲区可以统一按 32 字节分配。一句话避坑EVP_DigestFinal_ex之后上下文状态即已收尾同一个 ctx 上直接再Update会得到错误结果——想算下一个值就先EVP_MD_CTX_reset或重新Init。场景 B我要做对称加密AES-256-GCM 从 key 到 tag 的全参数路径对称加密的主链路和哈希同构EVP_EncryptInit_ex → EVP_EncryptUpdate → EVP_EncryptFinal_ex。签名include/openssl/evp.hint EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv); int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl); int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);以 AES-256-GCM 为例算法选择器EVP_aes_256_gcm()见 include/openssl/evp.h/* AES-256-GCM 加密一段数据key 32 字节iv 12 字节 */ int gcm_encrypt(const unsigned char *key, const unsigned char *iv, const unsigned char *aad, size_t aad_len, const unsigned char *in, size_t in_len, unsigned char *out, size_t *out_len, unsigned char tag[16]) { EVP_CIPHER_CTX *ctx EVP_CIPHER_CTX_new(); if (ctx NULL) return 0; int ret 0; int len 0, final_len 0; if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key, iv) 1) { /* AAD只认证不加密用 inNULL 的 Update 喂入 */ if (aad_len 0 EVP_EncryptUpdate(ctx, NULL, len, aad, (int)aad_len) ! 1) goto end; if (EVP_EncryptUpdate(ctx, out, len, in, (int)in_len) ! 1) goto end; if (EVP_EncryptFinal_ex(ctx, out len, final_len) ! 1) goto end; *out_len (size_t)len (size_t)final_len; /* GCM 必须取出 16 字节 tag 随密文一起交付 */ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, tag) ! 1) goto end; ret 1; } end: EVP_CIPHER_CTX_free(ctx); return ret; }参数表参数类型含义约束或推荐值ctxEVP_CIPHER_CTX *加密上下文EVP_CIPHER_CTX_new()创建见 include/openssl/evp.hcipherconst EVP_CIPHER *算法模式EVP_aes_256_gcm()国密场景可用EVP_sm4_cbc()evp.h 第 1086 行keyconst unsigned char *密钥32 字节AES-256SM4-CBC 为 16 字节ivconst unsigned char *初始向量/nonce12 字节GCM 推荐长度避免内部 GHASH 预计算implENGINE *硬件引擎通常NULL进阶坑点AEAD 的 tag 长度与 AAD 喂法GCM 属于 AEAD认证加密普通三件套之外还有两个参数必须显式处理tag 长度默认 16 字节可用EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, len, tag)自定义ctrl 命令常量见 include/openssl/evp.hEVP_CTRL_AEAD_SET_TAG第 399 行。解密侧必须先SET_TAG塞入对方发来的 tag 再Final否则校验不过直接失败。AAD 的喂法附加数据走EVP_EncryptUpdate(ctx, NULL, len, aad, aad_len)——out 传 NULL 表示只认证不加密这一步不产生输出字节。IV 来源nonce 用RAND_bytes()生成include/openssl/rand.h且同一个 (key, nonce) 组合绝对不允许复用GCM 下复用 nonce 会直接泄漏机密与认证信息。一句话避坑密文交付时忘带 16 字节 tag对端EVP_DecryptFinal_ex必定返回失败——把 tag 和密文放在同一信封里传递。场景 C我要做非对称操作RSA 填充怎么选、SM2 用户 ID 怎么配非对称走EVP_PKEY_CTX三件套EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e); /* evp.h L1798 */ int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx); /* evp.h L1962 */ int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen); /* evp.h L1964 */ void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx); /* evp.h L1812 */RSA 填充模式为什么默认推荐 OAEP填充模式通过EVP_PKEY_CTX_set_rsa_padding()include/openssl/rsa.h设置常量定义在 include/openssl/rsa.hEVP_PKEY_CTX *pctx EVP_PKEY_CTX_new(pub_key, NULL); EVP_PKEY_encrypt_init(pctx); EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_OAEP_PADDING); /* 新代码一律 OAEP */ /* 还可继续配 OAEP 摘要EVP_PKEY_CTX_set_rsa_oaep_md(pctx, EVP_sha256()); */常量值描述安全性RSA_PKCS1_OAEP_PADDING4OAEP 填充随机化推荐概率性填充抗 Bleichenbacher 类自适应选择密文攻击RSA_PKCS1_PADDING1PKCS#1 v1.5结构性填充解密侧实现不当即成侧信道仅限兼容旧系统RSA_NO_PADDING3裸 RSA仅限 RSA-KEM 等由协议层自行组合的场景裸用即明文可被乘法预言推荐逻辑OAEP 每次填充引入随机性密文本身无法被代数分解v1.5 在 OpenSSL 3.x 之后已逐步弃用只有对接存量系统时才选它。SM2 用户 ID 参数怎么配SM2 签名/加密会把用户 ID 的 Z 值哈希进运算对端不一致则验签失败const char sm2_id[] 1234567812345678; /* 用户 ID双方必须约定一致 */ EVP_PKEY_CTX_set1_id(pctx, sm2_id, (int)sizeof(sm2_id) - 1); /* evp.h L1668内部拷贝 */EVP_PKEY_CTX_set1_id(EVP_PKEY_CTX *ctx, const void *id, int len)在 include/openssl/evp.h注意函数名带set1表示库内部完成复制你的缓冲区可在调用返回后释放。一句话避坑SM2 双端 ID 一个用字符串、一个用空值签出来的结果互验必挂——集成时把 ID 写进配置并写测试用例钉死。操作失败了先走一遍错误码调试路径所有返回int的 EVP 函数0 表示失败失败原因堆在进程级错误栈里两个函数就能读出来原型见 include/openssl/err.h.in 与 include/openssl/err.h.in#include openssl/err.h unsigned long err ERR_get_error(); /* 弹出一个错误码0 表示栈空 */ char msg[256]; ERR_error_string_n(err, msg, sizeof(msg)); /* 转成可读字符串 */ printf(密码操作失败: %s\n, msg);参数类型含义约束errunsigned long单个错误码失败调用后至少循环取一次直到为 0buf / lenchar */size_t输出缓冲区256 字节足以容纳常见错误文本错误码定义文件通用错误码模板 include/openssl/err.h.inEVP 模块错误码常量 include/openssl/evperr.h例如EVP_R_INVALID_KEY_LENGTHevperr.h 第 69 行——出现它通常就是 key 长度对不上算法。一句话避坑多线程环境里ERR_get_error()只取当前线程的栈务必在同一线程内紧挨着失败调用读取隔一层异步就取不到了。参数选择速查表维度推荐配置具体数值/说明哈希算法SM3 或 SHA-256摘要均 32 字节国密合规选 SM3GB/T 32905-2016对称算法AES-256-GCM / SM4-CBCAES-256 密钥 32 字节SM4 密钥 16 字节IV / Nonce 长度GCM 12 字节CBC 16 字节一律RAND_bytes()生成禁止复用 (key, nonce) 组合AEAD tag16 字节EVP_CTRL_AEAD_SET_TAG显式设置tag 随密文交付RSA 填充RSA_PKCS1_OAEP_PADDING值 4v1.5 仅限存量兼容SM2 用户 ID双方约定一致的字符串EVP_PKEY_CTX_set1_id内部拷贝语义国密 vs 国际SM3/SM4/SM2 vs SHA/AES/RSA摘要长度 32 字节对齐对称块/分组模式按对端实现选择性能上下文复用 大块 Update热点路径EVP_MD_CTX_reset/EVP_CIPHER_CTX_reset代替 new/free集成前最后核对一遍每条并发任务持有独立的EVP_MD_CTX/EVP_CIPHER_CTX用完free无全局共享IV/nonce 全部来自RAND_bytes()且 (key, nonce) 不重复GCM 按 12 字节分配AEAD 密文信封里带了 16 字节 tagAAD 用inNULL的EVP_EncryptUpdate单独喂入RSA 新代码只出现RSA_PKCS1_OAEP_PADDINGv1.5 有存量依赖才降级所有EVP_*返回值逐一判 1失败路径统一走ERR_get_error()ERR_error_string_n()落日志【免费下载链接】Tongsuo铜锁/Tongsuo is a Modern Cryptographic Primitives and Protocols Library项目地址: https://gitcode.com/GitHub_Trending/to/Tongsuo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑