首页 > 其他分享 >AES加密原理

AES加密原理

时间:2024-11-11 16:18:53浏览次数:7  
标签:AES 加密 int 矩阵 ++ 异或 密钥 数组 原理

文章目录


一 基础知识

1.c语言如何储存二维数组

在C语言中,数组是一种基本的数据结构,用于存储相同类型的元素集合。数组可以是一维的也可以是多维的(如二维数组)。

一维数组

一维数组是最简单的数组形式,它包含一系列相同类型的元素。

  1. 定义一个一维长度为四的数组:

    int a[4]; // 定义一个名为a的一维数组,包含4个int类型的元素。
    

    这里没有对数组a的元素进行初始化,所以它们的值是未定义的。

  2. 定义一个一维长度为四的数组并初始化:

    int b[4] = {0, 1, 2, 3}; // 定义一个名为b的一维数组,并初始化元素为0, 1, 2, 3。
    

    数组b的每个元素都被显式地初始化了。

  3. 定义一个int类型指针指向b[0]:

    int *p = &b[0]; // 定义一个指针p,指向数组b的第一个元素b[0]。
    

    这里&b[0]是取数组b第一个元素的地址,然后让指针p指向这个地址。

  4. 令指针p指向的b[0]=4:

    *p = 4; // 将指针p指向的元素(即b[0])的值设置为4。
    

    由于p指向b[0],这个操作实际上是将b[0]的值改为4。

  5. 令指针p指向的b[0]的下一个数组元素b[1]=5:

    *(p+1) = 5; // 将指针p指向的下一个元素(即b[1])的值设置为5。
    

    p+1是将指针p向前移动一个元素的大小(因为p是指向int的指针,所以移动sizeof(int)个字节),然后通过解引用操作符*来修改这个位置的值。

二维数组

二维数组可以看作是数组的数组,即数组的每个元素本身也是一个数组。

  1. 定义一个4*4的二维数组并初始化:

    int a[4][4] = {
        {0, 1, 2, 3},
        {4, 5, 6, 7},
        {8, 9, 10, 11},
        {12, 13, 14, 15}
    };
    

    这里定义了一个名为a的二维数组,它有4行4列,并且每个元素都被显式地初始化了。

  2. 定义一个int类型指针指向a[0][0]:

    int *p = &a[0][0]; // 定义一个指针p,指向二维数组a的第一个元素a[0][0]。
    

    这里&a[0][0]是取二维数组a第一个元素的地址,然后让指针p指向这个地址。

  3. 令a[2][1]=5:

    *(p + 2*4 + 1) = 5; // 将二维数组a的第3行第2列的元素(即a[2][1])的值设置为5。
    

    这里p + 2*4 + 1的计算是基于二维数组在内存中的线性存储方式。2*4是因为每行有4个元素,所以跳过两行需要移动2*4个元素的位置,然后+1是因为我们要访问的是第2列的元素,所以再加上1个元素的位置。

在C语言中,二维数组在内存中是连续存储的,可以看作是一个长度为行数*列数的一维数组。因此,可以通过计算偏移量来访问二维数组的任意元素。上述代码中的*(p + 2*4 + 1)实际上是利用了这一点,通过指针算术来访问特定的数组元素。


2.异或运算

异或运算符(^)是一种位运算符,它对两个数的对应位进行异或操作。异或的规则是:如果两个比较的位相同,则结果是0;如果两个比较的位不同,则结果是1。

在C语言中,异或运算符可以用于整数类型的数据。例如:

int a = 0x16; // 十六进制数,二进制表示为00010110
int b = 0x23; // 十六进制数,二进制表示为00100011
int result = a ^ b; // 异或结果,二进制表示为00110101,即0x35

异或运算在加密算法中非常有用,因为它具有一些特殊的性质,比如:

  • 任何数和0进行异或运算结果仍然是原数(自反性)。
  • 任何数和自身进行异或运算结果是0(幂等性)。
  • 异或运算是可逆的,即(a ^ b) ^ b = a
  • 异或运算满足交换律和结合律。

这些性质使得异或运算在数据加密和解密过程中扮演着重要的角色,尤其是在AES加密算法中。AES算法中广泛使用了异或运算来混合数据位。

二 加密第一步——做好分组和异或运算

