AES算法加密原理和实践学习笔记

2023-11-02

AES算法加密原理和实践学习学习笔记。

一.背景

DES,Data Encryption Standard, 数据加密标准。尽管DES是一个很宽泛的名字,但是它指代的只是一个具体的标准。它在1970年代被美国NBS接受为信息处理的标准。与之对应,DES的加解密算法称为DEA算法,它采用了56-bit的密钥和块加密方式。具体的实现中,考虑到块加密的问题,如区块相似性,要使用DES的变种。常用DES有最早出现的工作模式,ECB,CBC,OFB和CFB,可以追溯到1981年。AES和DES一样也是一个标准,但是称为高级加密标准,听起来也很宽泛。这两个加密标准在历史上确实是DES先于AES, 而且同属于块加密,所以它们都可以用块加密的四种工作模式。它们都是对称加密,等等。尽管AES不具备RSA这种加密方法的特殊优点,但是它确实地受到了一种特殊的待遇,这可能是因为密码学家确实在筛选它的过程中发现了它良好的防止攻击的性能,而这要追究到密码学中很多复杂的理论。

1.1 MCU的ISP和IAP功能

现在MCU已经普遍采用Flash型的EEPROM作为程序存储器,Flash结构的存储器写入时可以按字节操作把1写成0,擦除时都是按扇区操作,把0写成1。它的流行与普及为ISP/IAP功能的实现提供了技术基础。

ISP,In System Programming,它的意思是“在系统编程”,即MCU不需要从电路板上取下就可以编程写人最终用户代码,已经编程的器件也可以用ISP方式擦除或再编程,从而改变了单片机系统的固化程序升级需拔插单片机芯片、使用专用编程器进行烧录的麻烦方式,轻松实现在单片机系统电路板上对单片机芯片内固化的程序进行升级。

IAP,In Application Programming,它的意思是“在应用编程”,即MCU在运行时程序存储器可由程序自身进行擦写。也就是说程序自己可以往程序存储器里写数据或修改程序。这种方式的典型应用就是程序运行中的数据存储。ISP/IAP的区别是:ISP通常是在手工操作下整片擦除、编程,需要简单的硬件资源;而IAP却是在某段程序的控制下对另外一段程序Flash进行读写,可以控制对特定地址的读写操作。实际上单片机的ISP功能就是通过IAP技术来实现的,即芯片在出厂前就已经有一段芯片制造商的boot程序在芯片里面,片子上电后,开始运行这段程序,当检测到上位机有下载要求时,便和上位机通信,然后下载数据到存储区。其实在系统编程是通过厂商的boot程序对片内Flash存储器进行擦除/编程的方法,而在应用编程是用户的应用代码对片内Flash存储器进行擦除/编程的方法。正是因为有了IAP,从而使得MCU可以将数据写入到程序存储器中,数据如同烧入的程序一样,掉电不丢失。IAP技术的普及对于高档仪器仪表的智能化意义重大,使得仪器的程序可以根据实际情况进行改变和调整。

1.2 数据加密概述

加密就是把明文变成别人看不懂的密文,然后发送给自己想要的人,接收方用配套的解密算法又把密文解开成明文,这样就不怕在传送的路上被别人截获而泄密,以此来防止非授权用户使用该数据。

数据加密中的明文是指原始的或未加密的数据,需要通过加密算法对其进行加密,加密算法的输入信息为明文和密钥;密文,即明文加密后的格式,是加密算法的输出信息。加密算法是公开的,而密钥则是不公开的。密文,不应为无密钥的用户理解,用于数据的存储以及传输。传统的加密方法有两种,替换和置换。替换是使用密钥将明文中的每一个字符转换为密文中的一个字符。而置换仅将明文的字符按不同的顺序重新排列。单独使用这两种方法的任意一种都是不够安全的,但是将这两种方法结合起来就能提供比较高的安全程度。

