第一百七十二章:密钥管理
渡劫期涉及内核源码:
一
crypto API 指向的那道门,林小源推开了。门后不是密室,而是一座戒备森严的宝库。宝库的中央悬浮着一个巨大的环形结构——keyring,无数钥匙悬挂在环上,每一把都散发着不同颜色的光芒。
一个面容严肃的老者守在宝库入口。他的双手覆盖着一层金属光泽——那是 TPM 芯片的印记。
"我是 keyring 子系统,"老者的声音低沉而谨慎,"密钥的守护者。你刚才在 crypto API 那里看到了加密算法,但算法只是锁。钥匙在我这里。"
林小源走近那个环形结构。钥匙按照不同的类型分区悬挂:user 类型的钥匙由用户自己提供,trusted 类型的钥匙被 TPM 芯片包裹着一层无法穿透的外壳,encrypted 类型的钥匙本身就是加密的。
"trusted 密钥……"林小源伸手想触碰,老者立刻挡住了他。
"别碰。那把钥匙永远不会以明文形式离开 TPM。即使内核被攻破,攻击者也拿不到它的原始数据。TPM 是硬件级别的信任根——密钥在 TPM 内部生成,在 TPM 内部使用,从不暴露在外。"
"更准确地说,"老者又补了一句,"trusted key 让用户态只能看见、保存、重新加载加密 blob。普通 trusted key 的明文 key-data 在内核使用时仍可能出现在系统内存;protected key 才会把明文限制在 trust source 边界内。信任源不只有 TPM,也可能是 TEE、CAAM、DCP、PKWM。"
宝库初试
trusted/encrypted keys 中,用户态通常保存和加载的是明文密钥,还是加密 blob?
/*
* 内核 keyring 子系统:
*
* 密钥类型:
* user — 用户密钥
* logon — 登录密钥
* big_key — 大密钥
* asymmetric — 非对称密钥
* encrypted — 加密密钥
* trusted — 可信密钥
*
* keyring 类型:
* 进程 keyring — 进程私有
* 会话 keyring — 会话共享
* 用户 keyring — 用户共享
*
* 密钥操作:
* add_key() — 添加密钥
* request_key() — 请求密钥
* keyctl() — 密钥控制
*
* 使用场景:
* dm-crypt — 磁盘加密密钥
* IPsec — 网络加密密钥
* Kerberos — 认证密钥
* TLS — 会话密钥
*/
/* 模拟密钥 */
struct key {
char type[32];
char description[64];
unsigned char data[256];
int data_len;
int permissions; /* rwx */
};
struct keyring {
char name[32];
struct key keys[10];
int key_count;
};
void add_key(struct keyring *kr, const char *type,
const char *desc, const unsigned char *data, int len) {
if (kr->key_count >= 10) return;
struct key *k = &kr->keys[kr->key_count];
strncpy(k->type, type, 31);
strncpy(k->description, desc, 63);
memcpy(k->data, data, len < 256 ? len : 256);
k->data_len = len < 256 ? len : 256;
k->permissions = 0600; /* rw------- */
kr->key_count++;
}
struct key *request_key(struct keyring *kr, const char *desc) {
for (int i = 0; i < kr->key_count; i++) {
if (strcmp(kr->keys[i].description, desc) == 0)
return &kr->keys[i];
}
return NULL;
}
void print_key(struct key *k) {
printf(" 类型: %s\n", k->type);
printf(" 描述: %s\n", k->description);
printf(" 长度: %d 字节\n", k->data_len);
printf(" 权限: %o\n", k->permissions);
}
printf("=== 密钥管理 — 保护密钥 ===\n\n");
/* 创建进程 keyring */
struct keyring proc_keyring = {
.name = "进程 keyring",
.key_count = 0
};
/* 添加密钥 */
unsigned char aes_key[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
};
add_key(&proc_keyring, "user", "disk-encryption-key",
aes_key, sizeof(aes_key));
unsigned char ipsec_key[] = {
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
};
add_key(&proc_keyring, "user", "ipsec-sa-key",
ipsec_key, sizeof(ipsec_key));
printf("--- 进程 keyring ---\n");
printf("名称: %s\n", proc_keyring.name);
printf("密钥数量: %d\n\n", proc_keyring.key_count);
printf("--- 添加的密钥 ---\n");
for (int i = 0; i < proc_keyring.key_count; i++) {
print_key(&proc_keyring.keys[i]);
printf("\n");
}
printf("--- 请求密钥 ---\n");
struct key *k = request_key(&proc_keyring, "disk-encryption-key");
if (k) {
printf("找到密钥:\n");
print_key(k);
}
printf("\n--- keyring 类型 ---\n");
printf("进程 keyring:\n");
printf(" 进程私有\n");
printf(" fork 时继承\n\n");
printf("会话 keyring:\n");
printf(" 会话共享\n");
printf(" 登录时创建\n\n");
printf("用户 keyring:\n");
printf(" 用户共享\n");
printf(" 持久存储\n\n");
printf("--- 密钥类型 ---\n");
printf("user: 用户提供的密钥\n");
printf("trusted: TPM 保护的密钥\n");
printf("encrypted: 加密的密钥\n");
printf("asymmetric: 非对称密钥对\n\n");
printf("--- 使用方式 ---\n");
printf("命令行:\n");
printf(" keyctl add user mykey \"data\" @u\n");
printf(" keyctl show\n\n");
printf("代码:\n");
printf(" add_key(\"user\", \"desc\", data, len, keyring)\n");
printf(" request_key(\"user\", \"desc\", NULL, keyring)\n");#include <stdio.h>
#include <string.h>
/*
* 内核 keyring 子系统:
*
* 密钥类型:
* user — 用户密钥
* logon — 登录密钥
* big_key — 大密钥
* asymmetric — 非对称密钥
* encrypted — 加密密钥
* trusted — 可信密钥
*
* keyring 类型:
* 进程 keyring — 进程私有
* 会话 keyring — 会话共享
* 用户 keyring — 用户共享
*
* 密钥操作:
* add_key() — 添加密钥
* request_key() — 请求密钥
* keyctl() — 密钥控制
*
* 使用场景:
* dm-crypt — 磁盘加密密钥
* IPsec — 网络加密密钥
* Kerberos — 认证密钥
* TLS — 会话密钥
*/
/* 模拟密钥 */
struct key {
char type[32];
char description[64];
unsigned char data[256];
int data_len;
int permissions; /* rwx */
};
struct keyring {
char name[32];
struct key keys[10];
int key_count;
};
void add_key(struct keyring *kr, const char *type,
const char *desc, const unsigned char *data, int len) {
if (kr->key_count >= 10) return;
struct key *k = &kr->keys[kr->key_count];
strncpy(k->type, type, 31);
strncpy(k->description, desc, 63);
memcpy(k->data, data, len < 256 ? len : 256);
k->data_len = len < 256 ? len : 256;
k->permissions = 0600; /* rw------- */
kr->key_count++;
}
struct key *request_key(struct keyring *kr, const char *desc) {
for (int i = 0; i < kr->key_count; i++) {
if (strcmp(kr->keys[i].description, desc) == 0)
return &kr->keys[i];
}
return NULL;
}
void print_key(struct key *k) {
printf(" 类型: %s\n", k->type);
printf(" 描述: %s\n", k->description);
printf(" 长度: %d 字节\n", k->data_len);
printf(" 权限: %o\n", k->permissions);
}
int main() {
printf("=== 密钥管理 — 保护密钥 ===\n\n");
/* 创建进程 keyring */
struct keyring proc_keyring = {
.name = "进程 keyring",
.key_count = 0
};
/* 添加密钥 */
unsigned char aes_key[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
};
add_key(&proc_keyring, "user", "disk-encryption-key",
aes_key, sizeof(aes_key));
unsigned char ipsec_key[] = {
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
};
add_key(&proc_keyring, "user", "ipsec-sa-key",
ipsec_key, sizeof(ipsec_key));
printf("--- 进程 keyring ---\n");
printf("名称: %s\n", proc_keyring.name);
printf("密钥数量: %d\n\n", proc_keyring.key_count);
printf("--- 添加的密钥 ---\n");
for (int i = 0; i < proc_keyring.key_count; i++) {
print_key(&proc_keyring.keys[i]);
printf("\n");
}
printf("--- 请求密钥 ---\n");
struct key *k = request_key(&proc_keyring, "disk-encryption-key");
if (k) {
printf("找到密钥:\n");
print_key(k);
}
printf("\n--- keyring 类型 ---\n");
printf("进程 keyring:\n");
printf(" 进程私有\n");
printf(" fork 时继承\n\n");
printf("会话 keyring:\n");
printf(" 会话共享\n");
printf(" 登录时创建\n\n");
printf("用户 keyring:\n");
printf(" 用户共享\n");
printf(" 持久存储\n\n");
printf("--- 密钥类型 ---\n");
printf("user: 用户提供的密钥\n");
printf("trusted: TPM 保护的密钥\n");
printf("encrypted: 加密的密钥\n");
printf("asymmetric: 非对称密钥对\n\n");
printf("--- 使用方式 ---\n");
printf("命令行:\n");
printf(" keyctl add user mykey \"data\" @u\n");
printf(" keyctl show\n\n");
printf("代码:\n");
printf(" add_key(\"user\", \"desc\", data, len, keyring)\n");
printf(" request_key(\"user\", \"desc\", NULL, keyring)\n");
return 0;
}二
老者带林小源走进宝库的更深处。墙壁上挂着三种不同的 keyring——进程 keyring、会话 keyring、用户 keyring。
"密钥不是随便放的,"老者指着它们,"进程 keyring 跟着进程走,fork 时继承,进程结束就销毁。会话 keyring 跟着登录会话走,登出时清除。用户 keyring 持久存储,只要用户存在,密钥就在。"
林小源注意到进程 keyring 上挂着两把钥匙——一把标注着 "disk-encryption-key",另一把是 "ipsec-sa-key"。
"进程通过 把钥匙挂上来,需要的时候用 取。取的时候按描述符查找——'给我那把磁盘加密的钥匙'。"
"权限呢?"
"每把钥匙都有权限位,"老者伸出金属光泽的手指,"0600 表示只有属主能读写。 命令可以查看和管理。钥匙的生命周期也很重要——过期的钥匙应该销毁,不能留着。"
"内核里的每把 key 都有更完整的档案。"老者展开一张铜页,"serial number、type、description、owner、group、permission mask、expiry time、payload、state。状态也不止活着和死了:uninstantiated、instantiated、negative、expired、revoked、dead。后几种会被垃圾回收。"
"权限位也更细。"他继续说,"View 看属性,Read 读 payload 或 keyring 链表,Write 更新 payload 或修改 keyring 链接,Search 决定能否搜索,Link 决定能否挂接,Set Attribute 决定能否改 UID、GID 和权限。"
钥权之试
key 权限中,允许搜索 keyring 并找到 key 的权限叫什么?
三
宝库的尽头,林小源看到一扇紧锁的小门。门上只有一个钥匙孔,形状与 TPM trusted 密钥完全吻合。
"那是 dm-crypt 的根密钥,"老者说,"整个磁盘的加密都依赖于它。它被 trust source 封装,用户态只能保存加密 blob。启动时,TPM 可以把 key seal 到 PCR 度量值上——如果 bootloader、内核、initramfs 的度量符合预期,TPM 才会解封对应密钥。"
"如果有人篡改了启动链?"
"TPM 拒绝释放密钥。磁盘永远是加密的。"
林小源沉默了。密钥管理不是简单的存储问题——它涉及信任链、硬件根基、生命周期管理。一把钥匙泄漏,整个加密体系崩溃。一把钥匙丢失,所有数据永久不可访问。
"安全与可用性,"他低声说,"永远是跷跷板的两端。"
"encrypted key 则更像一只快一些的铁盒。"老者说,"它不要求 trust source,用 AES 和指定 master key 加解密。master key 可以是 trusted key,也可以是 user key;如果根不够可信,整只盒子的安全性也随之下降。"
"所以 keyring 不是单纯藏钥匙。它还要处理查找、配额、过期、撤销、SELinux 标签,以及找不到 key 时回调用户态补钥匙。"
老者点了点头,转身回到宝库入口。那些钥匙在环形结构上缓缓旋转,每转动一圈,就有一把旧钥匙被销毁,一把新钥匙被生成——密钥轮换,永不停歇。
根信之试
encrypted key 的安全性取决于用来加密它的哪个 key?
道藏笔记
内核启示
加密算法只是锁,钥匙在 keyring 子系统里。key 有 serial、type、description、权限、过期时间、payload 和 state;keyring 本身也是一种 key,里面挂着其他 key。user/logon/keyring 是基础类型,trusted/encrypted keys 则把用户态看到的内容变成加密 blob。
keyring 也有层次:线程、进程、会话、用户相关 keyring 的继承和生命周期不同。权限不只是 rwx,而包括 View、Read、Write、Search、Link、Set Attribute。trusted key 依赖 TPM/TEE 等 trust source;encrypted key 不要求 trust source,但安全性取决于 master key 的可信程度。
keyring 是保险箱——保护密钥不被窃取。
密钥管理之试
密钥管理中,用硬件信任根保存和保护密钥的模块缩写是什么?