十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux 内核非对称签名 API(crypto_sig)深入解析:从 sig_alg 算法定义到 ECDSA / RSA-PKCS1 实战

Linux 内核非对称签名 API(crypto_sig)深入解析:从 sig_alg 算法定义到 ECDSA / RSA-PKCS1 实战 Linux 内核非对称签名 APIcrypto_sig深入解析从 sig_alg 算法定义到 ECDSA / RSA-PKCS#1 实战【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux导读本文以内核文档 Documentation/crypto/api-sig.rst 为主体系统讲解 Linux 内核 Crypto API 中的非对称签名Asymmetric Signature子系统它以struct sig_alg定义算法、以struct crypto_sig作为转换transformation句柄并通过crypto_alloc_sig()/crypto_sig_sign()/crypto_sig_verify()等一组函数向内核其他模块提供公钥签名与验签能力。读完本文你将掌握 sig 类型算法的注册方式、每个 API 的语义与返回约定、当前内核中基于该框架实现的 ECDSA 与 RSA-PKCS#1 v1.5 算法的底层原理以及它们在密钥管理asymmetric keys子系统中的真实调用链。一、非对称签名 API 在 Crypto API 中的定位Linux 内核的 Crypto API 将密码学变换抽象为若干类型type非对称签名对应类型CRYPTO_ALG_TYPE_SIG。根据 include/crypto/sig.h 中DOC: Generic Public Key Signature API的说明The Public Key Signature API is used with the algorithms of typeCRYPTO_ALG_TYPE_SIG(listed as type sig in /proc/crypto)即所有通过该 API 注册的算法在/proc/crypto中都以type : sig的形式列出。这一点在实现文件 crypto/sig.c 中有直接印证——crypto_sig_show()对每个 sig 算法输出seq_puts(m, type : sig\n);且该子系统对应的struct crypto_type见 crypto/sig.c中type CRYPTO_ALG_TYPE_SIG、tfmsize offsetof(struct crypto_sig, base)、algsize offsetof(struct sig_alg, base)。从架构层次看sig 类型属于通用型generic接口它不关心底层是硬件加速器还是软件实现只规定一组签名 / 验签 / 设置密钥 / 查询尺寸的回调契约与 akcipher非对称密码、skcipher对称流密码、ahash异步哈希等并列是内核公钥基础设施如模块签名验证、IMA、密钥环的重要一环。二、算法定义struct sig_alg文档api-sig.rst的第一部分 Asymmetric Signature Algorithm Definitions 指向 include/crypto/sig.h 中的struct sig_alg它是算法实现者需要填充的核心结构内嵌一个通用的struct crypto_alg basestruct sig_alg { int (*sign)(struct crypto_sig *tfm, const void *src, unsigned int slen, void *dst, unsigned int dlen); int (*verify)(struct crypto_sig *tfm, const void *src, unsigned int slen, const void *digest, unsigned int dlen); int (*set_pub_key)(struct crypto_sig *tfm, const void *key, unsigned int keylen); int (*set_priv_key)(struct crypto_sig *tfm, const void *key, unsigned int keylen); unsigned int (*key_size)(struct crypto_sig *tfm); unsigned int (*digest_size)(struct crypto_sig *tfm); unsigned int (*max_size)(struct crypto_sig *tfm); int (*init)(struct crypto_sig *tfm); void (*exit)(struct crypto_sig *tfm); struct crypto_alg base; };各回调的含义与强制/可选属性依据头文件注释整理如下回调成员语义属性sign按公钥算法定义执行签名操作成功返回签名长度字节失败返回负错误码可选未提供时注册框架会填入默认实现verify执行完整的验签操作返回验证状态0 表示通过可选set_pub_key调用算法特定的设置公钥函数负责解码并解释BER 编码的公钥与参数必选set_priv_key同上针对 BER 编码的私钥与参数可选key_size返回密钥大小必选digest_size返回最大摘要大小可选max_size返回最大签名大小可选init初始化转换对象仅在对象实例化时被调用一次上下文分配之后。若硬件有需软件处理的特殊要求在此放置软件回退逻辑可选exitinit的逆操作清理init建立的各类状态可选注册框架对回调的补全与校验在 crypto/sig.c 的sig_prepare_alg()中注册前框架会做以下处理理解它对为什么某些回调是可选至关重要sign、verify、set_priv_key若为空分别被替换为返回-ENOSYS的默认实现如 sig_default_sign避免空指针调用set_pub_key为空则注册直接失败return -EINVAL因为设置公钥是验签的必备前提key_size为空同样导致注册失败max_size、digest_size若为空则统一使用默认计算式DIV_ROUND_UP_POW2(crypto_sig_keysize(tfm), BITS_PER_BYTE)见 sig_default_size即按密钥位数向上取整到整字节。完成补全后base-cra_type被置为crypto_sig_typecra_flags的类型位被改写为CRYPTO_ALG_TYPE_SIG最终由crypto_register_sig()crypto/sig.c写入通用算法注册表对应的注销函数为crypto_unregister_sig()。此外模板template实现可通过 sig_register_instance 注册派生实例并使用 crypto_grab_sig 抓取底层算法 spawn。三、转换对象struct crypto_sigstruct crypto_sig是用户调用方实例化的对象封装算法与核心处理逻辑本质是一个内嵌struct crypto_tfm base的句柄include/crypto/sig.hstruct crypto_sig { struct crypto_tfm base; };配套的内联辅助函数include/crypto/sig.h负责在crypto_tfm与crypto_sig、crypto_alg与sig_alg之间做container_of双向转换crypto_sig_tfm()/__crypto_sig_tfm()取回 / 还原通用crypto_tfm__crypto_sig_alg()/crypto_sig_alg()从 tfm 取出sig_alg。算法实现方通过 include/crypto/internal/sig.h 中的crypto_sig_ctx(tfm)获得自己的私有上下文即crypto_tfm_ctx例如 ECDSA 的struct ecc_ctx就存放在这里而cra_ctxsize字段负责告诉框架上下文需要多大空间。四、调用方 API 逐一详解api-sig.rst第二部分 Asymmetric Signature API 引用了 include/crypto/sig.h 中DOC: Generic Public Key Signature API之后的一组 API这是文档的实操核心下面逐一展开其参数语义与返回值约定。4.1 句柄分配与释放crypto_alloc_sig()/crypto_free_sig()struct crypto_sig *crypto_alloc_sig(const char *alg_name, u32 type, u32 mask); static inline void crypto_free_sig(struct crypto_sig *tfm);alg_name签名算法的cra_name通用名或cra_driver_name驱动名例如ecdsa、ecdsa-nist-p256type/mask指定算法类型与掩码通常传0, 0表示不施加额外约束返回值成功返回struct crypto_sig *句柄失败时该指针满足IS_ERR()为真用PTR_ERR()提取错误码crypto_free_sig()负责销毁句柄若传入 NULL 或错误指针该函数为空操作内部通过crypto_destroy_tfm实现。4.2 设置密钥crypto_sig_set_pubkey()/crypto_sig_set_privkey()static inline int crypto_sig_set_pubkey(struct crypto_sig *tfm, const void *key, unsigned int keylen); static inline int crypto_sig_set_privkey(struct crypto_sig *tfm, const void *key, unsigned int keylen);key的格式为BER 编码的密钥 算法 OID paramlen BER 编码的参数keylen密钥本身的长度不含上述其他数据内部调用alg-set_pub_key/alg-set_priv_key成功返回 0失败返回负错误码。4.3 尺寸查询crypto_sig_keysize()/crypto_sig_maxsize()/crypto_sig_digestsize()static inline unsigned int crypto_sig_keysize(struct crypto_sig *tfm); static inline unsigned int crypto_sig_maxsize(struct crypto_sig *tfm); static inline unsigned int crypto_sig_digestsize(struct crypto_sig *tfm);crypto_sig_keysize()返回以位为单位的密钥大小注意与字节的区别crypto_sig_maxsize()返回以字节为单位的最大签名大小crypto_sig_digestsize()返回以字节为单位的最大摘要大小。关键使用前提头文件注释明确警告这三个函数都假设密钥已经通过 setkey 设置成功如果在未设置或设置失败的情况下调用可能触发空指针解引用NULL dereference。调用方应严格遵循先 set key再查询尺寸的顺序。4.4 签名与验签crypto_sig_sign()/crypto_sig_verify()static inline int crypto_sig_sign(struct crypto_sig *tfm, const void *src, unsigned int slen, void *dst, unsigned int dlen); static inline int crypto_sig_verify(struct crypto_sig *tfm, const void *src, unsigned int slen, const void *digest, unsigned int dlen);crypto_sig_sign()调用算法特定的签名操作。src/slen为待签名数据如消息摘要dst/dlen为输出缓冲区成功返回签名长度失败返回负错误码crypto_sig_verify()执行完整验签。src/slen为待验证的签名digest/dlen为消息摘要验签成功返回 0失败返回负错误码如-EKEYREJECTED、-EBADMSG。五、真实算法实现一ECDSA 系列crypto/ecdsa.c 是基于 sig 框架的典型软件实现通过四个静态struct sig_alg实例注册ecdsa-nist-p192、ecdsa-nist-p256、ecdsa-nist-p384、ecdsa-nist-p521四个算法注册逻辑见 ecdsa_init注意 p192 在 FIPS 模式下可能不可用代码会记录注册失败状态并容错继续。以ecdsa-nist-p256为例crypto/ecdsa.cstatic struct sig_alg ecdsa_nist_p256 { .verify ecdsa_verify, .set_pub_key ecdsa_set_pub_key, .key_size ecdsa_key_size, .digest_size ecdsa_digest_size, .init ecdsa_nist_p256_init_tfm, .exit ecdsa_exit_tfm, .base { .cra_name ecdsa-nist-p256, .cra_driver_name ecdsa-nist-p256-generic, .cra_priority 100, .cra_module THIS_MODULE, .cra_ctxsize sizeof(struct ecc_ctx), }, };可以看到该算法未提供sign与set_priv_key签名与私钥设置走注册框架的默认-ENOSYS实现只做验签用途。实现要点上下文struct ecc_ctx保存曲线 ID、曲线参数、x/y坐标及pub_key_set状态标志设置公钥ecdsa_set_pub_key按 RFC 5480 第 2.2 节 Subject Public Key 解析只支持未压缩格式首字节必须为0x04随后校验坐标长度与曲线位数匹配并调用ecc_is_pubkey_valid_full()做完整性校验校验通过才置pub_key_set true验签ecdsa_verify 与 _ecdsa_verify先检查0 r n且0 s n再依次计算s1 s^-1 mod n、u1 hash * s1 mod n、u2 r * s1 mod n用 Shamir 多点乘计算res u1*G u2*pub_key最后比较res.x mod n与rdigest_sizeecdsa_digest_size由于 ECDSA 密钥远小于 RSA允许摘要大于密钥位数如 secp256r1 配合 SHA-384故返回最大支持哈希大小SHA512_DIGEST_SIZE64 字节。六、真实算法实现二RSA PKCS#1 v1.5 签名crypto/rsassa-pkcs1.c 实现了 RFC 8017 第 8.2 节的 RSASSA-PKCS1-v1_5RSA Signature Scheme with Appendix。它内部以crypto_akcipher为 child通过 sig 模板实例化出形如rsassa-pkcs1(sha256)的派生算法。哈希前缀表hash_prefixes为none、md5、sha1、rmd160、sha256、sha384、sha512、sha224以及sha3-256/384/512定义了完整的 ASN.1 DER 编码的 Full Hash PrefixRFC 9580 table 24用于 EMSA-PKCS1-v1_5 编码签名流程rsassa_pkcs1_sign对应 RFC 8017 8.2.1 的步骤——在缓冲区中构造0x01 || 0xff... || 0x00 || hash_prefix || digest的 EMSA 编码随后调用crypto_akcipher_sync_decrypt()用私钥完成 RSA 签名运算并处理可能的前导零填充pad_len验签流程rsassa_pkcs1_verify对应 RFC 8017 8.2.2——先用crypto_akcipher_encrypt()做 RSA 公钥运算解出编码串再逐字节校验0x01、0xff填充、0x00分隔符与哈希前缀最后用crypto_memneq常量时间比较比对摘要长度防御rsassa_pkcs1_invalid_hash_len通过校验 Full Hash Prefix 末字节与摘要长度一致来防止哈希长度不匹配并static_assert(HASH_MAX_DIGESTSIZE 127)保证 ASN.1 单字节长度编码成立none前缀长度 0用于 TLS 1.1 及更早、IKE v1 等不携带前缀的遗留协议场景。七、模板机制ecdsa-x962 与 ecdsa-p1363除直接注册算法外sig 框架还支持模板派生。crypto/ecdsa-x962.c 与 crypto/ecdsa-p1363.c 都是包装 child sig 算法的模板ecdsa-p1363接受 IEEE P1363 原始(r, s)二元组格式的签名见 crypto/ecdsa-p1363.c 直接透传crypto_sig_verify签名结构即r || secdsa-x962接受 X9.62 ASN.1 DER 编码的签名需先解包出r、s再交给 childcrypto/ecdsa-x962.c。两者都通过sig_register_instance()注册并利用crypto_grab_sig()抓取底层ecdsa-nist-p*算法实现了编码格式与曲线算法的解耦——这正是 sig 框架中模板机制的价值所在。八、内核中的真实调用者asymmetric keys 子系统sig API 最典型的消费者是内核公钥基础设施。crypto/asymmetric_keys/public_key.c 在多个位置通过该 API 完成密钥解析、签名与验签构成一条完整的调用链公钥解析crypto_alloc_sig(alg_name, 0, 0)后调用crypto_sig_set_pubkey(sig, key, pkey-keylen)public_key.c私钥解析同样分配句柄并crypto_sig_set_pubkey随后用crypto_sig_sign()执行签名public_key.c验签路径分配 tfm →crypto_sig_set_pubkey→crypto_sig_verify(tfm, sig-s, sig-s_size, sig-m, sig-m_size)public_key.c。由此可见内核在解析 X.509 证书后会把解析出的密钥与摘要交给 sig API 完成真正的密码学运算sig 层再按算法类型分派到 ECDSA / RSA-PKCS#1 等实现。九、测试验证testmgr 中的 sig 用例内核使用 testmgr 对 sig 算法做自测。crypto/testmgr.c 中的签名测试流程完整复现了调用方的标准操作序列tfm crypto_alloc_sig(driver, type, mask);testmgr.ccrypto_sig_set_pubkey(tfm, key, vecs-key_len);testmgr.c验签用例crypto_sig_verify(tfm, vecs-c, vecs-c_size, ...)testmgr.c其中vecs-c为签名、vecs-m为消息签名用例crypto_sig_sign(tfm, vecs-m, vecs-m_size, sig, sig_size);testmgr.c。该测试路径与 crypto/testmgr.h 中的向量数据配合可在每次模块自检时验证 ECDSA / RSA-PKCS#1 各算法与曲线、哈希组合的正确性。十、调用方使用范式与注意事项综合上述源码一个标准的 sig API 调用流程可归纳为crypto_alloc_sig(alg_name, 0, 0) │ ▼ crypto_sig_set_pubkey(tfm, ber_pubkey, keylen) // 先设置密钥必选前提 │ ▼ crypto_sig_keysize / maxsize / digestsize // 再查询尺寸依赖密钥已设置 │ ▼ crypto_sig_verify(tfm, sig, sig_len, digest, dlen) // 验签0 为通过 │ ▼ crypto_free_sig(tfm) // 释放句柄需要特别注意的工程要点先 set key 再查询尺寸keysize/maxsize/digestsize都依赖密钥已正确设置否则存在空指针解引用风险include/crypto/sig.h 的注释已明确警示返回值语义crypto_sig_sign()成功返回签名长度字节crypto_sig_verify()成功返回 0错误统一为负 errno如-EINVAL、-EBADMSG、-EKEYREJECTED、-EOVERFLOW、-ENOSYS错误指针判断crypto_alloc_sig()失败时用IS_ERR()/PTR_ERR()处理不要直接解引用算法名选择可传cra_name如ecdsa或cra_driver_name如ecdsa-nist-p256-generic需要具体曲线时优先使用驱动名以避免歧义密钥格式crypto_sig_set_pubkey()接收的是 BER 编码的密钥及算法 OID、参数等复合结构实现方如 ECDSA 的 ecdsa_set_pub_key负责解码与校验调用方需保证格式正确。结语从 api-sig.rst 这张地图出发本文沿着struct sig_alg→crypto/sig.c注册框架 → ECDSA / RSA-PKCS#1 实现 → 模板与 asymmetric keys 调用链 → testmgr 测试的脉络完整还原了 Linux 内核非对称签名 API 的全貌。无论是想要在内核模块中直接使用crypto_alloc_sig()完成验签还是计划为新的签名算法如基于格密码的方案编写sig_alg实现并注册都可以此文档与源码为参照前者读 include/crypto/sig.h后者则参考 crypto/ecdsa.c 与 crypto/rsassa-pkcs1.c 的完整实现范式。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表