加密方式分为对称加密和不对称加密。对称,就是采用这种加密方法的双方使用同样的密钥进行加密和解密。在对称加密算法中,数据发送方将明文和加密密钥一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。接收方收到密文后,若想解读原文,则需要使用与加密相同的密钥及相同算法的逆运算对密文进行解密,才能使其恢复成可读明文。这就要求加密密钥能够从解密密钥中推算出来,同时解密密钥也可以从加密密钥中推算出来。而在大多数的对称算法中,加密密钥和解密密钥是相同的,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求发送方和接收方在安全通信之前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都可以对他们发送或接收的消息解密,所以密钥的保密性对通信安全至关重要。由于其计算量小、加密速度快、加密效率高,对称性加密通常在消息发送方需要加密大量数据时使用。如果用户每次使用对称加密算法时,都使用其他人不知道的惟一密钥,安全性是可以得到保证的。

不对称加密算法的基本原理是,如果发信方想发送只有收信方才能解读的加密信息,发送方必须首先知道接收方的公钥,然后利用接收方的公钥来加密原文;接收方收到加密密文后,使用自己的私钥才能解密密文。显然,采用不对称加密算法,收发双方在通信之前,接收方必须将自己早已随机生成的公钥送给发送方,而自己保留私钥。由于不对称算法拥有两个密钥,因而特别适用于分布式系统中的数据加密。非对称加密算法的保密性比较好,它消除了最终用户交换密钥的需要,但加密和解密花费时间长、速度慢,它不适合于对文件加密而只适用于对少量数据进行加密。广泛应用的不对称加密算法有RSA算法和美国国家标准局提出的DSA。以不对称加密算法为基础的加密技术应用非常广泛。

1.3 常用加密算法

由于受限于MCU的运算能力,并非所有的加密算法都适用于嵌入式系统。TEA(Tiny Encryption Algorithm)是一种小型的对称加密解密算法,由剑桥大学计算机实验室的David Wheeler和Roger Needham于1994年发明。它以加密解密速度快、效率高、实现简单著称。该算法的可靠性是通过加密轮数而不是算法的复杂度来保证的,TEA算法主要运用了移位和异或运算,密钥在加密过程中始终不变。它是一种分组密码算法,其明文密文块为64比特,密钥长度为128比特。TEA算法利用不断增加的Delta(黄金分割率)值作为变化,使得每轮的加密不相同,有很强的抗差分分析能力。

DES算法又被称为美国数据加密标准,是上世纪七十年代美国IBM公司研制的对称密码体制加密算法,并在1977年成为美国官方加密标准。DES的工作原理为:明文按64位进行分组,每个块用64位密钥进行加密,密钥事实上是56位参与DES运算(第8、16、24、32、40、48、56、64位是校验位,使得每个密钥都有奇数个1),分组后的明文组和56位的密钥按位替代或交换的方法形成密文组。每块先用初始置换方法进行加密,再连续进行16次复杂的替换,最后再对其使用初始置换的逆。第i步的替换并不是直接利用原始的密钥K,而是由K与i计算出的密钥Ki。其入口参数有三个:key、data、mode。key为加密解密使用的密钥,data为加密解密的数据,mode为其工作模式。当模式为加密模式时,明文按照64位进行分组,形成明文组,key用于对数据加密,当模式为解密模式时,key用于对数据解密。攻击DES的主要形式被称为蛮力或彻底密钥搜索,即重复尝试各种密钥直到有一个符合为止。如果DES使用56位的密钥,则可能的密钥数量是2的56次方个。随着计算机系统能力的不断发展,DES的安全性比它刚出现时会弱得多,然而从非关键性质的实际出发,仍可以认为它是足够的。不过在实际使用中更多地选择新的加密标准一高级加密标准。

