电子说
顾名思义,在开启该功能之后,内核在加载内核模块时,会对内核模块的签名进行检查。
如果内核模块本身没有经过签名,或者签名值与预期值不符,这两种情况都会被认为是签名认证失败。根据策略的不同,签名认证失败可能会导致模块被拒绝加载,也可能是继续正常加载但内核会显示一条警告信息。
内核模块签名功能的本质是限制root用户载入恶意的内核模块。当root用户加载一个内核模块时,内核在分辨是系统管理员还是攻击者的时候,依靠的就是能够进行身份认证的可信的X.509证书和与之对应的私钥。只要私钥存储妥当不发生泄露,攻击者就无法伪造X.509证书,因此也就不可能提供含有正确签名的内核模块;而系统管理员是唯一合法的X.509证书的使用者,是可以用合法的证书对应的私钥对内核模块进行签名的。
openssl req -new -nodes -utf8 -sha256 -days 36500
-batch -x509 -config x509.genkey
-outform PEM -out system_key.pem
-keyout system_key.pem
其中x509.genkey的内容如下:
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
prompt = no
string_mask = utf8only
x509_extensions = v3_req
[ req_distinguished_name ]
O = Alibaba Group
OU = < your_organization >
CN = Test modsign key
emailAddress = < your_user_name >@alibaba-inc.com
[ v3_req ]
basicConstraints=critical,CA:FALSE
keyUsage=digitalSignature
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always
make bzImage modules
sudo make modules_install install
在指定了CONFIG_MODULE_SIG_ALL=y的情况下,kbuild系统可以自动对模块进行签名,而且该步骤通常无需手动运行,会在module_install时自动执行签名。
make modules_sign
openssl smime -sign -nocerts -noattr -binary -in < module > -inkey
< key > -signer < x509 > -outform der -out < raw sig >
openssl smime -verify -in < raw sig > -inform der -content < module >
-certfile < x509 > -noverify -out /dev/null
CONFIG_MODULE_SIG:Module signature verification
如果开启了该选项,在内核在加载内核模块时,会对内核模块的签名进行检查。
默认情况下,在加载没有签名或者是签名不正确的内核模块时,内核仅仅是打印一条提示信息,比如:
k_netlink: module verification failed: signature and/or required key missing - tainting kernel
同时将内核标记为tainted,然后继续正常加载签名有问题的模块。
CONFIG_MODULE_SIG_FORCE: Require modules to be validly signed
如果开启了该选项,在加载没有签名或者是签名不正确的内核模块时,内核会直接拒绝加载签名有问题的内核模块。
CONFIG_MODULE_SIG_HASH: Which hash algorithm should modules be signed with?
该选项用于决定签名时使用的摘要算法。每一种算法都有对应的内核选项:
注意:内核会使用crypto子系统中实现的摘要算法内核模块来计算摘要值,这就意味着这些实现摘要算法的模块必须已经事先编译为内置,否则就会发生“为了计算签名摘要值而要加载对应的模块但该模块的签名因缺少摘要算法模块而无法计算“的窘况。
CONFIG_MODULE_SIG_KEY: File name or PKCS#11 URI of module signing key
该选项指定了用于对内核模块签名的PEM格式的签名key文件的路径,或者是一个PKCS#11 URI(在实际进行签名前,签名工具会先把这个URI指定的key下载下来)。该选项的默认值是"certs/signing_key.pem"。如果没有修改这个默认值,内核会自动创建"certs/signing_key.pem"文件用于内核模块签名。
kbuild会将这个X.509证书文件转换为system certificate list并编译进内核中,然后在system trusted keyring初始化阶段将这个list中的每一个X.509证书都添加到builtin trusted keyring中。此时,每一个X.509证书就是一把system trusted key。因此,该选项指定的key文件中的所有X.509证书还可以当成system trusted key]来用。key文件至少包含一个PEM格式的私钥和与之关联的、PEM格式的X.509证书。可以通过级联的方式将更多的X.509证书添加到该key文件中。
但是反过来却是不成立的:并不是所有的system trusted key都可以用来验证内核模块的签名,比如secondary trusted keyring中的key就不能用来验证内核模块的签名;只有builtin trusted keyring中的key才可以用来验证内核模块的签名。
最后要提醒的是,用于模块签名的签名key可以是自签名的,也可以不是。如果不是自签名的,只要确保签名key和父key能被导入到builtin trusted keyring即可。不要求父key的父key也必须在builtin trusted keyring中。
CONFIG_MODULE_SIG_ALL: Automatically sign all modules
选中该选项后,kbuild会在执行make modules_install的时候对所有的内核模块进行签名。如果没有选中该选项,则需要使用者自己手动调用scripts/sign-file签名工具对内核模块进行签名。
module.sig_enforce
如果将该参数传给内核,表示强制验证内核模块签名,效果上等价于CONFIG_MODULE_SIG_FORCE=y。如果内核在编译时已经将CONFIG_MODULE_SIG_FORCE设为了y,那这里的内核选项是不会起任何作用的。
该选项为内核强制验证功能在策略上提供了一定的灵活性,比如运行系统需要DKMS或者SystemTap支持的话,如果没有实现配套的PKI签名服务机制,最好将CONFIG_MODULE_SIG_FORCE设为n,同时为了保证安全在内核命令行参数中指定module.sig_enforce。在必要时,可以临时去掉module.sig_enforce,以便系统维护或调试。
内核编译阶段
如果CONFIG_MODULE_SIG_KEY参数为默认值,自动生成内核模块签名key文件certs/signing_key.pem
如果CONFIG_MODULE_SIG_KEY的值为默认值certs/signing_key.pem,表示用户希望使用由kbuild自动生成的key文件对内核模块进行签名。
kbuild会自动执行以下命令生成key文件:
openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_HASH) -days 36500
-batch -x509 -config certs/x509.genkey
-outform PEM -out $(obj)/signing_key.pem
-keyout certs/signing_key.pem
该命令的含义是:根据X509证书请求配置模板文件的内容,生成一个自签名的X509证书。生成的key文件保存为certs/signing_key.pem, 同时将自签名的X509证书附在key内容的后面。
其中生成X509证书请求时使用的配置模板文件的内容为:
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
prompt = no
string_mask = utf8only
x509_extensions = myexts
[ req_distinguished_name ]
#O = Unspecified company
CN = Build time autogenerated kernel key
#emailAddress = unspecified.user@unspecified.company
[ myexts ]
basicConstraints=critical,CA:FALSE
keyUsage=digitalSignature
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid
将内核模块签名key文件build到system certificate list中
kbuild会调用scripts/extract-cert程序将CONFIG_MODULE_SIG_KEY指定的PEM格式的X.509证书文件转换为DER格式的certs/signing_key.x509文件:
scripts/extract-cert $(CONFIG_MODULE_SIG_KEY) signing_key.x509
如果CONFIG_MODULE_SIG_KEY指定的一个PKCS#11 URI,scripts/extract-cert程序也会在签名之前先下载好再使用。
接下来,上一个步骤生成的certs/signing_key.x509文件会连同system_certificates.S一起被编译为certs/system_certificates.o。该object文件的内容会被包含到内核image的.init.rodata节中。
__INITRODATA
.align 8
.globl VMLINUX_SYMBOL(system_certificate_list)
VMLINUX_SYMBOL(system_certificate_list):
__cert_list_start:
#ifdef CONFIG_MODULE_SIG
.incbin "certs/signing_key.x509"
#endif
.incbin "certs/x509_certificate_list"
__cert_list_end:
该system certificate list中的所有X.509证书最终都会被加载到builtin trusted keyring中,并作为system trusted key来使用。
顺便一提,由内核选项CONFIG_SYSTEM_TRUSTED_KEYS指定的system trusted key文件(即certs/x509_certificate_list)也会成为system certificate list的一部分。准确来说,它会在内核初始化system trusted klseyring时加入到builtin trusted keyring中。
初始化system trusted keyrings
由内核选项CONFIG_MODULE_SIG_KEY指定的对内核模块进行签名的key文件会在这个阶段被加载到builtin trusted keyring中。
加载内核模块
static int load_module(struct load_info *info, const char __user *uargs,
int flags)
...
err = module_sig_check(info, flags);
if (err)
goto free_copy;
...
...
#ifdef CONFIG_MODULE_SIG
mod- >sig_ok = info- >sig_ok;
if (!mod- >sig_ok) {
pr_notice_once("%s: module verification failed: signature "
"and/or required key missing - tainting "
"kerneln", mod- >name);
add_taint_module(mod, TAINT_UNSIGNED_MODULE, LOCKDEP_STILL_OK);
}
#endif
结论:执行modprobe -f一定会导致签名认证失败, 这一点所有的责任人都要注意。原因是:作为内核模块的数据的一部分的版本信息将被modprobe -f完全忽略,如果一个曾经签过名的模块有安全漏洞,那么攻击者可以在新内核上强行加载这个有漏洞的模块以便攻击新内核。
#ifdef CONFIG_MODULE_SIG
static int module_sig_check(struct load_info *info, int flags)
{
int err = -ENOKEY;
const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
const void *mod = info- >hdr;
/*
* Require flags == 0, as a module with version information
* removed is no longer the module that was signed
*/
if (flags == 0 &&
info- >len > markerlen &&
memcmp(mod + info- >len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
/* We truncate the module to discard the signature */
info- >len -= markerlen;
err = mod_verify_sig(mod, &info- >len);
}
if (!err) {
info- >sig_ok = true;
return 0;
}
/* Not having a signature is only an error if we're strict. */
if (err == -ENOKEY && !sig_enforce)
err = 0;
return err;
}
#endif
int mod_verify_sig(const void *mod, unsigned long *_modlen)
{
struct module_signature ms;
size_t modlen = *_modlen, sig_len;
pr_devel("== >%s(,%zu)n", __func__, modlen);
if (modlen <= sizeof(ms))
return -EBADMSG;
memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms));
modlen -= sizeof(ms); // modlen包括模块内容+PKCS#7消息
sig_len = be32_to_cpu(ms.sig_len); // PKCS#7消息的长度
if (sig_len >= modlen)
return -EBADMSG;
modlen -= sig_len; // 仅包含模块内容的长度
*_modlen = modlen; // 返回模块内容的长度
if (ms.id_type != PKEY_ID_PKCS7) {
pr_err("Module is not signed with expected PKCS#7 messagen");
return -ENOPKG;
}
if (ms.algo != 0 ||
ms.hash != 0 ||
ms.signer_len != 0 ||
ms.key_id_len != 0 ||
ms.__pad[0] != 0 ||
ms.__pad[1] != 0 ||
ms.__pad[2] != 0) {
pr_err("PKCS#7 signature info has unexpected non-zero paramsn");
return -EBADMSG;
}
return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
NULL, VERIFYING_MODULE_SIGNATURE,
NULL, NULL);
}
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
/**
* verify_pkcs7_signature - Verify a PKCS#7-based signature on system data.
* @data: The data to be verified (NULL if expecting internal data).
* @len: Size of @data.
* @raw_pkcs7: The PKCS#7 message that is the signature.
* @pkcs7_len: The size of @raw_pkcs7.
* @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only,
* (void *)1UL for all trusted keys).
* @usage: The use to which the key is being put.
* @view_content: Callback to gain access to content.
* @ctx: Context for callback.
*/
int verify_pkcs7_signature(const void *data, size_t len,
const void *raw_pkcs7, size_t pkcs7_len,
struct key *trusted_keys,
enum key_being_used_for usage,
int (*view_content)(void *ctx,
const void *data, size_t len,
size_t asn1hdrlen),
void *ctx)
{
struct pkcs7_message *pkcs7;
int ret;
// 解析内核模块签名中的PKCS#7消息(DER格式),并将相关字段
// 填充到pkcs7数据结构中。
// struct pkcs7_message {
// PKCS#7中的signer证书列表(可以为空)
// struct x509_certificate *certs;
// 证书撤销列表CRL(可以为空)
// struct x509_certificate *crl;
// Signer信息列表
// struct pkcs7_signed_info *signed_infos;
// PKCS#7消息格式版本号。1: PKCS#7或CMS; 3:CMS
// u8 version;
// 表示PKCS#7消息中是否包含authenticatedAttributes字段
// bool have_authattrs;
// 内容类型
// enum OID data_type;
// 被签名的内容的长度
// size_t data_len;
// 被签名的内容的ASN.1 header的长度
// size_t data_hdrlen;
// 被签名的内容(如果为NULL表示是detached content)
// const void *data;
// };
//
// Signer信息
// struct pkcs7_signed_info {
// 指向下一个signer信息
// struct pkcs7_signed_info *next;
// 指向位于pkcs7_message.certs的signer证书(如果有的话)
// struct x509_certificate *signer;
// 当前signer信息在signer信息列表中的位置
// unsigned index;
// True if not usable due to missing crypto
// bool unsupported_crypto;
//
// Auth属性值: Message digest - the digest of the Content Data (or NULL)
// const void *msgdigest;
// unsigned msgdigest_len;
//
// Authenticated Attribute data (or NULL) */
// unsigned authattrs_len;
// const void *authattrs;
//
// Auth属性值
// unsigned long aa_set;
// Auth属性值
// time64_t signing_time;
//
// 指向signer生成的签名信息
// struct public_key_signature *sig;
// };
//
// struct public_key_signature {
// [0]包含的是由signer的issuerAndSerialNumber字段生成的key id
// [1]包含的则是skid(可以在X.509证书中的X509v3 Subject Key Identifier字段找到该值)
// struct asymmetric_key_id *auth_ids[2];
// 具体的签名值
// u8 *s;
// 签名值的长度
// u32 s_size;
// 摘要值
// u8 *digest;
// 摘要值的长度
// u8 digest_size;
// 签名算法,目前仅支持"rsa"
// const char *pkey_algo;
// 摘要算法,比如"sha256"等
// const char *hash_algo;
// };
pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
if (IS_ERR(pkcs7))
return PTR_ERR(pkcs7);
/* The data should be detached - so we need to supply it. */
// 对于内核模块来说,content总是detached的。
if (data && pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
pr_err("PKCS#7 signature with non-detached datan");
ret = -EBADMSG;
goto error;
}
// 验证PKCS#7签名。过程如下:
// 1. 计算内核模块主体内容的摘要值
// 2. 遍历每一个signer信息,如果PKCS#7中包含了signer的X.509证书的话,使用其
// 证书中的公钥验证signer的签名值
// 3. 如果signer信息中不包含signer的X.509证书的话,这里同样也会返回0,目的是
// 为了让接下来的system trusted key来验证signer中的的签名值
ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error;
// 确定验证签名的、且可信的issuer证书的来源
if (!trusted_keys) {
trusted_keys = builtin_trusted_keys;
} else if (trusted_keys == (void *)1UL) {
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
trusted_keys = secondary_trusted_keys;
#else
trusted_keys = builtin_trusted_keys;
#endif
}
// 用system trusted keyring中的system trusted key作为signer的X.509证书来验证
// PKCS#7中包含的每一个signer的签名值。这里的逻辑很简单,既然PKCS#7中可能
// 没有包含signer的证书,那么如果能在system trusted keyring中找到signer的证书
// 来验证每一个signer的签名值也是可以的。
// 这里只考虑最简单的情况:signer信息中不包含X.509证书的情况。
// 首先用auth_ids[0]、即通过signer.IssuerAndSerialNumbe字段
// 计算出的key id在system trusted keyring中搜索与之匹配的system trusted key。
// 如果没有找到,返回-ENOKEY,同时打印错误;如果找到匹配的system trusted key,
// 则使用该key验证PKCS#7中的签名。
ret = pkcs7_validate_trust(pkcs7, trusted_keys);
if (ret < 0) {
if (ret == -ENOKEY)
pr_err("PKCS#7 signature not signed with a trusted keyn");
goto error;
}
if (view_content) {
size_t asn1hdrlen;
ret = pkcs7_get_content_data(pkcs7, &data, &len, &asn1hdrlen);
if (ret < 0) {
if (ret == -ENODATA)
pr_devel("PKCS#7 message does not contain datan");
goto error;
}
ret = view_content(ctx, data, len, asn1hdrlen);
}
error:
pkcs7_free_message(pkcs7);
pr_devel("<==%s() = %dn", __func__, ret);
return ret;
}
EXPORT_SYMBOL_GPL(verify_pkcs7_signature);
#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
$ sudo keyctl list %:.builtin_trusted_keys
6 keys in keyring:
...
所有builtin trusted keyring中的key都可以用来对内核模块进行签名。
如何判断一个内核模块是否签过名?
有一种不太准确的方法是直接检查内核模块的二进制内容:
tail < module.ko >
如果有出现“ Module signature appended ”字样,大多数情况下可以认为该内核模块是有签名的。之所以这里的措辞很谨慎,原因是这里没有用科学的方法去解析该模块的签名格式是否正确,也没有去验证里面的key是否真的有效。
如果添加额外的内核模块签名key?
strip --keep-file-symbols < module_name >
加载内核模块时出现错误“module verification failed: signature and/or required key missing - tainting kernel”
该错误信息表示该内核模块没有经过签名,但是由于内核没有开启强制验证,因此该内核模块还是被加载了。
加载内核模块时出现错误“ERROR: could not insert module : Required key not available”(errno为-ENOKEY)
如果伴随着这个错误的同时,内核还报“PKCS#7 signature not signed with a trusted key”这个错误的话,说明被加载的模块虽然经过了签名,但签名key并不存在于builtin trusted keyring中,因此内核无法验证签名的有效性。
如果内核没有报“PKCS#7 signature not signed with a trusted key”这个错误,说明被加载的模块根本没有经过签名。
加载内核模块时出现错误“ERROR: could not insert module : Key was rejected by service”(errno为-EKEYREJECTED)
说明被加载的模块虽然经过了签名,但签名key并不存在于builtin system trusted keyring中,因此内核拒绝加载该内核模块。
注意这个错误与错误“Required key not available”+“PKCS#7 signature not signed with a trusted key”的区别:后者表示签名key与位于builtin system trusted keyring中的key是有关联的,比如builtin system trusted keyring中只包含了父key,而签名key是由父key签发的;或者是builtin system trusted keyring中只包含了由父key签发的签名key,而签名key是父key。前者则表示签名key与位于builtin system trusted keyring中的key(如果有key的话)是没有任何关联性的。
另外之前碰到过一个case也会返回-EKEYREJECTED:trusted keyring里带了2个不同的证书,但是却具有相同的issuer和serial number。问题现象是:用这2个证书对应的私钥来签名,总有一个会失败。根因是认证PKCS#7 signer签名的时候,是用生成签名的证书key id(issuer+证书序列号)作为KEY来search trusted keyring的。因此,如果用第一个被search到的证书key id对应的证书不是生成签名的证书,就会导致-EKEYREJECTED错误。解决方法很简单:用不同的serial number来生成同一个CA生成的子证书。
对内核模块签名后又strip导致内核模块签名验证失败
模块一旦被签名好,再次对其内容的修改会导致签名验证失败。
这种修改可能有多种来源: - rpmbuild可能会考虑到减小initramfs的尺寸进而在打包时strip掉内核模块的debuginfo。
RHEL 3.10的内核模块签名格式
RHEL 3.10的签名格式是redhat自己弄的,和standard linux kernel的格式不兼容。
连modinfo这个开源工具,对模块签名格式的支持的code,也是redhat写的。最新的modinfo对5.x的模块签名都不支持显示,但能正确显示3.10的redhat自己实现和port的模块签名的格式。
使用PKCS#11 URI进行内核模块签名
PKCS#11由RFC7512定义。它主要是定义了HSM的使用接口。虽然使用HSM不太方便,但是安全性还是有一定保障的,而且如果现场出了严重,必须亲自上阵的话,用HSM可能反而是最稳妥的办法。
签名工具sign-file
sign-file工具的源码位于kernel源码目录中的script目录下。
scripts/sign-file [-dp] < hash_algo > < private_key_name >
< x509_name > < module_name > [< dest_name >]
scripts/sign-file -s < raw_sig_name > < hash_algo > < x509_name > < module_name > [< dest >]
是签名使用的摘要算法;是签名的私钥文件,支持pkcs#11或PEM格式;是与私钥文件关联的X.509证书文件(PEM或DER格式都可以);是被签名的内核模块的名字;是签名后的内核模块文件。
-k
没有指定-k参数的话,OpenSSL使用issuer name和issuer序列号来标识签名证书;如果指定了-k参数的话,则使用subject key identifier(SKID)来标识签名证书。(注释:证书的AKID表示父CA证书的SKID;如果一个证书的AKID等于SKID,表示该证书是自签名的;因为issuer name和issuer都存在重名的情况,因此x509 v3增加了SKID和AKID扩展)。
默认情况下,kbuild没有设置该参数。
具体来说,在认证PKCS#7 signer签名的时候,是用生成签名的证书key id(issuer+证书序列号,由于没有指定-k参数)作为KEY来search trusted keyring的。
-p
将PKCS#7消息单独存为以.p7s为后缀的文件。可以用openssl pkcs7 -text -print -inform der -in .p7s来查看PKCS#7消息的详细内容。
默认情况下,kbuild没有设置该参数。
-s
指定由-p参数生成的.p7s文件作为内核模块签名中的部分。
默认情况下,kbuild没有设置该参数。
-d
只是走一下签名的流程,并不真的对模块进行签名;同时兼具-p的功能。
默认情况下,kbuild没有设置该参数。
KBUILD_SIGN_PIN:
该环境变量有两个作用: - 在不使用PKCS#11访问签名用私钥的情况下,用来指定私钥文件的解密口令。 - 在使用PKCS#11访问签名用私钥的情况下,用来指定PKCS#11的PIN码。
sign-file会使用X.509证书中的issuer和serial来设置PKCS#7 signer info中的issuer和serial中的字段。因此,如果内核模块是由子key签的,但放到system trusted keyring中只有父key的话,内核在验证签名时会使用issuer为父key+序号为子key证书的key id组合到system trusted keyring中查找匹配的key,结果肯定是不匹配。
< 内核模块的内容 >
< PKCS#7消息 >
< 模块签名 >
< Mark字符串"~Module signature appended~n"(共28字节,不包括结尾的NULL字符) >
PKCS#7是一种加密消息的语法标准,由RSA安全体系在公钥加密系统中交换数字证书产生的一种加密标准。有关其详细格式,请参考RFC 2315 - PKCS #7: Cryptographic Message Syntax Version 1.5。
PKCS#7消息的格式的主体是content,共支持6种content类型:
content的具体格式由content类型的定义来决定。目前内核中的PKCS#7 parser仅支持content类型为signedData的PKCS#7消息。
下面是PKCS#7的格式定义:
ContentInfo ::= SEQUENCE {
// 定义了content的类型。
// 该字段的类型为:ContentType ::= OBJECT IDENTIFIER
contentType ContentType,
// content的具体格式由contentType字段的定义来决定。
content
[0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
生成signedData类型的PKCS#7消息的过程如下:
authenticatedAttributes
[0] IMPLICIT Attributes OPTIONAL,
Attributes值的tag是SET OF,不包括前面的IMPLICIT [0] tag。
DigestInfo ::= SEQUENCE {
digestAlgorithm DigestAlgorithmIdentifier, // 摘要算法OID
digest Digest // 摘要值, 类型为OCTET STRING
}
PKCS#7允许多个signer对相同的输入内容进行签名,每个签名以及signer信息都会封装到PKCS#7消息中。因此一个PKCS#7消息可以同时对多个signer进行认证。
signedData类型的PKCS#7消息由一组signer信息以及一组signer/issuer的证书组成。signer信息包括特定signer提供的签名值,signer证书包括signer的实体信息以及公钥。issuer证书用于对signer进行身份认证。
// PKCS#7类型为signedData的格式定义
SignedData ::= SEQUENCE {
// 表示PKCS#7消息格式语法的版本号
// 该字段的类型为:Version ::= INTEGER
version Version,
// 表示所有signer使用的摘要算法的集合。
// 该字段的类型为:
// DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
// DigestAlgorithmIdentifier ::= AlgorithmIdentifier
digestAlgorithms DigestAlgorithmIdentifiers,
// 包含被签名的内容。
// contentType表示内容的类型;content表示实际被签名的内容。
// 注意:真正被签名的只有content自身,不包括DER编码中的id和长度字段。
// 如果content置空,表示真正被签名的content不在PKCS#7中,这种情况被称
// 为detached content。
// 该字段的类型为:
// ContentInfo ::= SEQUENCE {
// contentType ContentType,
// content
// [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
contentInfo ContentInfo,
// 一组证书PKCS#6格式的扩展证书或X.509证书。理论上来说,signer的
// 证书,以及所有与signer相关联的全部issuer证书甚至连根证书都可以放
// 在该字段中,从而构成一个证书层级结构;但实际上该字段也是可以为空的。
// 该字段的类型为:
// ExtendedCertificatesAndCertificates ::=
// SET OF ExtendedCertificateOrCertificate
// 可以是PKCS#6格式的扩展证书或X.509证书。
// ExtendedCertificateOrCertificate ::= CHOICE {
// certificate Certificate, -- X.509
// extendedCertificate [0] IMPLICIT ExtendedCertificate }
certificates
[0] IMPLICIT ExtendedCertificatesAndCertificates
OPTIONAL,
// 包含一组撤销证书。
// 该字段的类型为:
// CertificateRevocationLists ::= SET OF CertificateRevocationList
crls
[1] IMPLICIT CertificateRevocationLists OPTIONAL,
// 包含一组signer的相关信息
signerInfos SignerInfos
}
// 包含一组signer的相关信息
SignerInfos ::= SET OF SignerInfo
// 包含每个signer的相关信息:signer使用的签名证书,摘要算法,
// 签名算法,签名以及属性字段等。
SignerInfo ::= SEQUENCE {
// 表示PKCS#7消息格式语法的版本号
// 本字段的类型为: Version ::= INTEGER
version Version,
// 每个证书issuer都会为其所颁发的证书分配一个证书序列号,且该
// 序列号在其所颁发的所有证书中是唯一的。因此在给定证书issuer的
// 专有名称以及其颁发的证书序列号的共同作用下,本字段可以为用
// 于唯一标识指定证书issuer颁发的某个特定的证书。
// 本字段的类型为:
// IssuerAndSerialNumber ::= SEQUENCE {
// issuer Name, 证书issuer的专有名称
// serialNumber CertificateSerialNumber
// }
issuerAndSerialNumber IssuerAndSerialNumber,
// Signer使用的摘要算法
// 本字段的类型为: DigestAlgorithmIdentifier ::= AlgorithmIdentifier
digestAlgorithm DigestAlgorithmIdentifier,
// 一组经过签名或认证的属性信息
authenticatedAttributes
[0] IMPLICIT Attributes OPTIONAL,
// Signer使用的签名算法,比如PKCS#1 RSA。
// 本字段的类型为:
// DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
digestEncryptionAlgorithm
DigestEncryptionAlgorithmIdentifier,
// 用signer的私钥加密后的结果,即所谓的签名值
encryptedDigest EncryptedDigest,
// 一组未经签名或认证的属性信息
unauthenticatedAttributes
[1] IMPLICIT Attributes OPTIONAL }
内核对内核模块签名中的PKCS#7消息格式有如下硬性要求:
该签名格式包含了模块签名的各种元数据,并采用big endian编码。
struct module_signature {
uint8_t algo; /* Public-key crypto algorithm [0] */
uint8_t hash; /* Digest algorithm [0] */
uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */
uint8_t signer_len; /* Length of signer's name [0] */
uint8_t key_id_len; /* Length of key identifier [0] */
uint8_t __pad[3];
uint32_t sig_len; /* Length of signature data */ htonl(size of .p7s)
};
全部0条评论
快来发表一下你的评论吧 !