You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux内核AES加解密模块问题:解密结果与明文不一致

排查Linux内核模块AES解密不匹配的问题

我之前在内核里开发AES加解密模块时,也碰到过一模一样的情况——所有API都返回成功,但解密出来的结果就是和明文对不上。结合你的日志和常见的内核AES坑,给你几个最可能的排查方向:

1. 初始化向量(IV)的一致性问题

如果你的AES用的是CBC、CFB这类需要IV的模式,加密和解密必须使用完全相同的初始IV。内核里的crypto_cipher上下文在加密过程中会自动更新IV(用当前块的密文作为下一块的IV),加密完成后上下文里的IV已经不是初始值了。如果解密时直接复用这个被修改过的IV,前16字节(AES块大小)的解密结果肯定会出错,后续块也会跟着乱。

正确的做法是:加密前先把初始IV备份一份,解密前将备份的IV重新设置到解密上下文(或者同一个上下文)中。

2. 算法模式与参数不匹配

确认加密和解密使用的是完全相同的算法模式:比如加密用的是"aes-cbc",解密就不能用"aes-ecb";密钥长度也要对应(128/192/256位),虽然crypto_cipher_setkey返回成功,但如果密钥长度和算法要求不匹配,某些内核版本可能不会报错但会产生错误的加解密结果。

另外,要确保你分配的cipher handle是正确的,比如:

struct crypto_cipher *tfm = crypto_alloc_cipher("aes-cbc", 0, 0);

3. 内存对齐问题

很多内核AES实现(尤其是使用硬件加速的版本)要求明文、密文、密钥的内存地址是16字节对齐的。如果你的数据没有对齐,虽然API会返回成功,但底层的加解密操作会出现隐式错误,导致结果不符。

解决方法是在声明数据时加上__aligned(16)属性:

u8 plaintext[16] __aligned(16);
u8 ciphertext[16] __aligned(16);
u8 key[16] __aligned(16);

4. 数据长度与填充问题

AES是块加密算法,每块固定16字节。如果你的明文长度不是16的整数倍,需要手动做填充(比如PKCS#7填充),内核的AES API本身不会自动处理填充。从你的日志看,明文刚好是16字节,这一点没问题,但如果后续处理多块数据,一定要确保加密和解密的数据长度完全一致,且填充/去填充逻辑正确。

另外,检查解密时传入的密文是否完整——你的日志里解密部分的密文被截断了(decrypting -> encrypted: 91 1c 91 51 3c 22 77 dd 82 b4 b7 52 c7 9...),要确认解密时传入的密文长度和加密后的密文长度完全相同。

5. 上下文的正确复用

如果你复用同一个crypto_cipher上下文做加解密,一定要确保每次操作前重置必要的参数(比如IV),并且重新设置密钥(如果有修改的话)。不要假设上下文会自动恢复到初始状态,加密后的上下文状态已经被修改,直接用来解密会导致错误。

参考示例代码

这里给你一个完整的内核AES-CBC加解密示例,涵盖了上面提到的所有注意点:

#include <linux/crypto.h>
#include <linux/module.h>
#include <linux/string.h>

static int __init aes_test_init(void) {
    struct crypto_cipher *tfm;
    // 128位密钥
    u8 key[16] __aligned(16) = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                                0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
    // 初始IV
    u8 iv[16] __aligned(16) = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
                               0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f};
    u8 iv_backup[16] __aligned(16);
    // 你的明文
    u8 plaintext[16] __aligned(16) = {0xbb, 0x2b, 0xd7, 0xfb, 0xfc, 0xc6, 0x0c, 0xf6,
                                      0x82, 0x92, 0xcd, 0xe1, 0x62, 0x2f, 0x9f, 0x95};
    u8 ciphertext[16] __aligned(16);
    u8 decrypted[16] __aligned(16);
    int ret;

    // 分配CBC模式的AES cipher handle
    tfm = crypto_alloc_cipher("aes-cbc", 0, 0);
    if (IS_ERR(tfm)) {
        pr_err("Failed to allocate AES-CBC cipher handle\n");
        return PTR_ERR(tfm);
    }

    // 设置密钥
    ret = crypto_cipher_setkey(tfm, key, sizeof(key));
    if (ret) {
        pr_err("Failed to set AES key: %d\n", ret);
        goto free_tfm;
    }

    // 备份初始IV
    memcpy(iv_backup, iv, sizeof(iv));

    // 加密:注意crypto_cipher_encrypt_one会修改IV,所以要传入正确的初始IV
    memcpy(ciphertext, plaintext, sizeof(plaintext));
    crypto_cipher_encrypt_one(tfm, ciphertext, plaintext);

    pr_info("Encrypted: ");
    for (int i = 0; i < 16; i++) {
        pr_cont("%02x ", ciphertext[i]);
    }
    pr_cont("\n");

    // 重置IV为初始值,准备解密
    memcpy(iv, iv_backup, sizeof(iv));
    // 解密
    memcpy(decrypted, ciphertext, sizeof(ciphertext));
    crypto_cipher_decrypt_one(tfm, decrypted, ciphertext);

    pr_info("Decrypted: ");
    for (int i = 0; i < 16; i++) {
        pr_cont("%02x ", decrypted[i]);
    }
    pr_cont("\n");

    // 验证结果
    if (memcmp(decrypted, plaintext, sizeof(plaintext)) == 0) {
        pr_info("AES decryption matched plaintext!\n");
        ret = 0;
    } else {
        pr_err("Decryption result mismatch!\n");
        ret = -EINVAL;
    }

free_tfm:
    crypto_free_cipher(tfm);
    return ret;
}

static void __exit aes_test_exit(void) {
    pr_info("AES test module exited\n");
}

module_init(aes_test_init);
module_exit(aes_test_exit);
MODULE_LICENSE("GPL");

你可以对比自己的代码,看看有没有遗漏上面提到的细节,尤其是IV的备份和重置、内存对齐这两点,大概率是问题所在。

内容的提问来源于stack exchange,提问作者avee137

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:23:38