密码学中的高级加密标准(Advanced Encryption Standard,AES),是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。AES的区块长度固定为128比特,密钥长度则可以是128,192或256比特,分别称为AES—128,AES—192,AES—256,如果数据块及密钥长度不足时,则补齐之。AES算法是基于置换和代替的,置换是数据的重新排列,而代替是用一个单元数据替换另一个。AES算法使用了多重循环实现置换和替换,在规范中被称为Bytes Sub(字节替换)——对数据的每个字节应用非线性变换;Shift Rows(行位移变换)——对每一行字节循环重新排序;Mix Columns(列混合变换)——对矩阵的列应用线性变换;Add Round Key——对状态和每轮的子密钥进行异或操作。该算法对内存的需求非常低,使得它很适应于资源受限制的环境。

加密算法在升级中的应用

“在线升级”实际上是Flash数据存储的一个特例。完成这样的功能首先要求芯片提供的EEPROM的地址与程序空间是连续的,一致的,在用户应用程序中可以对另外一部分程序区进行擦除写入操作;再者芯片内部Flash程序存储器容量必须足够大,使之可以分为几个区域相互独立。第一部分是用户的boot程序,完成上电初始化和解密,并将接收的数据写入指定地址。第二部分是程序标识区,标记当前用户程序的运行地址。第三部分是应用程序区1和应用程序区2,当程序运行在程序区1时可以对程序区2进行升级,反之亦然。这两个区域的大小必须不小于用户的目标代码。

在用户程序区可以存放几组密钥信息,与PC下传的随机数按照一定的规则生成解密所需的密钥以保证每次加解密的密钥信息有差异,只要保证每次密钥是唯一的,数据的安全性是可以保证的。需要升级芯片中的程序时,PC机按照通信协议的格式下传指令,用户boot程序接收到指令后初始化各个变量和寄存器,关闭不需要的中断,下传的数据包交叉使用两种加密算法,包号是奇数时用TEA算法加密并同时下传两组随机数共16个字节,一组是本次解密所用,另一组是下一包解密所用,下传的数据包号是偶数时用AES算法加密。为提高运算速度,对AES算法的字节替换过程采用查表的方法。只要保证每次下传的有效数据长度相同,足以达到欺骗性,即使所有数据被截获,由于芯片内部的密钥无法读出,截获者也无法得到正确的明文。同时考虑到程序中数据容易处理,每次下传的有效字节数选择16的整数倍。boot程序将数据正确写入存储器后应向上位机返回正确状态,否则应返回出错信息以便处理。程序升级完成后上位机发出结束命令并下传校验字符,boot程序核对正确后擦除程序标志区并重新写入标识。

用户程序升级成功之后,可以通过函数指针的方式调用该程序。函数在编译时都会被分配一个入口地址,该地址就是函数的指针。只要用一个指针变量指向这个函数的入口地址,就可以通过指针变量调用这个函数。函数指针的本质是指针变量,只不过该指针变量指向函数,读出程序标志区的运行地址就可以通过指针变量调用新写入的程序。

AES模块

本文涉及的AES模块主要是 128bit 的 AES 加密解密,生成用于流密码加密的密钥。

  • 支持基于多种链接模式的加密和解密:
  • 支持电密码本模式(ECB 模式)
  • 支持密码分组链接模式(CBC 模式)
  • 支持计数器模式(CTR 模式)
  • 支持 Galois 计数器模式(GCM 模式)
  • 支持 Galois 消息认证码模式(GMAC 模式)
  • 支持 CBC-MAC 计数器(CCM 模式)

下面结合AES的密码学背景知识和实际应用来讲解。

加密算法原理

AES加密算法涉及4种操作:字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。下图给出了AES加解密的流程,从图中可以看出:1)解密算法的每一步分别对应加密算法的逆操作,2)加解密所有操作的顺序正好是相反的。正是由于这几点(再加上加密算法与解密算法每步的操作互逆)保证了算法的正确性。加解密中每轮的密钥分别由种子密钥经过密钥扩展算法得到。算法中16字节的明文、密文和轮子密钥都以一个4x4的矩阵表示。

  • 字节替代:通过S盒完成一个字节到另外一个字节的映射;
  • 行移位:一个4x4的矩阵内部字节之间的置换,用于提供算法的扩散性。
  • 列混淆:利用GF(28)域上算术特性的一个代替,同样用于提供算法的扩散性。
  • 轮密钥加:将轮密钥与明文(状态)按比特异或 AES的不同模式具体如下:

