Linux Key子系统
Linux Key Retention Service(密钥保留服务)是内核的核心安全组件,用于在内核空间安全地管理密钥、证书和认证凭据。它为内核模块和文件系统提供统一的密钥管理框架。
1 Key子系统基本概念
1.1 主要目标
1. 安全存储:在内核空间安全保存敏感数据(如加密密钥、SSL证书)
2. 访问控制:基于权限模型(UID/GID)控制密钥访问
3. 生命周期管理:支持密钥的创建、更新、撤销、自动过期
4. 命名空间隔离:为容器提供独立的密钥域(keyring_ns)
5. 层次化组织:通过嵌套的密钥环(keyring)构建树形结构。
1.2 秘钥基本概念
概念简介
1. ASN.1(Abstract Syntax Notation One)
- 作用:跨平台描述数据结构的抽象语法标准(如证书、密钥格式)。
- 特点:定义语法规则(如 INTEGER, SEQUENCE),不指定编码方式。
- 类比:类似编程语言的类型声明(如C的 struct)。
2. BER(Basic Encoding Rules)
- 作用:ASN.1 的基础编码规则,将数据结构转为二进制。
- 特点:编码灵活(同一数据可有多种合法编码),适用于网络传输等非严格场景。
- 问题:非唯一性可能导致安全风险。
3. DER(Distinguished Encoding Rules)
- 作用:BER 的严格子集,确保唯一二进制编码。
- 场景:证书(X.509)、签名(PKCS#7)等安全领域,避免编码歧义。
4. PKCS#7/CMS(Cryptographic Message Syntax)
- 作用:基于 ASN.1/DER 的加密消息封装标准。
- 功能:支持数字签名、加密、证书链封装(如 .p7b 文件)。
- 应用:文档签名、邮件加密、内核模块签名。
5. PKCS#1 v1.5
- 作用:RSA 密钥格式与签名填充标准(基于 ASN.1/DER)。
- 问题:填充漏洞(如 Bleichenbacher 攻击),逐渐被 RSA-PSS 替代。
- 遗留应用:旧版 TLS/SSL 协议。
6. X.509
- 作用:公钥证书的国际标准(基于 ASN.1 和 DER)。
- 结构:包含版本号、序列号、使用者/颁发者、公钥、签名等字段。
- 应用:TLS/SSL、代码签名、身份认证。
7. CRL(Certificate Revocation List)
- 作用:基于 ASN.1/DER 的列表,记录被吊销的证书序列号。
- 缺点:更新延迟(需定期下载全量列表),逐渐被 OCSP 替代。
8. OCSP(Online Certificate Status Protocol)
- 作用:实时查询证书吊销状态的协议(替代 CRL)。
- 数据格式:请求/响应使用 ASN.1 编码(DER 或 BER)。
- 优势:低延迟,按需查询。
9. PEM(Privacy-Enhanced Mail)
- 作用:将 DER 二进制数据转换为 Base64 的文本封装格式。
- 标识:以 -----BEGIN/END CERTIFICATE----- 包裹。
- 文件扩展名:.pem, .crt, .key。

2 Key子系统初始化
2.1 Key初始化
key_init用户初始化Key子系统基础struct key slab对象,以及添加默认key_type:
start_kernel
->key_init
->kmem_cache_create--创建密钥对象缓存key_jar用于保存struct key。
->list_add_tail--将keyring(密钥环容器)、dead(无效type)、user(用户数据密钥)、logon(登录凭证)等key类型加入到key_types_list中。
->rb_link_node--初始user会话密钥环。
->rb_insert_color
常见的key_type有:
| 密钥类型 (key_type) | 描述 | 典型用途 |
|---|---|---|
keyring |
密钥容器 | 组织其他密钥的容器 |
user |
用户密钥 | 存储任意用户数据 |
logon |
登录凭据/密码 | 用户登录 |
asymmetric |
非对称密钥 | 存储证书和公钥/私钥 |
trusted |
TPM保护密钥 | 可信平台模块保护的密钥 |
encrypted |
加密密钥 | 加密文件系统密钥 |
big_key |
大容量密钥 | 存储大尺寸数据(>4KB) |
cifs.idmap |
CIFS身份映射 | SMB/CIFS文件系统 |
fscrypt |
文件系统加密 | ext4/f2fs加密 |
dns_resolver |
DNS解析器 | 内核级DNS解析 |
2.2 初始化系统可信keyring
system_trusted_keyring_init()是内核密钥管理子系统的一部分,专门用于存储系统级可信的公钥或证书,以支持对关键安全操作的验证(如内核模块签名验证、安全启动、完整性校验等)。初始化两种keyring:builtin_trusted_keys和secondary_trusted_keys。
system_trusted_keyring_init
keyring_alloc--分配并初始化keyring对象(.builtin_trusted_keys和.secondary_trusted_keys),为内核或用户空间程序动态创建一个具有指定属性的keyring,用于几种管理秘钥或其他安全凭据。
key_alloc
key_user_lookup
security_key_alloc
refcount_inc
atomic_inc
key_alloc_serial
- builtin_trusted_keys:存储内核编译时内置的可信公钥或证书,是系统信任链的根。用于验证内核模块签名、驱动签名等核心安全操作。
- secondary_trusted_keys:存储从外部动态加载的次级可信密钥(如 Secure Boot 的 MOK(Machine Owner Key)或由管理员手动添加的密钥),用于扩展信任链。
2.3 加载系统可信证书到keyring
在system_certificates.S中定义了system_certificate_list内容:
- 来自certs/signing_key.x509。
- 来自certs/x509_certificate_list,通过CONFIG_SYSTEM_TRUSTED_KEYS配置文件路径。
.align 8
.globl system_certificate_list
system_certificate_list:
__cert_list_start:
#ifdef CONFIG_MODULE_SIG
.incbin "certs/signing_key.x509"
#endif
.incbin "certs/x509_certificate_list"
__cert_list_end:
.align 8
.globl system_certificate_list_size
system_certificate_list_size:
#ifdef CONFIG_64BIT
.quad __cert_list_end - __cert_list_start
#else
.long __cert_list_end - __cert_list_start
#endif
- system_certificate_list:指向编译时内置的 X.509 证书数据的指针。
- system_certificate_list_size:表示 `system_certificate_list` 数据的总大小(字节数),用于确定证书数据的边界。
load_system_certificate_init()在内核初始化阶段,加载编译到内核中的 X.509 证书列表到系统信任的密钥环(如 `builtin_trusted_keys`)。
load_system_certificate_list
x509_load_certificate_list
make_key_ref
key_create_or_update
__key_create_or_update
key_type_lookup
key_ref_to_ptr
key_check
key_set_index_key
__key_link_lock
__key_link_begin
key_permission
find_key_to_update
key_alloc
__key_instantiate_and_link
ima_post_key_create_or_update
make_key_ref
2.4 生成builtin_trusted_keys
Cryptographic API
Certificates for signature checking
Provide system-wide ring of trusted keys
Additional X.509 keys for default system keyring--配置x509格式签名证书路径,编译时即可编译到.builtin_trusted_keys段中。
Reserve area for inserting a certificate without recompiling
Number of bytes to reserve for the extra certificate
Provide a keyring to which extra trustable keys may be added
Provide system-wide ring of blacklisted keys
1. 生成用于签名的X.509密钥对:
# 生成私钥
openssl genrsa -out my_signing_key.pem 4096
# 生成自签名证书(公钥)
openssl req -new -x509 -key my_signing_key.pem -out my_signing_key.x509 -subj "/CN=My Custom Key/"
2. 复制DER文件到内核源码目录。
3. 修改内核配置CONFIG_SYSTEM_TRUSTED_KEYS指向my_signing_key.x509。
4. 编译内核,启动查看秘钥:
sudo keyctl list %:.builtin_trusted_keys
查看/proc/keys,同样可以看到.builtin_trusted_keys。
2.5 /proc/keys和/proc/key-users初始化
key_proc_init创建key相关proc节点:
key_proc_init
->proc_create_seq--创建/proc/keys和/proc/key-users两个proc节点。
/proc/keys显示当前内核中所有已注册密钥的详细信息。
每行代表一个密钥,字段用冒号分隔:
<key_ID> <flags> <usage_count> <timeout> <permissions> <UID> <GID> <type> <description> <payload_info>
字段说明:
| 字段 | 说明 | 示例 |
|---|---|---|
| key_ID | 密钥的唯一标识符(十六进制) | 01f3a5e1 |
| flags | 密钥状态标志(见下表) | 位置: 1 2 3 4 5 6 7 |
| usage_count | 当前引用计数 | 3 |
| permissions | 权限位(十六进制) | 3f3f0000 |
| UID | 所有者用户ID | 0 (root) |
| GID | 所有者组ID | 0 (root) |
| type | 密钥类型 | keyring, asymmetric, user |
| description | 密钥描述 | builtin_trusted_keys |
| payload_info | 负载摘要信息 | 12 (user密钥数据长度) |
flags显示时:- = 未设置,字母 = 已设置。
-
I - Instantiated (已实例化)
-
R - Revoked (已撤销)
-
D - Dead (待销毁)
-
Q - 计入用户配额
-
U - Under construction (构建中)
-
N - Negative key (负密钥)
-
i - Invalidated (已失效)
/proc/key-users显示每个用户的密钥配额使用情况。
每行代表一个用户,字段用空格分隔:
<UID>: <usage> <nkeys>/<nikeys> <qnkeys>/<maxkeys> <qnbytes>/<maxbytes>
字段说明
| 字段 | 说明 | 示例 |
|---|---|---|
| UID | 用户ID | 0 (root) |
| usage | 内核内部使用计数 | 10 |
| nkeys/nikeys | 总密钥数/已实例化密钥数 | 15/14 |
| qnkeys/maxkeys | 已使用密钥配额/最大配额 | 15/200 |
| qnbytes/maxbytes | 已使用存储配额/最大配额(字节) | 2048/20000 |
3 数据结构
struct key 表示内核中的一个密钥实体(包含密钥数据、权限、引用计数等),Keyring 本质上是一种特殊类型的密钥,用 struct key 表示,其类型为 key_type_keyring。
struct key { refcount_t usage; /* 引用计数 -- 跟踪密钥被引用的次数,归零时触发销毁 */ key_serial_t serial; /* 密钥序列号 -- 用户空间操作密钥的唯一标识符 */ union { struct list_head graveyard_link; /* 废弃链表节点 -- 用于撤销队列管理 */ struct rb_node serial_node; /* 红黑树节点 -- 按序列号组织全局密钥树 */ }; #ifdef CONFIG_KEY_NOTIFICATIONS struct watch_list *watchers; /* 监视器列表 -- 支持密钥变更通知机制 */ #endif struct rw_semaphore sem; /* 读写信号量 -- 保护密钥状态的并发修改 */ struct key_user *user; /* 密钥所有者 -- 关联配额和资源统计 */ void *security; /* 安全域指针 -- 存储LSM模块的安全数据(如SELinux) */ union { time64_t expiry; /* 过期时间 -- 密钥自动失效的时间戳(0=永不过期) */ time64_t revoked_at; /* 撤销时间 -- 密钥被标记为撤销的时间戳 */ }; time64_t last_used_at; /* 最后使用时间 -- 实现LRU淘汰机制的关键字段 */ kuid_t uid; /* 用户ID -- 密钥所有者的用户标识 */ kgid_t gid; /* 组ID -- 密钥所有者的组标识 */ key_perm_t perm; /* 权限位 -- 定义所有者/组/其他用户的访问权限(VIEW/READ/WRITE等) */ unsigned short quotalen; /* 配额长度 -- 此密钥占用的配额单位数 */ unsigned short datalen; /* 有效载荷长度 -- payload数据的实际长度(可能与RCU数据不同) */ short state; /* 密钥状态 -- 正值表示有效状态,负值表示错误码 */ unsigned long flags; /* 状态标志位 -- 关键状态标识(见下方宏定义) */ // 标志位宏定义(部分关键标志) #define KEY_FLAG_DEAD 0 /* 类型已删除 -- 关联的key_type已被注销 */ #define KEY_FLAG_REVOKED 1 /* 已撤销 -- 密钥被管理员显式撤销 */ #define KEY_FLAG_INVALIDATED 5 /* 已失效 -- 密钥内容被标记为无效 */ #define KEY_FLAG_BUILTIN 6 /* 内置密钥 -- 内核启动时预置的不可变密钥 */ #define KEY_FLAG_UID_KEYRING 9 /* 用户密钥环 -- 表示此密钥是用户/会话密钥环 */ /* 密钥标识联合体 */ union { struct keyring_index_key index_key; /* 索引结构 -- 加速密钥查找 */ struct { unsigned long hash; /* 哈希值 -- 基于描述符计算的快速查找键 */ unsigned long len_desc; /* 描述符长度 -- description字符串长度 */ struct key_type *type; /* 密钥类型 -- 指向操作函数集(如user/keyring) */ struct key_tag *domain_tag; /* 操作域标签 -- 支持密钥分域隔离(如NFS) */ char *description; /* 描述符 -- 人类可读的密钥标识字符串 */ }; }; /* 有效载荷联合体 */ union { union key_payload payload; /* 通用载荷 -- 存储实际密钥数据(如加密密钥) */ struct { /* 密钥环专用结构 */ struct list_head name_link; /* 名称链表 -- 按描述符维护子密钥链表 */ struct assoc_array keys; /* 关联数组 -- 存储子密钥的高效数据结构 */ }; }; struct key_restriction *restrict_link; /* 链接限制 -- 控制密钥环添加子密钥的规则 */ };
struct key_type 定义密钥类型的操作和行为(如加解密、销毁等逻辑)。key_type 是 Linux Keyring 子系统的核心抽象,定义了特定类型密钥的操作接口和行为规范。它在内核中的完整定义如下:
struct key_type { const char *name; -- // 密钥类型名称(如"keyring", "user") size_t def_datalen; -- // 默认负载长度(用于配额预计算) unsigned int flags; -- // 类型标志(如网络域/立即回收等) int (*vet_description)(const char *description); -- // 校验密钥描述字符串 int (*preparse)(struct key_preparsed_payload *prep); -- // 预解析用户态密钥数据 void (*free_preparse)(struct key_preparsed_payload *prep); -- // 释放预解析数据 int (*instantiate)(struct key *key, struct key_preparsed_payload *prep); -- // 初始化密钥实体 int (*update)(struct key *key, struct key_preparsed_payload *prep); -- // 更新密钥数据 int (*match_preparse)(struct key_match_data *match_data); -- // 预解析密钥匹配数据 void (*match_free)(struct key_match_data *match_data); -- // 释放匹配预解析数据 void (*revoke)(struct key *key); -- // 撤销密钥(清除敏感数据) void (*destroy)(struct key *key); -- // 销毁密钥私有数据 void (*describe)(const struct key *key, struct seq_file *p); -- // 密钥描述输出 long (*read)(const struct key *key, char *buffer, size_t buflen); -- // 读取密钥数据 request_key_actor_t request_key; -- // 自定义密钥请求处理函数 struct key_restriction *(*lookup_restriction)(const char *params); -- // 查找密钥操作限制 int (*asym_query)(const struct kernel_pkey_params *params, struct kernel_pkey_query *info); -- // 非对称密钥查询 int (*asym_eds_op)(struct kernel_pkey_params *params, const void *in, void *out); -- // 非对称加密/解密/签名 int (*asym_verify_signature)(struct kernel_pkey_params *params, const void *in, const void *in2); -- // 非对称签名验证 struct list_head link; -- // 类型链表节点(连接所有注册类型) struct lock_class_key lock_class;-- // 密钥信号量锁类型 } __randomize_layout;
4 Key相关API
1. 密钥(key)管理
struct key *key_alloc(struct key_type *type, const char *desc, ...); -- 分配新密钥对象(未初始化) int key_instantiate_and_link(struct key *key, ...); -- 初始化密钥并链接到密钥环 void key_put(struct key *key); -- 减少引用计数(计数归零时触发销毁) struct key *key_get(struct key *key); -- 增加密钥引用计数 void key_revoke(struct key *key); -- 立即使密钥失效(标记为不可用) int key_update(key_ref_t key_ref, ...); -- 更新密钥负载数据 void key_invalidate(struct key *key); -- 标记密钥为永久无效 long key_read(const struct key *key, ...); -- 读取密钥数据到用户空间
2. 密钥类型(key_type)
int register_key_type(struct key_type *ktype); -- 向内核注册新密钥类型 void unregister_key_type(struct key_type *ktype); -- 注销密钥类型 int key_type_vet_description(const struct key_type *ktype, const char *desc); -- 校验密钥描述符有效性 // 关键回调(在key_type结构体中定义): int (*preparse)(struct key_preparsed_payload *prep); -- 预解析用户空间密钥数据 void (*free_preparse)(struct key_preparsed_payload *prep); -- 释放预解析资源 int (*instantiate)(struct key *key, ...); -- 初始化密钥负载 int (*update)(struct key *key, ...); -- 更新密钥数据 void (*destroy)(struct key *key); -- 销毁密钥私有数据 int (*asym_eds_op)(struct kernel_pkey_params *params, ...); -- 非对称加密/解密/签名
3. 密钥环(keyring)操作
struct key *keyring_alloc(const char *desc, ...); -- 创建新密钥环对象 int key_link(struct key *keyring, struct key *key); -- 将密钥链接到密钥环 int key_unlink(struct key *keyring, struct key *key); -- 从密钥环移除密钥 int keyring_clear(struct key *keyring); -- 清空密钥环所有密钥 key_ref_t keyring_search(key_ref_t keyring_ref, ...); -- 在密钥环中递归搜索密钥 key_ref_t keyring_search_rcu(key_ref_t keyring_ref, ...); -- RCU保护的密钥搜索(无锁) int keyring_restrict(key_ref_t keyring_ref, ...); -- 设置密钥环操作限制
4. 密钥搜索与匹配
key_ref_t lookup_user_key(key_serial_t id, ...); -- 根据用户空间ID查找密钥 int key_match_preparse(struct key_match_data *match_data); -- 预解析密钥匹配数据 int key_match(const struct key *key, ...); -- 执行密钥匹配逻辑 bool search_nested_keyrings(struct key *keyring, ...); -- 递归搜索嵌套密钥环
5. 权限与安全控制
int key_permission(const key_ref_t key_ref, unsigned perm); -- 检查密钥操作权限 bool key_validate(const struct key *key); -- 验证密钥状态有效性 int key_task_permission(const key_ref_t key_ref, ...); -- 检查进程密钥权限 int key_create(key_ref_t keyring_ref, ...); -- 安全创建密钥(含权限检查)
6. 引用管理
key_ref_t make_key_ref(const struct key *key, bool possessed); -- 创建密钥引用对象 struct key *key_ref_to_ptr(const key_ref_t key_ref); -- 安全转换引用为指针 int key_create_or_update(key_ref_t keyring_ref, ...); -- 创建/更新密钥的封装
7. 用户空间交互
// keyring负载定义(include/linux/key.h) struct keyring_payload { struct assoc_array keys; -- 关联数组存储子密钥指针 }; // 密钥环类型定义(security/keys/keyring.c) struct key_type key_type_keyring = { .name = "keyring", -- 密钥环类型标识 .preparse = keyring_preparse, -- 密钥环专用预解析 .free_preparse = keyring_free_preparse, -- 释放密钥环预解析 .instantiate = keyring_instantiate, -- 密钥环实例化 .destroy = keyring_destroy, -- 密钥环销毁 .describe = keyring_describe, -- 密钥环描述 .read = keyring_read, -- 读取密钥环内容 };
5 其他Key/Keyring
5.1 Platform Keyring
platform_trusted_keys:存储与硬件平台相关的可信密钥(如 TPM 模块中的证书或固件提供的密钥),用于验证固件更新、硬件驱动等。
platform_keyring_init
integrity_init_keyring
__integrity_init_keyring
keyring_alloc
set_platform_trusted_keys
machine_trusted_keys:特定于虚拟机或容器环境的密钥环,用于验证虚拟设备(如虚拟 TPM)或容器镜像的签名。
5.2 Asymmetric Key(非对称密钥)
asymmetric_key_init注册asymmetric密钥类型:
asymmetric_key_init ->register_key_type -- 注册非对称密钥类型到内核密钥子系统 ->key_type_asymmetric -- 定义非对称密钥操作的标准结构体
asymmetric密钥类型操作函数如下:
struct key_type key_type_asymmetric = { .name = "asymmetric", .preparse = asymmetric_key_preparse, .free_preparse = asymmetric_key_free_preparse, .instantiate = generic_key_instantiate, .match_preparse = asymmetric_key_match_preparse, .match_free = asymmetric_key_match_free, .destroy = asymmetric_key_destroy, .describe = asymmetric_key_describe, .lookup_restriction = asymmetric_lookup_restriction, .asym_query = query_asymmetric_key, .asym_eds_op = asymmetric_key_eds_op, .asym_verify_signature = asymmetric_key_verify_signature, };
x509_key_init注册解析x509格式秘钥的解析器:
x509_key_init ->register_asymmetric_key_parser -- 将x509解析器注册到非对称密钥解析器列表 ->x509_key_parser -- X.509证书专用解析器结构 ->x509_key_preparse -- X.509证书预处理主函数 ->x509_cert_parse -- 执行证书解析的核心函数 ->kzalloc -- 动态分配证书解析所需内存空间 ->asn1_ber_decoder -- 使用BER规则解码ASN.1格式证书数据 ->kmemdup -- 复制解码后的公钥等关键数据到安全内存区 ->x509_get_sig_params -- 提取并验证证书签名算法参数 ->asymmetric_key_generate_id -- 生成基于颁发者和序列号的唯一密钥标识 ->x509_check_for_self_signed -- 检测证书是否自签名 ->public_key_verify_signature -- 用公钥验证证书签名有效性 ->填充struct key_preparsed_payload数据 -- 将解析结果装配到密钥预备数据结构
struct asymmetric_key_subtype定义非对称密钥的操作集合(函数指针),用于实现特定类型密钥(如公钥、私钥、证书)的定制化行为。它是内核密钥子系统的核心扩展机制。
struct asymmetric_key_subtype { const char *name; // 子类型名称(如 "public_key") void (*describe)(const struct key *key, struct seq_file *m); // 密钥描述 void (*destroy)(void *payload); // 密钥销毁时清理资源 int (*query)(const struct kernel_pkey_params *params, struct kernel_pkey_query *info); // 查询密钥能力 int (*eds_op)(struct kernel_pkey_params *params, const void *in, void *out); // 加密/解密/签名操作 int (*verify_signature)(const struct key *key, const struct public_key_signature *sig); // 验证签名 };
public_key_subtype 是 asymmetric_key_subtype 的一个具体实例,专用于处理 公钥(Public Key) 的操作。它是内核中公钥的默认实现。
static struct asymmetric_key_subtype public_key_subtype = { .name = "public_key", .describe = public_key_describe, // 打印公钥信息 .destroy = public_key_destroy, // 清理公钥资源 .query = public_key_query, // 查询公钥能力 .eds_op = public_key_eds_op, // 公钥加密(通常仅支持加密) .verify_signature = public_key_verify_signature, // 验证签名 };
6 Keyctl使用说明
密钥类型包括:
- user:用户自定义的密钥(明文存储)。
- keyring:密钥容器(类似目录)。
- logon:用于加密文件系统的密钥(如 eCryptfs)。
- big_key:存储大容量数据(超过 4KB)。
- 创建密钥
keyctl add <type> <description> <data> <keyring>
创建一个用户密钥,内容为 "mysecret",关联到当前会话密钥环(@s)
keyctl add user mykey "mysecret" @s
- 列出密钥
keyctl list <keyring>
列出当前用户会话密钥环中的密钥
keyctl list @s
列出所有进程可访问的密钥
keyctl list @us
- 读取密钥内容
keyctl print <key-id>
读取密钥 ID 为 12345678 的内容
keyctl print 12345678
- 更新密钥
keyctl update <key-id> <new-data>
更新密钥 ID 为 12345678 的内容为 "newsecret"
keyctl update 12345678 "newsecret"
- 删除密钥
keyctl unlink <key-id> <keyring>
从当前会话密钥环中删除密钥 12345678
keyctl unlink 12345678 @s
浙公网安备 33010602011771号