对称加解密算法学习——AES篇

1. 前言

高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。

2. 原理

2.1 术语

  • 分组(Block):在 AES 中,每个数据块的大小为 128 位(即 16 字节),无论密钥长度是多少。加密过程是基于每个数据块进行的。
  • 密钥(Key):AES 使用对称加密,即加密和解密使用相同的密钥。AES 支持三种不同长度的密钥:128 位、192 位和 256 位。
  • 轮数(Rounds):加密过程中一系列操作的执行周期。每轮包括若干个操作步骤,用于对数据进行变换。根据密钥的长度,AES 会执行不同数量的轮。
  • 轮密钥(Round Key):从原始密钥(主密钥)通过密钥扩展算法生成的每一轮加密所需的密钥。在 AES 中,密钥扩展通过一系列的算法生成多个轮密钥,并且每轮加密时都会与相应的轮密钥进行混合。
  • S-Box(Substitution Box)字节替代(Substitution)步骤使用的替代表。它是通过非线性变换来替代输入的字节值,增加加密的复杂度,从而增强安全性。S-Box 的设计是为了抵抗差分密码分析和线性密码分析。
  • 加密模式(Mode of Operation):AES 加密算法用于处理多个数据块的方式。AES 不支持直接加密超过 128 位的数据,因此加密模式用于将多个数据块串联起来。常见的加密模式有**ECB(Electronic Codebook),CBC(Cipher Block Chaining),CFB(Cipher Feedback),OFB(Output Feedback),CTR(Counter),CTS(Cipher Text)**等。
  • 初始化向量(IV, Initialization Vector):一种随机值,用于加密操作中,以确保相同的明文在多次加密时产生不同的密文。特别是在像 CBC(密文块链接)模式下,IV 是加密的必要参数。
  • 填充(Padding):在加密过程中,如果明文的长度不是 128 位的整数倍时,需要填充额外的数据来使其长度对齐。常见的填充方法包括 PKCS7 填充。

2.2 加密

AES 是基于 分组密码 的设计,使用相同的密钥进行加密和解密。该算法具有128位的分组长度,支持128/192/256位的密钥长度。

AES加密过程是在一个4×4的字节矩阵上运作,这个矩阵又称为“体(state)”,其初值就是一个明文区块(矩阵中一个元素大小就是明文区块中的一个Byte)。

加密时,各轮AES加密循环(除最后一轮外)均包含4个步骤:

  1. 轮密钥加AddRoundKey:矩阵中的每一个字节都与该次回合密钥做异或操作,轮密钥是通过密钥扩展从初始密钥中生成的。
  2. 字节替代SubBytes:透过一个非线性的替换函数,使用 S-Box(字节替换表)对状态矩阵中的每个字节进行替换。
  3. 行移位ShiftRows:将矩阵中的每个横列进行循环式移位。
  4. 列混淆MixColumns:为了充分混合矩阵中各个直行的操作。这个步骤使用线性转换来混合每内联的四个字节。最后一个加密循环中省略MixColumns步骤,而以另一个AddRoundKey取代。

2.3 密钥扩展

密钥扩展是 AES 加密中非常重要的一步,其作用是根据原始的加密密钥生成多个轮密钥。原始的密钥会经过一系列的处理(例如,使用 轮常量S-Box)生成轮密钥,供每一轮加密使用。

  • AES-128 需要生成 11 个轮密钥(1 个初始密钥和 10 个轮密钥)。
  • AES-192 需要生成 13 个轮密钥(1 个初始密钥和 12 个轮密钥)。
  • AES-256 需要生成 15 个轮密钥(1 个初始密钥和 14 个轮密钥)。

2.4 解密

AES 的解密过程与加密过程类似,但操作顺序和某些步骤有所不同。解密过程通过逆操作来完成。

  • SubBytes 变为 InvSubBytes(逆字节替代)。
  • ShiftRows变为 InvShiftRows(逆行移位)。
  • MixColumns 变为 InvMixColumns(逆列混合)。
  • AddRoundKey 步骤与加密过程相同。

解密过程的关键在于使用了与加密相同的轮密钥,但执行顺序与加密过程相反。

2.5 安全性

AES 算法被认为是 高度安全 的。它抗拒常见的密码分析方法,如 已知明文攻击选择明文攻击。AES 的安全性基于以下几点:

  • 密钥长度越长,破解的难度越大。例如,AES-128 需要 2^128 次操作才能暴力破解,而 AES-256 需要 2^256 次操作,这几乎是不可能完成的任务。
  • 采用了复杂的 非线性操作分散技术,使得密文和明文之间的关系非常复杂,难以通过简单的模式识别来破解。

3. 步骤

3.1 密钥扩展(keyExpansion)

密钥扩展将固定N = (4 * Nk)字节的密钥扩展成(NR + 1)*16字节,密钥扩展简单流程如下:

1. 初始化

将原始的密钥(长度为 Nk 字)分成 4 字节一组存储在扩展密钥矩阵 W 中。每个字的大小为 4 字节(32 位)。(例如对于 AES-256,Nk = 8)。
假设输入密钥为 K = [K0, K1, K2, ..., K(Nk-1)](每个 Ki 为 32 位的字),将其直接复制到扩展密钥的前 Nk 个 32 位字:

W[i]=K[i] 0≤i<Nk
2. **轮常量 Rcon **

轮常量是与当前轮数相关的一个字节,它在 AES 密钥扩展过程中起到确保每个轮密钥不相同的作用。轮常量的生成规则如下:

  • Rcon[1] = 0x01
  • Rcon[i] = Rcon[i-1] * 2(对于 i > 1

因此,通常可以将轮常量定义为常量。

const byte Rcon[10] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36};
3. T函数

一个复杂函数,由三个步骤构成:字循环,字节代换和轮常量异或。

  • 字循环(Word Rotation):将1个字中的4个字节循环左移1个字节
Rotate(W[i−1]) = (W[i−1][1],W[i−1][2],W[i−1][3],W[i−1][0])
  • 字节代换(Byte Substitution):使用预定义的S盒进行替代每一个字节
Subword(W[i−1]) = SBox(W[i−1])
  • 轮常量异或(XOR with Round Constant):对 T(W[i - 1]) 的第一个字节进行轮常量异或,假设 rconIndex 表示当前的轮常量索引,那么
T(W[i−1]) = SBox(W[i−1]) ⊕ Rcon[rcon_index]
4. 生成轮密钥

W[Nk] 开始,逐步计算生成剩余的轮密钥(直到 W[4*(NR+1) - 1])。

对于每个 W[i],计算如下:

  • 对于 i % Nk == 0 的字,对前一个轮密钥进行T函数转换,与W[i - Nk]进行异或。

    W[i] = W[i - Nk]T(W[i - 1])
    
  • 其他字节W[i+1], W[i+2], W[i+3])直接将与前一个轮密钥的对应字节进行异或操作:

    W[i] = W[i - Nk]W[i - 1]
    

重复此过程,直到所有轮密钥都被生成。

void KeyExpansion(byte* key, byte* expanded, int Nk, int Nr) {
    int i = 0;
    // initialize the first Nk*4 bytes of the expanded key
    while (i < Nk << 2) {
        expanded[i] = key[i];
        expanded[i + 1] = key[i + 1];
        expanded[i + 2] = key[i + 2];
        expanded[i + 3] = key[i + 3];
        i += 4;
    }

    int rcon_index = 0;
    byte temp[4];
    while (i < (Nr + 1) << 4) {
        // copy the W[k-1] to temp
        for (int j = 0; j < 4; j++)
            temp[j] = expanded[i - 4 + j];

        if ((i % (Nk << 2)) == 0) {
            // word rotation
            // subword
            // xor with round constant
            k = temp[0];
            temp[0] = s_box[temp[1]] ^ r_con[rcon_index++];
            temp[1] = s_box[temp[2]];
            temp[2] = s_box[temp[3]];
            temp[3] = s_box[k];
        }
        // additional subword processing for aes256
        else if (Nk == 8 && ((i & 0x1F) ^ 0x10) == 0) {
            for (int j = 0; j < 4; j++) temp[j] = s_box[temp[j]];
        }

        // W[i] = W[i - Nk] ^ W[i - 1]
        for (int j = 0; j < 4; j++)
            expanded[i + j] = expanded[i - (Nk << 2) + j] ^ temp[j];

        i += 4;
    }
}

3.2 轮密钥加(AddRoundKey)

轮密钥加是将128位轮密钥Ki同状态矩阵中的数据进行逐位异或操作,增加加密过程的复杂度和安全性。

void AddRoundKey(byte* state, byte* round_key) {
    for (int i = 0; i < 16; i++)
        state[i] ^= round_key[i];
}

3.3 字节替代(SubBytes)

通过一个非线性的替换函数,用查找表的方式把每个字节替换成对应的字节,使得加密过程更加复杂,增加安全性。加密使用S-box(Substitution box),解密使用inverse S-box

void SubBytes(byte* state) {
    for (int i = 0; i < 16; i++)
        state[i] = sbox[state[i]];
}

3.4 行移位(ShiftRows)

将矩阵中的每个横列进行循环式移位,加密和解密的移动方向相反。

在这里插入图片描述

void ShiftRows(byte* state) {
    byte temp;
    // The first row remains unchanged.

    // Rotate first row 1 columns to left
    temp = state[1];
    state[1] = state[5];
    state[5] = state[9];
    state[9] = state[13];
    state[13] = temp;

    // Rotate second row 2 columns to left
    temp = state[2];
    state[2] = state[10];
    state[10] = temp;

    temp = state[6];
    state[6] = state[14];
    state[14] = temp;

    // Rotate third row 3 columns to left
    temp = state[15];
    state[15] = state[11];
    state[11] = state[7];
    state[7] = state[3];
    state[3] = temp;
}

3.5 列混淆(MixColumns)

列混合是一个替代操作,是AES算法中最复杂的一步,只在第0到r-1轮中用到,最后一轮不使用该变换。

1. 有限域GF(2^8)运算