ECB模式

ECB(Electronic CodeBook)模式,即电子密码本模式。该模式是将明文分组,加密后直接成为密文分组,分组之间没有关系。

ECB模式是所有模式中最简单的一种,该模式的明文分组与密文分组是一一对应的关系,若明文分组相同,其密文分组也一定相同。因此,ECB模式也是最不安全的模式。

CBC模式

CBC(Cipher Block Chaining)模式,即密码分组链接模式。该模式首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密。只有第一个明文分组特殊,需要提前为其生成一个与分组长度相同的比特序列,进行XOR运算,这个比特序列称为初始化向量(Initialization Vector),简称IV。

CFB模式

CFB(Cipher FeedBack)模式,即密文反馈模式。该模式首先将前一个密文分组进行加密,再与当前明文分组进行XOR运算,来生成密文分组。同样CFB模式也需要一个IV。

OFB模式

OFB(Output FeedBack)模式,即输出反馈模式。该模式会产生一个密钥流,即将密码算法的前一个输出值,做为当前密码算法的输入值。该输入值再与明文分组进行XOR运行,计算得出密文分组。该模式需要一个IV,进行加密后做为第一个分组的输入。

CTR模式

CTR(CounTeR)模式,即计数器模式。该模式也会产生一个密钥流,它通过递增一个计数器来产生连续的密钥流。对该计数器进行加密,再与明文分组进行XOR运算,计算得出密文分组。

分组密码的填充

在分组密码中,当数据长度不符合分组长度时,需要按一定的方式,将尾部明文分组进行填充,这种将尾部分组数据填满的方法称为填充(Padding)。

No Padding

即不填充,要求明文的长度,必须是加密算法分组长度的整数倍。

... | DD DD DD DD DD DD DD DD | DD DD DD DD DD DD DD DD |

ANSI X9.23

在填充字节序列中,最后一个字节填充为需要填充的字节长度,其余字节填充0。

... | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 04 |

ISO 10126

在填充字节序列中,最后一个字节填充为需要填充的字节长度,其余字节填充随机数。

... | DD DD DD DD DD DD DD DD | DD DD DD DD 81 A6 23 04 |

PKCS#5和PKCS#7

在填充字节序列中,每个字节填充为需要填充的字节长度。

... | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |

ISO/IEC 7816-4

在填充字节序列中,第一个字节填充固定值80,其余字节填充0。若只需填充一个字节,则直接填充80。

... | DD DD DD DD DD DD DD DD | DD DD DD DD 80 00 00 00 |

... | DD DD DD DD DD DD DD DD | DD DD DD DD DD DD DD 80 |

Zero Padding

在填充字节序列中,每个字节填充为0。

... | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 00 |
/*
 * Advanced Encryption Standard
 * @author Dani Huertas
 * @email huertas.dani@gmail.com
 *
 * Based on the document FIPS PUB 197
 */
#include "aes.h"

/* 128 bits */
static uint8_t key[] =
{
    0x2b, 0x7e, 0x15, 0x16,
    0x28, 0xae, 0xd2, 0xa6,
    0xab, 0xf7, 0x15, 0x88,
    0x09, 0xcf, 0x4f, 0x3c
};
/*
    Number of columns (32-bit words) comprising the State. For this standard, Nb = 4.
*/
static int Nb = 4;

//Number of 32-bit words comprising the Cipher Key. For this standard, Nk = 4, 6, or 8.
static int Nk = 4;