AES(高级加密标准)是一种对称密钥加密算法,它使用相同的密钥进行加密和解密。AES加密算法的核心是多轮的变换,每一轮都包括四个基本步骤:轮密钥加(AddRoundKey)、替换(SubBytes)、行移位(ShiftRows)和列混淆(MixColumns)。在第一轮加密之前,AES算法会进行一次特殊的操作,即轮密钥加,这一步实际上是将明文分组与初始密钥进行异或运算。

算法原理

在AES中,明文被分割成固定大小的分组(通常是128位,即16个字节),每个字节被视为一个矩阵中的元素。初始密钥也是128位。轮密钥加操作就是将明文分组和初始密钥进行按位异或操作,结果作为第一轮加密的输入。

C语言实现思路

要在C语言中实现轮密钥加,我们可以遵循以下步骤:

  1. 定义矩阵:由于AES处理的是128位数据,我们可以将其视为一个4x4的字节矩阵。因此,我们需要定义两个4x4的二维数组,一个用于存储明文分组,另一个用于存储密钥。

  2. 初始化矩阵:将明文分组和密钥的字节分别填充到对应的矩阵中。

  3. 异或操作:遍历这两个矩阵的每个元素,对它们进行异或操作。

  4. 函数封装:将上述操作封装成一个函数,以便在主函数中调用。

详细实现步骤

以下是用C语言实现轮密钥加的详细步骤:

这段C代码实现了AES加密算法中的轮密钥加(AddRoundKey)步骤。轮密钥加是AES算法中的一个重要步骤,它涉及将明文块与轮密钥进行按位异或操作

void addbyte16(int *p, int *k) // 轮密钥加,结果保存在左参数,也为4*4数组异或
  • void:指定函数没有返回值。
  • addbyte16:函数名,表示执行16个字节的添加操作,即128位的异或操作。
  • int *p:第一个参数,指向一个包含明文块的数组的指针。
  • int *k:第二个参数,指向一个包含轮密钥的数组的指针。
{
    int i, j;
    for (j = 0; j < 4; j++)
        for (i = 0; i < 4; i++)
            *(p + j * 4 + i) = *(p + j * 4 + i) ^ *(k + j * 4 + i);
}
  • int i, j;:声明两个循环变量ij,用于遍历数组的行和列。

  • for (j = 0; j < 4; j++):外层循环,遍历4行。

  • for (i = 0; i < 4; i++):内层循环,遍历每行的4列。

  • *(p + j * 4 + i) = *(p + j * 4 + i) ^ *(k + j * 4 + i);:执行异或操作。这里pk是指向数组的指针,j * 4 + i计算出当前要访问的元素的索引。*操作符用于解引用指针,即访问指针指向的值。^是C语言中的按位异或运算符。

  • *(p + j * 4 + i):访问明文数组中第j行第i列的元素。

  • *(k + j * 4 + i):访问密钥数组中第j行第i列的元素。

  • *(p + j * 4 + i) ^ *(k + j * 4 + i):将明文块的当前元素与对应的密钥元素进行异或操作,并将结果存回明文块的当前位置。

这个函数通过两层循环遍历明文块和密钥的每个字节,对每个对应的字节进行异或操作,实现AES算法中的轮密钥加步骤。这种操作是AES加密过程中的第一步,为后续的加密步骤(如字节替换、行移位、列混淆)做准备。


三 S盒子——对每个字节映射更安全

字节代替(S盒)算法原理

在AES加密算法中,字节代替(SubBytes)步骤是将每个字节的值通过查表的方式替换成另一个值。这个表被称为S盒(S-Box)。S盒的目的是提供一个非线性的变换,增加密码的安全性。

  • 输入:一个字节,例如0x16。
  • 处理:将该字节的高四位(高半字节,例如0x1)作为行索引,低四位(低半字节,例如0x6)作为列索引。
  • 查表:在S盒表中查找对应的行和列的交叉点的值,这个值就是输出。例如,0x16的高四位是0x1,低四位是0x6,所以在S盒表中查找第1行第6列的值,得到的输出是0x47。

C代码实现思路

  1. 定义S盒:首先,需要定义一个16x16的S盒,通常以一维数组的形式实现,因为一维数组可以通过计算index = row * 16 + column来访问对应的元素。

  2. 输入处理:对于输入的每个字节,将其作为数组的索引来获取S盒中的新值。

  3. 替换操作:将每个字节替换为其在S盒中对应的新值。