在 GF(2^8) 中,运算(如加法、乘法)都在二进制数上进行,并且每个运算都使用模一个不可约多项式。AES使用的是:

m(x) = x^8 + x^4 + x^3 + x + 1
  • 加法:按位异或(XOR)操作。对于两个字节ab,它们的和 a + b 是它们的按位异或:
a + b = a ^ b
  • 乘法:基于模不可约多项式的。对于两个字节0xb0x5,它们的乘积 0xb * 0x5 需要按以下步骤进行:

1)执行常规的多项式乘法

0xb * 0X5 = (x^3 + x^1 + x^0) * (x^2 + x^0) 
          = (x^5 + x^3 + x^2) + (x^3 + x^1 + x^0)
          = x^5 + x^3*(1^1) + x^2 + x^1 + x^0
          = x^5 + x^2 + x^1 + x^0
          = 0x27

2)对多项式 x^8 + x^4 + x^3 + x + 1取模

0x27 % (x^8 + x^4 + x^3 + x + 1) = 0x27 ^ 0x1B = 0x27

编程实现:

byte gmul(byte a, byte b) {
    byte p = 0;
    byte hi_bit_set;
    for (int i = 0; i < 8 && b; i++) {
        if (b & 1) p ^= a;          // xor if the lowest bit of b is 1
        hi_bit_set = (a & 0x80);    // if the highest bit of a is set
        a <<= 1;                    // Multiply a by x (shift left by 1)
        if (hi_bit_set) a ^= 0x1B;  // if the highest bit was set, reduce modulo the AES polynomial (0x1b)
        b >>= 1;                    // // divide b by x (shift right by 1)
    }
    return p;
}
2.列混淆

通过将state矩阵与常矩阵C相乘以达到在列上的扩散,实质是在有限域GF(256)上的多项式乘法运算。

// 加密
void MixColumns(byte* state) {
    byte temp[4];
    for (int i = 0; i < round_key_size; i += 4) {
        temp[0] = gmul(state[i], 2) ^ gmul(state[1 + i], 3) ^ state[2 + i] ^ state[3 + i];
        temp[1] = state[i] ^ gmul(state[1 + i], 2) ^ gmul(state[2 + i], 3) ^ state[3 + i];
        temp[2] = state[i] ^ state[1 + i] ^ gmul(state[2 + i], 2) ^ gmul(state[3 + i], 3);
        temp[3] = gmul(state[i], 3) ^ state[1 + i] ^ state[2 + i] ^ gmul(state[3 + i], 2);

        for (int j = 0; j < 4; j++)
            state[i + j] = temp[j];
    }
}

// 解密
void InvMixColumns(byte* state) {
    byte temp[4];
    for (int i = 0; i < round_key_size; i += 4) {
        temp[0] = gmul(state[i], 14) ^ gmul(state[1 + i], 11) ^ gmul(state[2 + i], 13) ^ gmul(state[3 + i], 9);
        temp[1] = gmul(state[i], 9) ^ gmul(state[1 + i], 14) ^ gmul(state[2 + i], 11) ^ gmul(state[3 + i], 13);
        temp[2] = gmul(state[i], 13) ^ gmul(state[1 + i], 9) ^ gmul(state[2 + i], 14) ^ gmul(state[3 + i], 11);
        temp[3] = gmul(state[i], 11) ^ gmul(state[1 + i], 13) ^ gmul(state[2 + i], 9) ^ gmul(state[3 + i], 14);

        for (int j = 0; j < 4; j++)
            state[i + j] = temp[j];
    }
}

3.6 加解密

// 加密
void AES_encrypt(const byte* input, byte* output, const byte* key, int key_size) {
    const int Nk = key_size / 32;
    const int Nr = (Nk == 4) ? 10 : (Nk == 8 ? 14 : 12);

    byte expanded_keys[(14 + 1) * 16];
    KeyExpansion(key, expanded_keys, Nk, Nr);

    memcpy(output, input, 16);
    AddRoundKey(output, expanded_keys);
    for (int round = 1; round < Nr; round++) {
        SubBytes(output);
        ShiftRows(output);
        MixColumns(output);
        AddRoundKey(output, expanded_keys + round * 16);
    }
    SubBytes(output);
    ShiftRows(output);
    AddRoundKey(output, expanded_keys + Nr * 16);
}

// 解密
void AESDecrypt(const byte* input, byte* output, const byte* key, int key_size) {
    const int Nk = key_size / 32;
    const int Nr = (Nk == 4) ? 10 : (Nk == 8 ? 14 : 12);

    byte expanded_keys[(14 + 1) * 16];
    KeyExpansion(key, expanded_keys, Nk, Nr);

    memcpy(output, input, 16);
    AddRoundKey(output, expanded_keys + Nr * 16);
    for (int round = Nr - 1; round > 0; round--) {
        InvShiftRows(output);
        InvSubBytes(output);
        AddRoundKey(output, expanded_keys + round * 16);
        InvMixColumns(output);
    }
    InvShiftRows(output);
    InvSubBytes(output);
    AddRoundKey(output, expanded_keys);
}

简单的对称加解密实现 tiny_crypt

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值