//Number of rounds, which is a function of  Nk  and  Nb (which is fixed). For this standard, Nr = 10, 12, or 14.
static int Nr = 10;

/*******************具体实现代码*********************/

/*
 * Addition in GF(2^8)
 * http://en.wikipedia.org/wiki/Finite_field_arithmetic
 */
uint8_t gadd(uint8_t a, uint8_t b)
{
    return a^b;
}

/*
 * Subtraction in GF(2^8)
 * http://en.wikipedia.org/wiki/Finite_field_arithmetic
 */
uint8_t gsub(uint8_t a, uint8_t b)
{
    return a^b;
}

/*
 * Multiplication in GF(2^8)
 * http://en.wikipedia.org/wiki/Finite_field_arithmetic
 * Irreducible polynomial m(x) = x8 + x4 + x3 + x + 1
 */
uint8_t gmult(uint8_t a, uint8_t b)
{

    uint8_t p = 0, i = 0, hbs = 0;

    for (i = 0; i < 8; i++)
    {
        if (b & 1)
        {
            p ^= a;
        }

        hbs = a & 0x80;
        a <<= 1;
        if (hbs) a ^= 0x1b; // 0000 0001 0001 1011
        b >>= 1;
    }

    return (uint8_t)p;
}

/*
 * Addition of 4 byte words
 * m(x) = x4+1
 */
void coef_add(uint8_t a[], uint8_t b[], uint8_t d[])
{

    d[0] = a[0]^b[0];
    d[1] = a[1]^b[1];
    d[2] = a[2]^b[2];
    d[3] = a[3]^b[3];
}

/*
 * Multiplication of 4 byte words
 * m(x) = x4+1
 */
void coef_mult(uint8_t *a, uint8_t *b, uint8_t *d)
{

    d[0] = gmult(a[0],b[0])^gmult(a[3],b[1])^gmult(a[2],b[2])^gmult(a[1],b[3]);
    d[1] = gmult(a[1],b[0])^gmult(a[0],b[1])^gmult(a[3],b[2])^gmult(a[2],b[3]);
    d[2] = gmult(a[2],b[0])^gmult(a[1],b[1])^gmult(a[0],b[2])^gmult(a[3],b[3]);
    d[3] = gmult(a[3],b[0])^gmult(a[2],b[1])^gmult(a[1],b[2])^gmult(a[0],b[3]);
}

/*
 * S-box transformation table
 */
static uint8_t s_box[256] =
{
    // 0     1     2     3     4     5     6     7     8     9     a     b     c     d     e     f
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, // 0
    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, // 1
    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, // 2
    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, // 3
    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, // 4
    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, // 5
    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, // 6
    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, // 7
    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, // 8
    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, // 9
    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, // a
    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, // b
    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, // c
    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, // d
    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, // e
    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};// f

/*
 * Inverse S-box transformation table
 */
static uint8_t inv_s_box[256] =
{
    // 0     1     2     3     4     5     6     7     8     9     a     b     c     d     e     f
    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, // 0
    0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, // 1
    0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, // 2
    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, // 3
    0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, // 4
    0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, // 5
    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, // 6
    0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, // 7
    0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, // 8
    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, // 9
    0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, // a
    0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, // b
    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, // c
    0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, // d
    0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, // e
    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};// f


/*
 * Generates the round constant Rcon[i]
 */
uint8_t R[] = {0x02, 0x00, 0x00, 0x00};

uint8_t * Rcon(uint8_t i)
{

    if (i == 1)
    {
        R[0] = 0x01; // x^(1-1) = x^0 = 1
    }
    else if (i > 1)
    {
        R[0] = 0x02;
        i--;
        while (i-1 > 0)
        {
            R[0] = gmult(R[0], 0x02);
            i--;
        }
    }

    return R;
}

/*
 * Transformation in the Cipher and Inverse Cipher in which a Round
 * Key is added to the State using an XOR operation. The length of a
 * Round Key equals the size of the State (i.e., for Nb = 4, the Round
 * Key length equals 128 bits/16 bytes).
 */