C代码实现

void she(int *p) // S盒
{
    int i;
    int s[256] = {
        0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
        0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
        0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
        0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
        0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
        0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
        0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
        0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
        0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
        0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
        0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
        0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
        0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
        0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
        0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
        0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16
    };
    for (i = 0; i < 16; i++) *(p + i) = s[*(p + i)];
}
  • int s[256]:定义了一个包含256个元素的一维数组,用于存储S盒的值。
  • for (i = 0; i < 16; i++) *(p + i) = s[*(p + i)];:循环遍历输入的每个字节,使用S盒中的值进行替换。*(p + i)获取当前字节的值,s[*(p + i)]从S盒中获取对应的新值,并将其赋值回原位置。

通过查表的方式将每个字节的值替换为S盒中对应的值,增加了加密的复杂性和安全性。

四 行位移——逐行递增的移动方式

行移位(ShiftRows)算法原理

行移位是AES加密算法中的一个重要步骤,它涉及到对状态矩阵中的行进行循环左移位操作。具体来说,这个步骤会按照以下规则对4x4的状态矩阵进行处理:

  • 第1行(从0开始编号)保持不变,左移0位。
  • 第2行左移1位。
  • 第3行左移2位。
  • 第4行左移3位。

这意味着每一行的每个元素都会根据其行号和列号进行相应的循环位移。例如,第2行的第一个元素(原本位于位置(2,0))会移动到第2行的第二个位置(即原本第2行的第二个元素会移动到第三个位置,以此类推,最后一个元素会绕回到行首)。

C代码实现思路

在C语言中,我们可以通过以下步骤实现行移位:

  1. 定义临时矩阵:首先,定义一个4x4的临时矩阵b,用于存储行移位后的结果。
  2. 执行行移位:通过循环遍历状态矩阵p的每个元素,并根据行移位规则计算其在b中的新位置。具体来说,对于p中的每个元素p[j*4+i](其中j是行索引,i是列索引),其在b中的新位置为b[j][i] = p[(j*4 + (i + j) % 4)]
  3. 更新原矩阵:将临时矩阵b中的元素复制回原矩阵p,以完成行移位操作。

C代码实现

void left(int *p) { // 行移位
    int i, j;
    int b[4][4]; // 定义一个4x4的临时矩阵b

    // 执行行移位,将结果存储在临时矩阵b中
    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            b[j][i] = *(p + j * 4 + (i + j) % 4); // 根据行移位规则计算新位置
        }
    }

    // 将临时矩阵b的结果复制回原矩阵p
    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            *(p + i + j * 4) = b[j][i]; // 更新原矩阵p
        }
    }
}

这段代码首先定义了一个4x4的临时矩阵b,然后通过两层循环遍历原矩阵p的每个元素,并根据行移位规则计算其在b中的新位置。最后,将b中的结果复制回p,完成行移位操作。这种方法简单直观,能够有效地实现AES算法中的行移位步骤。

列混淆——跳动在有限域的运算

列混淆(MixColumns)算法原理

列混淆是AES算法中的一个重要步骤,它作用于状态矩阵的每一列。这一步骤通过与一个固定的多项式矩阵相乘来实现,这个矩阵被称为列混淆矩阵。在AES中,列混淆矩阵是固定的,并且定义在有限域GF(2^8)上,这意味着所有的算术运算(包括乘法和加法)都是在模2的条件下进行的,即加法等同于异或操作。

在GF(2^8)中,乘法是通过将两个元素相乘,然后取结果模一个不可约多项式来完成的。AES算法使用的不可约多项式是 ( x^8 + x^4 + x^3 + x + 1 )。这意味着任何乘法操作的结果都必须通过这个多项式来化简。

C代码实现思路

  1. 定义乘法函数:首先,我们需要定义一个函数 mul 来处理GF(2^8)上的乘法。这个函数需要处理两种情况:当乘数是0x02和0x03时,因为这两种情况涉及到特殊的模运算。

  2. 矩阵乘法:然后,我们需要实现矩阵乘法。对于状态矩阵的每一列,我们都需要与列混淆矩阵相乘,并将结果存储在一个新的矩阵中。

  3. 更新状态矩阵:最后,我们将新计算出的矩阵的值复制回原始状态矩阵,以完成列混淆步骤。

