Linux内核AES加密算法使用指南
Linux内核AES加密算法用法
在 Linux 内核中使用 AES 加密算法主要通过 内核加密 API 实现。这个 API 提供了一个抽象层,允许内核模块(如文件系统加密 dm-crypt/LUKS、IPSec、磁盘加密等)使用各种加密算法,包括 AES,而无需关心底层具体的实现(是软件实现还是硬件加速)。
以下是使用内核 AES 加密的基本步骤和要点:
核心组件:
struct crypto_skcipher
: 这是表示对称密钥加密(如 AES)上下文的句柄。它封装了算法、密钥、操作模式(如 CBC, XTS, CTR, GCM 等)等信息。struct skcipher_request
: 这是用于封装单次加密或解密操作请求的结构。它包含输入/输出数据的散列表(struct scatterlist
)、初始化向量(IV)、异步操作完成后的回调函数等信息。struct scatterlist
: 用于描述分散/聚集 I/O 的数据缓冲区。内核加密 API 通常操作的是scatterlist
而不是直接的线性内存地址,以高效处理可能分散在物理内存中的数据。
基本使用流程:
分配算法句柄 (
crypto_alloc_skcipher
)1
2struct crypto_skcipher *tfm;
tfm = crypto_alloc_skcipher("aes-<mode>-<keylength>", 0, 0);"aes-<mode>-<keylength>"
: 指定算法名称字符串。常见组合:"ecb(aes)"
: AES ECB 模式 (通常不推荐用于安全加密)。"cbc(aes)"
: AES CBC 模式。"ctr(aes)"
: AES CTR 模式。"xts(aes)"
: AES XTS 模式 (常用于磁盘加密)。"gcm(aes)"
: AES GCM 模式 (带认证的加密)。"pcbc(aes)"
,"lrw(aes)"
,"ofb(aes)"
,"cfb(aes)"
等。- 密钥长度:通常在模式名后隐含指定(如
cbc(aes)
需要调用setkey
时指定长度),但有些名字明确包含长度(如"aes-xts-plain64"
常用于 dm-crypt,但实际密钥长度由setkey
决定)。最重要的是setkey
调用时传递的密钥长度必须与所选算法变体期望的长度一致(128, 192, 256 位)。
0, 0
: 通常表示使用默认实现(优先硬件加速)。可以指定CRYPTO_ALG_ASYNC
标志启用异步操作(利用硬件加速时常用)。
设置密钥 (
crypto_skcipher_setkey
)1
2
3
4
5
6
7int err;
const u8 *key = ...; // 指向你的密钥数据的指针
unsigned int keylen = ...; // 密钥长度(字节数):16 (128-bit), 24 (192-bit), 32 (256-bit)
err = crypto_skcipher_setkey(tfm, key, keylen);
if (err) {
// 处理错误:无效密钥长度或不支持等
}- 关键点:
keylen
必须 与你选择的算法变体(在crypto_alloc_skcipher
时指定)所要求的长度完全匹配。否则会返回-EINVAL
。
- 关键点:
准备请求 (
skcipher_request_alloc
+ 初始化)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15struct skcipher_request *req;
struct scatterlist sg_in, sg_out;
DECLARE_CRYPTO_WAIT(wait); // 用于同步等待的辅助结构
req = skcipher_request_alloc(tfm, GFP_KERNEL);
if (!req) {
// 处理内存分配失败
}
// 初始化输入和输出的 scatterlist (假设源数据在 src, 目标在 dst, 长度 len)
sg_init_one(&sg_in, src, len);
sg_init_one(&sg_out, dst, len);
// 将请求关联到 tfm
skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP,
crypto_req_done, &wait);
skcipher_request_set_crypt(req, &sg_in, &sg_out, len, iv);skcipher_request_alloc
: 分配一个请求结构并将其与tfm
关联。sg_init_one
: 初始化一个scatterlist
表示单个线性缓冲区(src
和dst
是内核虚拟地址)。如果数据分散,使用sg_init_table
等更复杂的操作。skcipher_request_set_callback
: 设置异步操作完成后的回调函数 (crypto_req_done
) 和一个上下文 (&wait
)。CRYPTO_TFM_REQ_MAY_BACKLOG
允许请求在硬件队列满时排队,CRYPTO_TFM_REQ_MAY_SLEEP
允许在内存分配时休眠。skcipher_request_set_crypt
: 这是核心配置函数:req
: 请求指针。&sg_in
: 输入数据(明文或密文)的散列表头指针。&sg_out
: 输出数据(密文或明文)的散列表头指针。len
: 要处理的数据长度(字节)。iv
: 指向初始化向量 (IV) 缓冲区的指针。IV 的长度和生成方式取决于所选的操作模式(如 CBC 需要 16 字节 IV)。
执行操作 (加密/解密)
- 加密:
1
2
3
4
5
6
7
8err = crypto_skcipher_encrypt(req);
if (err == -EINPROGRESS || err == -EBUSY) {
// 对于异步操作,等待完成
err = crypto_wait_req(err, &wait);
}
if (err) {
// 处理加密错误
} - 解密:
1
2
3
4
5
6
7err = crypto_skcipher_decrypt(req);
if (err == -EINPROGRESS || err == -EBUSY) {
err = crypto_wait_req(err, &wait);
}
if (err) {
// 处理解密错误(可能意味着认证失败 -GCM, 或数据损坏/密钥错误)
} crypto_skcipher_encrypt
/crypto_skcipher_decrypt
: 提交请求。如果返回-EINPROGRESS
或-EBUSY
,表示操作是异步的并在后台执行。crypto_wait_req
: 一个辅助函数,用于同步等待异步请求完成。它会阻塞当前线程直到操作完成或出错。crypto_req_done
回调会唤醒这个等待。
- 加密:
清理资源
1
2skcipher_request_free(req); // 释放请求结构
crypto_free_skcipher(tfm); // 释放算法句柄- 务必释放分配的资源以避免内存泄漏。
重要注意事项:
- 模式选择: AES 是一个分组密码。必须选择一个合适的操作模式 (CBC, XTS, CTR, GCM 等) 来加密超过一个块的数据。选择模式取决于安全需求(保密性、认证)、性能、并行化能力和具体应用场景(文件加密、网络协议等)。例如:
- 全盘加密 (dm-crypt/LUKS): 常用 XTS 模式。
- IPSec ESP: 常用 CBC 或 GCM 模式。
- 需要认证: 使用 GCM 或 CCM 等 AEAD 模式(通过
crypto_aead
API)。
- 密钥管理: 安全地生成、存储和销毁密钥是至关重要的。内核模块通常需要从用户空间(通过
netlink
,ioctl
等)或其他安全来源(如 TPM)获取密钥。切勿将密钥硬编码在内核中! - IV 管理: 对于大多数模式(CBC, CTR, XTS 等),IV 必须是唯一的(对于给定的密钥)。重复使用 IV 会严重破坏安全性。IV 通常不需要保密,但 GCM 的 nonce 有其特殊要求。IV 的生成和传递方式由协议或应用规范决定(例如,在文件加密中,IV 可能存储在文件头或由扇区号派生)。
- 数据对齐: 虽然现代内核实现通常能处理非对齐访问,但为了最佳性能,输入/输出缓冲区和 IV 缓冲区最好按算法块大小(AES 是 16 字节)对齐。
- 异步操作: 当使用硬件加速引擎时,加密/解密操作通常是异步的。使用回调函数和等待机制(如
crypto_wait_req
)是处理异步操作的标准方式。 - 错误处理: 仔细检查所有函数调用的返回值(
crypto_alloc_skcipher
,crypto_skcipher_setkey
,crypto_skcipher_encrypt/decrypt
)。crypto_skcipher_setkey
失败最常见的原因是密钥长度不匹配。 - AEAD (Authenticated Encryption with Associated Data): 如果需要同时提供保密性、完整性和认证(如 GCM 模式),应该使用
crypto_aead
API 而不是crypto_skcipher
API。它的使用方式类似但增加了关联数据 (AAD) 的设置。
示例片段 (简化概念):
1 |
|
总结:
在内核中使用 AES 加密涉及使用 crypto_skcipher
API(或 crypto_aead
用于认证加密)。关键步骤包括:选择合适的算法/模式字符串分配句柄 (crypto_alloc_skcipher
)、正确设置密钥 (crypto_skcipher_setkey
)、使用 scatterlist
准备输入/输出数据、分配并配置请求 (skcipher_request_alloc
, skcipher_request_set_crypt
)、设置 IV、执行操作 (crypto_skcipher_encrypt
/decrypt
) 并处理可能的异步等待、最后释放资源。务必谨慎处理密钥、IV 和错误代码。实际应用应参考内核源码树中的示例(如 drivers/crypto/
, crypto/tcrypt.c
, dm-crypt
驱动等)。
给一个示例
以下是一个完整的 Linux 内核模块示例,演示如何使用 AES-CBC 算法进行加密和解密操作。该模块在加载时执行加密和解密,并通过内核日志输出结果:
1 |
|
使用说明:
编译模块:
保存为aes_example.c
,使用以下 Makefile:1
2
3
4
5obj-m += aes_example.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean加载模块:
1
sudo insmod aes_example.ko
查看结果:
1
dmesg | tail -n 30
预期输出:
1 | Key : aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa |
关键点说明:
- 算法选择:使用
"cbc(aes)"
指定 AES-CBC 模式 - 密钥管理:
- 128-bit 密钥 (16字节)
- 通过
crypto_skcipher_setkey()
设置
- IV 处理:
- 固定大小 (16字节)
- 每次操作前重置(实际应用中必须唯一)
- 数据准备:
- 使用
scatterlist
处理缓冲区 - 数据长度必须是块大小(16字节)的倍数
- 使用
- 异步操作:
- 使用
crypto_wait_req()
处理异步完成
- 使用
- 资源管理:
- 正确释放请求和算法上下文
- 错误检查所有步骤
实际应用注意事项:
- 密钥安全:不要硬编码密钥,应从安全来源获取
- IV 生成:使用随机 IV(如
get_random_bytes()
),保证唯一性 - 数据填充:非对齐数据需要添加填充(如 PKCS#7)
- 性能优化:对于大数据,使用多个 scatterlist 条目
- 认证模式:如需完整性保护,改用
crypto_aead
API (GCM/CCM)
此示例演示了内核中 AES 加密的基本流程,实际使用时需根据具体场景调整安全参数和数据处理逻辑。
Linux内核crypto_cipher_encrypt_one接口用法及示例
Linux 内核 crypto_cipher_encrypt_one
接口用法及示例
crypto_cipher_encrypt_one
是 Linux 内核中用于单块加密的低级 API,它提供了一种简单直接的方式来执行块加密算法的单次加密操作。这个接口通常用于 ECB(电子密码本)模式,或作为构建更复杂加密模式的基础。
接口特点
- 简单性:直接的单块加密操作
- 同步操作:立即返回结果,无需异步处理
- 底层接口:适合构建更高级的加密模式或特定硬件操作
- 单块处理:每次只处理一个加密块(如 AES 的 16 字节)
函数原型
1 | void crypto_cipher_encrypt_one(struct crypto_cipher *tfm, u8 *dst, const u8 *src); |
使用步骤
- 分配密码对象:
crypto_alloc_cipher()
- 设置密钥:
crypto_cipher_setkey()
- 执行加密/解密:
crypto_cipher_encrypt_one()
/crypto_cipher_decrypt_one()
- 释放密码对象:
crypto_free_cipher()
完整示例代码
1 |
|
编译和使用
保存文件:将代码保存为
cipher_example.c
创建 Makefile:
1
2
3
4
5obj-m += cipher_example.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean编译模块:
1
make
加载模块:
1
sudo insmod cipher_example.ko
查看结果:
1
dmesg | tail -n 20
预期输出
1 | Key : aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa |
关键点说明
算法名称:
- 使用
"aes"
而不是"cbc(aes)"
或"ecb(aes)"
crypto_cipher
API 默认使用 ECB 模式
- 使用
单块操作:
- 每次只处理一个加密块(AES 为 16 字节)
- 不适合直接处理大数据流
同步操作:
- 函数立即执行并返回
- 不需要异步处理或回调机制
密钥设置:
- 密钥长度必须与算法要求一致
- AES 支持 16 (128-bit), 24 (192-bit), 32 (256-bit) 字节密钥
实际应用场景
- 硬件加速实现:在自定义硬件加密驱动中作为基础操作
- 构建加密原语:用于实现更高级的加密模式
- 密码学实验:测试和验证加密算法的基本功能
- 密钥派生操作:在某些密钥派生函数中使用
安全注意事项
ECB 模式限制:
- ECB 模式不提供语义安全性
- 相同明文块总是产生相同密文块
- 不适合直接用于加密敏感数据
仅限单块操作:
- 对于多块数据,需要实现链式操作(如 CBC、CTR)
- 实际应用中应使用更高级的 API(如
crypto_skcipher
)
密钥管理:
- 示例中硬编码密钥仅用于演示
- 实际应用中应从安全来源获取密钥
与 crypto_skcipher
的区别
特性 | crypto_cipher |
crypto_skcipher |
---|---|---|
操作模式 | 基本 ECB 模式 | 支持多种模式 (CBC, CTR, XTS 等) |
数据处理 | 单块 (16 字节) | 任意长度数据 |
操作类型 | 同步 | 支持异步操作 |
IV 支持 | 不支持 | 支持 |
使用场景 | 底层操作、硬件驱动 | 通用加密需求 |
性能 | 更轻量级 | 功能更全面但稍重 |
何时使用 crypto_cipher
- 实现自定义加密模式或协议
- 编写硬件加密驱动程序
- 需要直接访问底层块加密操作
- 性能敏感且只需 ECB 模式的场景
对于大多数内核加密需求,推荐使用更高级的 crypto_skcipher
API,它提供了更完整的加密解决方案和更好的安全性。