void add_round_key(uint8_t *state, uint8_t *w, uint8_t r)
{

    uint8_t c;

    for (c = 0; c < Nb; c++)
    {
        state[Nb*0+c] = state[Nb*0+c]^w[4*Nb*r+4*c+0];   //debug, so it works for Nb !=4
        state[Nb*1+c] = state[Nb*1+c]^w[4*Nb*r+4*c+1];
        state[Nb*2+c] = state[Nb*2+c]^w[4*Nb*r+4*c+2];
        state[Nb*3+c] = state[Nb*3+c]^w[4*Nb*r+4*c+3];
    }
}

/*
 * Transformation in the Cipher that takes all of the columns of the
 * State and mixes their data (independently of one another) to
 * produce new columns.
 */
void mix_columns(uint8_t *state)
{

    uint8_t a[] = {0x02, 0x01, 0x01, 0x03}; // a(x) = {02} + {01}x + {01}x2 + {03}x3
    uint8_t i, j, col[4], res[4];

    for (j = 0; j < Nb; j++)
    {
        for (i = 0; i < 4; i++)
        {
            col[i] = state[Nb*i+j];
        }

        coef_mult(a, col, res);

        for (i = 0; i < 4; i++)
        {
            state[Nb*i+j] = res[i];
        }
    }
}

/*
 * Transformation in the Inverse Cipher that is the inverse of
 * MixColumns().
 */
void inv_mix_columns(uint8_t *state)
{

    uint8_t a[] = {0x0e, 0x09, 0x0d, 0x0b}; // a(x) = {0e} + {09}x + {0d}x2 + {0b}x3
    uint8_t i, j, col[4], res[4];

    for (j = 0; j < Nb; j++)
    {
        for (i = 0; i < 4; i++)
        {
            col[i] = state[Nb*i+j];
        }

        coef_mult(a, col, res);

        for (i = 0; i < 4; i++)
        {
            state[Nb*i+j] = res[i];
        }
    }
}

/*
 * Transformation in the Cipher that processes the State by cyclically
 * shifting the last three rows of the State by different offsets.
 */
void shift_rows(uint8_t *state)
{

    uint8_t i, k, s, tmp;

    for (i = 1; i < 4; i++)
    {
        // shift(1,4)=1; shift(2,4)=2; shift(3,4)=3
        // shift(r, 4) = r;
        s = 0;
        while (s < i)
        {
            tmp = state[Nb*i+0];

            for (k = 1; k < Nb; k++)
            {
                state[Nb*i+k-1] = state[Nb*i+k];
            }

            state[Nb*i+Nb-1] = tmp;
            s++;
        }
    }
}

/*
 * Transformation in the Inverse Cipher that is the inverse of
 * ShiftRows().
 */
void inv_shift_rows(uint8_t *state)
{

    uint8_t i, k, s, tmp;

    for (i = 1; i < 4; i++)
    {
        s = 0;
        while (s < i)
        {
            tmp = state[Nb*i+Nb-1];

            for (k = Nb-1; k > 0; k--)
            {
                state[Nb*i+k] = state[Nb*i+k-1];
            }

            state[Nb*i+0] = tmp;
            s++;
        }
    }
}

/*
 * Transformation in the Cipher that processes the State using a non­
 * linear byte substitution table (S-box) that operates on each of the
 * State bytes independently.
 */
void sub_bytes(uint8_t *state)
{

    uint8_t i, j;
    uint8_t row, col;

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < Nb; j++)
        {
            row = (state[Nb*i+j] & 0xf0) >> 4;
            col = state[Nb*i+j] & 0x0f;
            state[Nb*i+j] = s_box[16*row+col];
        }
    }
}

/*
 * Transformation in the Inverse Cipher that is the inverse of
 * SubBytes().
 */