C代码实现

int mul(int b, int a) { // 有限域上的乘法
    if (b == 0x02) {
        if (a < 0x80) a = (a * 0x02);
        else a = ((a % 0x80) * 0x02) ^ 0x1b;
    } else if (b == 0x03) {
        if (a < 0x80) b = (a * 0x02);
        else b = ((a % 0x80) * 0x02) ^ 0x1b;
        a = a ^ b;
    }
    return a;
}

void column(int *p, int *q) { // 列混淆
    int i, j;
    int a[4][4];
    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            a[j][i] = mul(*(q + j * 4 + 0), *(p + 0 * 4 + i)) ^
                     mul(*(q + j * 4 + 1), *(p + 1 * 4 + i)) ^
                     mul(*(q + j * 4 + 2), *(p + 2 * 4 + i)) ^
                     mul(*(q + j * 4 + 3), *(p + 3 * 4 + i));
        }
    }
    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            *(p + j * 4 + i) = a[j][i];
        }
    }
}
  • mul 函数:这个函数处理了GF(2^8)上的乘法。对于0x02和0x03的乘法,它使用了特殊的规则来确保结果符合有限域的性质。

  • column 函数:这个函数实现了列混淆的矩阵乘法。它首先计算了新矩阵的值,然后将这些值复制回原始矩阵。

这种实现方式确保了AES算法的列混淆步骤可以正确执行,同时保持了代码的清晰和效率。

密钥扩展

密钥扩展算法原理

在AES算法中,密钥扩展(Key Expansion)是将初始密钥扩展成一系列轮密钥的过程。这个过程对于AES-128、AES-192和AES-256的密钥长度是不同的。密钥扩展的结果是一个密钥调度,包含了多轮加密所需的所有轮密钥。

对于AES-128,初始密钥是128位,最终会扩展成11个轮密钥,每个轮密钥也是128位。密钥扩展的过程包括以下几个步骤:

  1. 初始密钥:将初始密钥分成四个16位的单词(4字节),形成一个4x4的矩阵。

  2. 生成轮密钥:通过一系列的操作生成后续的轮密钥。这些操作包括:

    • 轮常数(Round Constants, RC):在G函数中使用,每个轮次使用不同的轮常数。
    • 字节替换(SubWord, SW):使用S盒对4个字节进行替换。
    • 行移位(RotWord, RCon):将4x4矩阵的第一行进行循环左移。
    • 异或(XOR):将生成的4x4矩阵与前一个轮密钥进行异或。
  3. G函数:对4x4矩阵的某一行进行变换,生成新的4个字节。

C代码实现思路

  1. 将二维数组分成四个竖向数组:在C语言中,没有竖向数组,因此需要将二维数组分成四个一维数组。

  2. G-S盒实现:G-S盒与S盒类似,但是作用于4字节的数组。

  3. 1x4数组的加法:实现两个1x4数组的异或操作。

  4. G函数:实现G函数,包括左移一位操作和与RC数组的异或。

  5. 密钥扩展:实现密钥扩展函数,包括对每个轮密钥的生成。

C代码实现

void gshe(int *p) { /* G-S盒 */ }
void addbyte4(int *p, int *q) { /* 1x4数组的加法 */ }
void g(int *p, int j) { /* G函数 */ }
void keyextension(int *p, int j) { /* 密钥扩展 */ }

密钥扩展函数实现

void keyextension(int *p, int j) {
    int a[4], b[4], c[4], d[4], d1[4];
    int i, *q1 = &a[0], *q2 = &b[0], *q3 = &c[0], *q4 = &d[0], *q5 = &d1[0];
    
    // 分割二维数组到四个一维数组
    for (i = 0; i < 4; i++) a[i] = *(p + i * 4 + 0);
    for (i = 0; i < 4; i++) b[i] = *(p + i * 4 + 1);
    for (i = 0; i < 4; i++) c[i] = *(p + i * 4 + 2);
    for (i = 0; i < 4; i++) d[i] = *(p + i * 4 + 3);
    
    // 复制d数组到d1数组
    for (i = 0; i < 4; i++) d1[i] = d[i];
    
    // 应用G函数和轮常数
    g(q5, j);
    addbyte4(q1, q5);
    addbyte4(q2, q1);
    addbyte4(q3, q2);
    addbyte4(q4, q3);
    
    // 将结果写回二维数组
    for (i = 0; i < 4; i++) *(p + i * 4 + 0) = a[i];
    for (i = 0; i < 4; i++) *(p + i * 4 + 1) = b[i];
    for (i = 0; i < 4; i++) *(p + i * 4 + 2) = c[i];
    for (i = 0; i < 4; i++) *(p + i * 4 + 3) = d[i];
}