void inv_sub_bytes(uint8_t *state)
{

    uint8_t i, j;
    uint8_t row, col;

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < Nb; j++)
        {
            row = (state[Nb*i+j] & 0xf0) >> 4;
            col = state[Nb*i+j] & 0x0f;
            state[Nb*i+j] = inv_s_box[16*row+col];
        }
    }
}

/*
 * Function used in the Key Expansion routine that takes a four-byte
 * input word and applies an S-box to each of the four bytes to
 * produce an output word.
 */
void sub_word(uint8_t *w)
{

    uint8_t i;

    for (i = 0; i < 4; i++)
    {
        w[i] = s_box[16*((w[i] & 0xf0) >> 4) + (w[i] & 0x0f)];
    }
}

/*
 * Function used in the Key Expansion routine that takes a four-byte
 * word and performs a cyclic permutation.
 */
void rot_word(uint8_t *w)
{

    uint8_t tmp;
    uint8_t i;

    tmp = w[0];

    for (i = 0; i < 3; i++)
    {
        w[i] = w[i+1];
    }

    w[3] = tmp;
}

/*
 * Key Expansion
 */
void key_expansion(uint8_t *key, uint8_t *w)
{

    uint8_t tmp[4];
    uint8_t i;
    uint8_t len = Nb*(Nr+1);

    for (i = 0; i < Nk; i++)
    {
        w[4*i+0] = key[4*i+0];
        w[4*i+1] = key[4*i+1];
        w[4*i+2] = key[4*i+2];
        w[4*i+3] = key[4*i+3];
    }

    for (i = Nk; i < len; i++)
    {
        tmp[0] = w[4*(i-1)+0];
        tmp[1] = w[4*(i-1)+1];
        tmp[2] = w[4*(i-1)+2];
        tmp[3] = w[4*(i-1)+3];

        if (i%Nk == 0)
        {

            rot_word(tmp);
            sub_word(tmp);
            coef_add(tmp, Rcon(i/Nk), tmp);

        }
        else if (Nk > 6 && i%Nk == 4)
        {

            sub_word(tmp);

        }

        w[4*i+0] = w[4*(i-Nk)+0]^tmp[0];
        w[4*i+1] = w[4*(i-Nk)+1]^tmp[1];
        w[4*i+2] = w[4*(i-Nk)+2]^tmp[2];
        w[4*i+3] = w[4*(i-Nk)+3]^tmp[3];
    }
}

void cipher(uint8_t *in, uint8_t *out, uint8_t *w)
{

    uint8_t state[4*Nb];
    uint8_t r, i, j;

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < Nb; j++)
        {
            state[Nb*i+j] = in[i+4*j];
        }
    }

    add_round_key(state, w, 0);

    for (r = 1; r < Nr; r++)
    {
        sub_bytes(state);
        shift_rows(state);
        mix_columns(state);
        add_round_key(state, w, r);
    }

    sub_bytes(state);
    shift_rows(state);
    add_round_key(state, w, Nr);

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < Nb; j++)
        {
            out[i+4*j] = state[Nb*i+j];
        }
    }
}

void inv_cipher(uint8_t *in, uint8_t *out, uint8_t *w)
{

    uint8_t state[4*Nb];
    uint8_t r, i, j;

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < Nb; j++)
        {
            state[Nb*i+j] = in[i+4*j];
        }
    }

    add_round_key(state, w, Nr);

    for (r = Nr-1; r >= 1; r--)
    {
        inv_shift_rows(state);
        inv_sub_bytes(state);
        add_round_key(state, w, r);
        inv_mix_columns(state);
    }

    inv_shift_rows(state);
    inv_sub_bytes(state);
    add_round_key(state, w, 0);

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < Nb; j++)
        {
            out[i+4*j] = state[Nb*i+j];
        }
    }
}

/*******************结束*********************/