这段代码实现了AES算法中的密钥扩展步骤,通过一系列的操作将初始密钥扩展成多轮密钥,为AES加密过程中的每一轮提供所需的密钥。

总设计流程

在AES加密算法中,主函数的设计思路是按照AES的加密流程来组织的。AES加密流程包括多个轮次的处理,每一轮次包括四个主要的步骤:字节替换(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。以下是主函数的设计思路和步骤:

1. 初始化和输入

  • 明文和密钥输入:首先,程序需要从用户那里获取明文和密钥。明文和密钥都是128位(16字节)的数据,分别存储在4x4的二维数组ukey中。

2. 第一轮前的轮密钥加

  • 轮密钥加:在第一轮加密之前,需要将明文与初始密钥进行异或操作,这一步称为轮密钥加。这一步确保了密钥与明文的第一次混合。

3. 循环处理9轮

  • 循环处理:对于前9轮,每一轮都包括四个步骤:
    • 字节替换(SubBytes):对状态矩阵中的每个字节应用S盒变换。
    • 行移位(ShiftRows):对状态矩阵的行进行循环移位。
    • 列混淆(MixColumns):对状态矩阵的列进行特定的线性变换。
    • 轮密钥加(AddRoundKey):将当前轮的轮密钥与状态矩阵进行异或操作。

4. 第十轮的特殊处理

  • 第十轮:第十轮与前九轮的主要区别在于它不包括列混淆步骤。这一轮只包括字节替换、行移位和轮密钥加。

5. 输出密文

  • 密文输出:在所有轮次处理完成后,输出最终的状态矩阵,即密文。

6. 代码实现

以下是主函数的代码实现,其中包含了上述所有步骤:

#include <stdio.h>
#include <stdlib.h>

// 假设所有必要的函数(如she, left, column, addbyte16, keyextension等)已定义

int main(int argc, char *argv[]) {
    int i, j;
    int u[4][4], key[4][4];

    // 输入明文
    printf("input plaintext:\n");
    for (j = 0; j < 4; j++)
        for (i = 0; i < 4; i++)
            scanf("%x", &u[j][i]);

    // 输入密钥
    printf("input key:\n");
    for (j = 0; j < 4; j++)
        for (i = 0; i < 4; i++)
            scanf("%x", &key[j][i]);

    // 列混淆常数矩阵
    int u1[4][4] = {
        {0x02, 0x03, 0x01, 0x01},
        {0x01, 0x02, 0x03, 0x01},
        {0x01, 0x01, 0x02, 0x03},
        {0x03, 0x01, 0x01, 0x02}
    };
    int *p = &u[0][0];
    int *q = &u1[0][0];
    int *k = &key[0][0];

    // 第一轮前
    addbyte16(p, k);

    // 第一轮到第九轮(0~8)
    for (i = 0; i < 9; i++) {
        she(p);
        left(p);
        column(p, q);
        keyextension(k, i);
        addbyte16(p, k);
    }

    // 第十轮
    she(p);
    left(p);
    keyextension(k, 9);
    addbyte16(p, k);

    // 密文输出
    printf("ciphertext:\n");
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++)
            printf("%x ", u[i][j]);
        printf("\n");
    }

    system("pause");
    return 0;
}

这段代码实现了AES加密算法的核心流程,从输入明文和密钥,到执行多轮加密处理,最终输出加密后的密文。

代码举例

#include <stdio.h>
#include <stdlib.h>

// S盒替换函数
void she(int *p) {
    int i;
    int s[256] = {
        // S盒值,省略了具体值...
    };
    for (i = 0; i < 16; i++) {
        *(p + i) = s[*(p + i)];  // 将每个字节通过S盒进行替换
    }
}

// g-S盒替换函数,用于密钥扩展
void gshe(int *p) {
    int i;
    int s[256] = {
        // g-S盒值,省略了具体值...
    };
    for (i = 0; i < 4; i++) {  // g-S盒只作用于4个字节
        *(p + i) = s[*(p + i)];
    }
}

// 行移位函数
void left(int *p) {
    int i, j;
    int b[4][4];
    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            b[j][i] = *(p + j * 4 + (i + j) % 4);  // 循环左移
        }
    }
    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            *(p + i + j * 4) = b[j][i];  // 将结果写回原矩阵
        }
    }
}

// 4x4矩阵字节加法函数,用于轮密钥加
void addbyte16(int *p, int *q) {
    int i, j;
    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            *(p + j * 4 + i) = *(p + j * 4 + i) ^ *(q + j * 4 + i);
        }
    }
}

// 1x4矩阵字节加法函数,用于密钥扩展
void addbyte4(int *p, int *q) {
    int i;
    for (i = 0; i < 4; i++) {
        *(p + i) = *(p + i) ^ *(q + i);
    }
}

// 有限域GF(2^8)上的乘法函数
int mul(int b, int a) {
    if (b == 0x02) {
        if (a < 0x80) a = (a * 0x02);
        else a = ((a % 0x80) * 0x02) ^ 0x1b;
    } else if (b == 0x03) {
        if (a < 0x80) b = (a * 0x02);
        else b = ((a % 0x80) * 0x02) ^ 0x1b;
        a = a ^ b;
    }
    return a;
}

// 列混淆函数
void column(int *p, int *q) {
    int i, j;
    int a[4][4];
    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            a[j][i] = mul(*(q + j * 4 + 0), *(p + 0 * 4 + i)) ^
                      mul(*(q + j * 4 + 1), *(p + 1 * 4 + i)) ^
                      mul(*(q + j * 4 + 2), *(p + 2 * 4 + i)) ^
                      mul(*(q + j * 4 + 3), *(p + 3 * 4 + i));
        }
    }
    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            *(p + j * 4 + i) = a[j][i];
        }
    }
}

// g函数,用于密钥扩展
void g(int *p, int j) {
    int rc[10] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
    int i, a = *p;
    for (i = 0; i < 3; i++) {
        *(p + i) = *(p + i + 1);
    }
    *(p + 3) = a;
    gshe(p);
    *p = *p ^ rc[j];
}

// 密钥扩展函数
void keyextension(int *p, int j) {
    int a[4], b[4], c[4], d[4], d1[4];
    int i, *q1 = &a[0], *q2 = &b[0], *q3 = &c[0], *q4 = &d[0], *q5 = &d1[0];
    for (i = 0; i < 4; i++) {
        a[i] = *(p + i * 4 + 0);
        b[i] = *(p + i * 4 + 1);
        c[i] = *(p + i * 4 + 2);
        d[i] = *(p + i * 4 + 3);
    }
    for (i = 0; i < 4; i++) {
        d1[i] = *(p + i * 4 + 3);
    }
    g(q5, j);
    addbyte4(q1, q5);
    addbyte4(q2, q1);
    addbyte4(q3, q2);
    addbyte4(q4, q3);
    for (i = 0; i < 4; i++) {
        *(p + i * 4 + 0) = a[i];
        *(p + i * 4 + 1) = b[i];
        *(p + i * 4 + 2) = c[i];
        *(p + i * 4 + 3) = d[i];
    }
}

// 主函数,执行AES加密
int main(int argc, char *argv[]) {
    int i, j;
    int u[4][4], key[4][4];

    // 输入明文
    printf("input plaintext:\n");
    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            scanf("%x", &u[j][i]);
        }
    }

    // 输入密钥
    printf("input key:\n");
    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            scanf("%x", &key[j][i]);
        }
    }

    // 列混淆常数矩阵
    int u1[4][4] = {
        {0x02, 0x03, 0x01, 0x01},
        {0x01, 0x02, 0x03, 0x01},
        {0x01, 0x01, 0x02, 0x03},
        {0x03, 0x01, 0x01, 0x02}
    };
    int *p = &u[0][0];
    int *q = &u1[0][0];
    int *k = &key[0][0];

    // 第一轮前轮密钥加
    addbyte16(p, k);

    // 第一轮到第九轮(0~8)
    for (i = 0; i < 9; i++) {
        she(p);
        left(p);
        column(p, q);
        keyextension(k, i);
        addbyte16(p, k);
    }

    // 第十轮
    she(p);
    left(p);
    keyextension(k, 9);
    addbyte16(p, k);

    // 密文输出
    printf("ciphertext:\n");
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            printf("%x ", u[i][j]);
        }
        printf("\n");
    }

    system("pause");
    return 0;
}