//将原始string转换为密文
//原始数据长度: orign
//加密后的数据:text
bool EncryptDataToCipherTxt(uint8_t *orign, uint8_t *result, uint16_t length)
{
    uint8_t w[240];      //密钥扩展,定义最大长度

    //根据密钥长度计算Nk,Nr
    switch (sizeof(key))
    {
    default:
    case 16:
        Nk = 4;
        Nr = 10;
        break;
    case 24:
        Nk = 6;
        Nr = 12;
        break;
    case 32:
        Nk = 8;
        Nr = 14;
        break;
    }

    //计算出扩展密钥的值
    key_expansion(key, w);

    //分块加密,每段16字节
    if( length % 16 == 0 )
    {
        uint16_t i;
        uint16_t counter=length / 16;
        uint8_t *p,*q;

        for(i=0; i<counter; i++)
        {
            p = &orign[16*i];
            q = &result[16*i];
            cipher(p, q, w);   //加密
        }
    }
    else
    {
        return false;
    }
    return true;
}

//将密文转为名为str
//原始已加密字符串: orign
//解密后字符串:result
bool DecryptCipherTxtToData(uint8_t *orign, uint8_t *result, uint16_t length)
{
    uint8_t w[240];      //密钥扩展,定义最大长度

    //根据密钥长度计算Nk,Nr
    switch (sizeof(key))
    {
    default:
    case 16:
        Nk = 4;
        Nr = 10;
        break;
    case 24:
        Nk = 6;
        Nr = 12;
        break;
    case 32:
        Nk = 8;
        Nr = 14;
        break;
    }

    //计算出扩展密钥的值
    key_expansion(key, w);

    //分块加密,每段16字节
    if( length % 16 == 0 )
    {
        uint16_t i;
        uint16_t counter=length / 16;
        uint8_t *p,*q;

        for(i=0; i<counter; i++)
        {
            p = &orign[16*i];
            q = &result[16*i];
            inv_cipher(p, q, w);   //解密
        }
    }
    else
    {
        return false;
    }
    return true;
}

aes.h:

#ifndef __AES_H
#define __AES_H

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

//加密
bool EncryptDataToCipherTxt(uint8_t *orign, uint8_t *result, uint16_t length);

//解密
bool DecryptCipherTxtToData(uint8_t *orign, uint8_t *result, uint16_t length);

#endif

main.c:

#include <stdio.h>
#include <stdint.h>
#include "aes.h"
 
#define  AES_ENC_MAX_LEN    (512)
 
//使用例程
int main(int argc, char *argv[])
{
    uint16_t i=0;
 
    //定义加密缓存数据
    char    in[AES_ENC_MAX_LEN]="{\"name\":\"Jack Nimble\",\"format\":{\"type\":\"rect\",\"width\":1920,\"height\":1080,\"interlace\":false,\"framerate\":24}}";
    uint8_t  out[AES_ENC_MAX_LEN];
 
    //获取数据长度
    uint16_t length=strlen(in);
 
    //原始数据
    printf("原始数据:\n");
    printf("报文长度%d\n",strlen(in));
    printf("%s\n\n", in);
 
    //数据长度非16字节整除,则补齐
    while(length%16)
    {
        strcat(in,"\0");   //用0补齐
        length++;
    }
 
    //原始数据
    printf("补位后数据:\n");
    printf("报文长度%d\n",strlen(in));
    printf("%s\n\n", in);
 
    //加密数据
    printf("加密数据:\n");
    EncryptDataToCipherTxt((uint8_t*)in,out,length);
    printf("密文长度=%d\n",length);
    for(i=0; i<length; i++)
    {
        printf("%02x ", out[i]);
    }
    printf("\n\n");
 
    // 清空in内容
    memset(in, 0x00, AES_ENC_MAX_LEN);
 
    //将字符数组转为字符串
    printf("解密数据:\n");
    DecryptCipherTxtToData(out,(uint8_t*)in,length);
    printf("解密报文长度=%d\n",length);
    printf("%s\n",in);
 
    return 0;