标签:AES,加密,int,矩阵,++,异或,密钥,数组,原理
From: https://blog.csdn.net/zhuqiyua/article/details/143687070

相关文章

  • 人体感应电锯工作原理
    电锯遇到人手就停止的原理,主要依赖于一种先进的安全技术。这种技术通过在锯片上导入微弱电流,利用人体与木材不同的电气属性来实现安全保护。以下是对该原理的详细解释:一、工作原理概述当人手或其他导电物体接触到带有微弱电流的锯片时,由于人体内部相对较大的电容会改变电信号......
  • 多线程锁的升级原理是什么
      锁的级别:无锁=>偏向锁=>轻量级锁=>重量级锁 无锁:没有对资源进行锁定,所有线程都可以访问,但是只有一个线程能成功修改资源,其他的线程会不断尝试,直至修改成功。  偏向锁:偏向锁是指当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储线程ID。一旦该......
  • 多线程锁的升级原理是什么
      锁的级别:无锁=>偏向锁=>轻量级锁=>重量级锁 无锁:没有对资源进行锁定,所有线程都可以访问,但是只有一个线程能成功修改资源,其他的线程会不断尝试,直至修改成功。  偏向锁:偏向锁是指当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储线程ID。一旦该......
  • 内网IP地址实现HTTPS加密访问教程
    一、前期准备确定内网IP地址:确保有一个明确且固定的内网IP地址。动态IP地址可能不适合此场景,因为它们会频繁改变,导致SSL证书失效。选择SSL证书颁发机构(CA):选择一个受信任的CA,如JoySSL、CFCA等,并确认其是否提供针对内网IP地址的SSL证书服务。二、申请SSL证书内网ip证书申请入......
  • 千兆反射内存卡的技术原理与优势
    在当今数字化的快节奏世界中,数据的快速、准确和实时传输已成为各行各业成功的关键。反射内存卡,作为一种尖端的数据传输解决方案,以其独特的性能和广泛的应用前景,正在引领行业的变革。本文将深入探讨千兆反射内存卡的应用领域及其发展前景,揭示这一技术如何为各行各业带来前所未有......
  • 计算机组成原理之超标量和动态流水线的基本概念
    1.超标量的基本概念定义:超标量(superscalar)CPU架构是指在一颗处理器内核中实行了指令级并行的一类并行运算。这种技术能够在相同的CPU主频下实现更高的CPU吞吐率(throughput)。应用背景:随着处理器技术的不断发展,为了提高处理器的性能,需要处理器具有每个周期能发射执行多条指......
  • unity项目托管代码和非托管代码之间的数据传递原理刨析
    Unity3D架构Unity3D是一个广泛使用的游戏引擎,支持多种平台的游戏开发。它的架构主要由两部分组成:非托管代码(UnmanagedCode):这部分主要是用C++编写的,负责引擎的底层功能,如图形渲染、物理计算、音频处理等。非托管代码直接与操作系统和硬件交互,通常具有更高的性能,但开......
  • GFPS技术原理(三)广播信息
    我们知道GFPS有两个角色,Provider用于发送广播,然后Provider的广播数据在BR/EDR处于配对模式和非配对模式两种不同情况下,广播数据也是不尽相同,下面来做分析:配对模式:广播频率:处于配对模式下,Provideradvertisinginterval必须不超过100ms,也就是最少要一秒发送10次广播,这样......
  • 鸿蒙Next系统中的随机数生成:从Crypto Architecture Kit看加密原理
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。在当今数字化浪潮汹涌澎湃的时代,信息安......
  • AUTOSAR CP Ethernet State Manager(EthSM)规范的主要功能以及工作原理导读
    AUTOSAREthernetStateManager(以下简称EthSM)规范的主要功能AUTOSAREthernetStateManager(以下简称EthSM)规范的主要功能包括:通信控制网络模式管理:为通信管理器(ComM)提供API,用于请求以太网网络的通信模式,如ETHSM_FULL_COMMUNICATION(全通信)、ETHSM_SILENT_COMMUNICATIO......