AES加密算法及逆向

AES加密算法逆向以及特征识别

1.AES加密算法

关于AES加密算法,网上资料特别特别多,个人认为可以从这几点去学习:

  • AES加密算法设计者的相关论文以及其他相关论文(如安全性、性能、硬件/指令实现等)
  • 相关书籍、维基百科、国内外他人的优质文章/博客、论坛等
  • 标准文件,如:NIST、ISO、ITU-T、RFC等发布的标准文件
  • 一些加密库,如Openssl、Crypto++、EverCrypt、Botan等

部分相关资料:

  1. AES Proposal: Rijndael 链接1
  2. 现代密码学理论与实践5 ⾼级加密标准AES 链接2
  3. AES 链接3
  4. 维基百科-高级加密标准 链接4
  5. AES加密算法的详细介绍与实现 链接5
  6. 密码学基础:AES加密算法 链接6
  7. 密码算法详解——AES 链接7
  8. William Stallings著;王张宜等译. 密码编码学与网络安全——原理与实践(第五版)[M]. 北京:电子工业出版社,2012.1.
  9. 二又三大佬的博客 高级加密标准-AES 链接8
  10. yjp大佬的博客 AES算法详解 链接9

1.1 介绍

AES加密算法就是众多对称加密算法中的一种,它的英文全称是Advanced Encryption Standard,翻译过来是高级加密标准,它是用来替代之前的DES加密算法的。AES加密算法采用分组密码体制,明文分组的长度为128位即16字节,密钥长度可以为16,24或者32字节(128,192,256位),根据密钥的长度,算法被称为AES-128,AES-192或者AE-256。一共有四种加密模式,我们通常采用需要初始向量IV的CBC模式,初始向量的长度也是128位16个字节。AES的加密模式有以下几种:电码本模式(ECB)密码分组链接模式(CBC)计算器模式(CTR)密码反馈模式(CFB)输出反馈模式(OFB)

部分术语定义和表示:
状态(State):密码运算的中间结果称为状态。
State的表示:状态用以字节为基本构成元素的矩阵阵列来表示,该阵列有4行,列数记为Nb。 Nb=分组长度(bits)÷ 32。Nb可以取的值为4,对应的分组长度为128bits。
密码密钥(Cipher Key)的表示: Cipher Key类似地用一个4行的矩阵阵列来表示,列数记为Nk。Nk=密钥长度(bits)÷32。Nk可以取的值为4,6,8,对应的密钥长度为128, 192, 256 bits。

密码学三大原则:扩散原则混淆原则雪崩效应
雪崩效应:雪崩效应(avalanche effect)指加密算法(尤其是块密码和加密散列函数)的一种理想属性。雪崩效应是指当输入发生最微小的改变(例如,反转一个二进制位)时,也会导致输出的不可区分性改变(输出中每个二进制位有50%的概率发生反转)。合格块密码中,无论密钥或明文的任何细微变化都必须引起密文的不可区分性改变。 --摘自维基百科 雪崩效应

1.2 加密流程

AES未使用Feistel结构(关于Feistel结构详见 链接10)。其前N-1轮由4个不同的变换组成(即涉及的主要操作有4种):字节替代(SubByte)、行移位(ShiftRow)、列混淆(MixColumn)和轮密钥加(AddRoundKey)。最后一轮仅包含三个变换。而在第一轮前面有一个起始的单变换(轮密钥加),可以视为0轮。解密过程分别为对应的逆操作。由于每一步操作都是可逆的,按照相反的顺序进行解密即可恢复明文。

  • 字节代替(SubBytes):用一个S盒完成分组的字节到字节的代替。
  • 行移位(ShiftRows):一个简单的置换。
  • 列混淆(MixColumns):利用域GF(2^8)上的算术特性的一个代替。
  • 轮密钥加(AddRoundKey):当前分组和扩展密钥的一部分进行按位异或XOR。

在这里插入图片描述

1.2.1 密钥扩展

AES密钥调度

  • 密钥bit的总数 = 分组长度 x (轮数Round + 1)
  • 当分组长度是128bit且轮数为10时,轮密钥长度为128∗(10+1)=1408bit
  • 将初始密钥扩展成扩展密钥
  • 轮密钥从扩展密钥中取,第1轮轮密钥取扩展密钥的前Nb个字,第2轮轮密钥取接下来的Nb个字,以此类推。

AES密钥扩展图如下:
在这里插入图片描述
函数T由三部分组成:字循环移位、字节代换和轮常量异或。

  • 字循环移位:将1个字中的4个字节循环左移1个字节,即将输入字[b0,b1,b2,b3]变换为[b1,b2,b3,b0]。
  • 字节代换:对字循环的结果使用S盒进行字节代换。
  • 轮常量异或: 将前两步的结果同轮常量Rcon[j]进行异或,其中j表示轮数。
  • 轮常量是一个字,使用轮常量是为了防止不同轮中产生的轮密钥的对称性或相似性。

1.2.2 字节替代

字节代换(SubBytes)是对state矩阵中的每一个独立元素于置换盒Substitution-box,S盒)中进行查找并以此替换输入状态的操作。字节代换是可逆的非线性变换,也是AES运算组中唯一的非线性变换。字节代换逆操作也是通过逆向置换盒的查找及替换来完成的。
映射方式:把该字节的高4位作为行值,低4位作为列值,以这些行列值作为索引从S盒中对应位置取出元素作为输出,假设输入字节的值为a=a7a6a5a4a3a2a1a0,则输出值为S[a7a6a5a4][a3a2a1a0]。例如,十六进制数{23}所对应的S盒的行值是2,列值是3,S盒中在此位置的值是{26},相应的{23}被映射为{26};字节00000000B替换后的值为(S[0][0])63H
s盒
逆S盒
S盒是事先设计好的16×16的查询表,即256个元素。其设计不是随意的,要根据设计原则严格计算求得,不然无法保证算法的安全性。既然是S盒是计算得来,所以字节代换的操作完全可以通过计算来完成,不过通过S盒查表操作更方便快捷,图中所示就是通过S盒查找对应元素进行的替换操作。

Q:AES加密的S盒与DES加密的S盒有什么区别?
A: from 链接11

  • AES的S盒的原理是运用了GF(2^8)的乘法逆和矩阵的可逆运算来保证加密与解密过程的可逆性。DES的S盒设计主要是为了确保非线性关系,并不需要可逆。因而在设计理念上有极大的不同。
  • AES的S盒与DES的S盒形式上差别也很大,AES的S盒输入和输出的位数相同,均为128位,大小为16×16的字节矩阵,且只有1组;而DES的S盒输入6位,输出只有4位,大小是4×16的位矩阵,并且有8组。在输入的坐标选择规定上亦有不同。

Q:为什么要用查表法
A:查表法虽然牺牲了空间,但是效率高,更快捷。
Q:为什么S盒的元素是定值或者说计算而来?怎么计算的?
A(摘自看雪版主看场雪的回答 链接11):S盒是AES的核心部件,负责提供“非线性变换”的功能。理论上,除了现在AES所使用的“生成S盒的算法”以外,还有很多方法可以生成S盒。但其要求并不是只有“随机”这一点。其重要的安全要求包括但不仅限于:

  • 输入与输出的相关性
  • 输出的自相关性
  • 输入单bit与输出多bit的扩散关系
  • 输入多bit与输出单bit的混淆关系
  • 可逆
    S盒的生成(或者说挑选)是个深奥的学问。

S盒是按照这个公式计算出来的:GF(2^8) = GF(2) [x]/(x^8 + x^4 + x^3 + x + 1)
更为详细具体内容见以下链接;
AES128加密-S盒和逆S盒构造推导及代码实现 链接12
Rijndael S-box 链接13
初识白盒密码 链接14
有限域算术 链接15

1.2.3 行移位

行移位是一个4x4的矩阵内部字节之间的置换,用于提供算法的扩散性。
行移位变换完成基于行的循环移位操作,变换方法为:第0行不变,第1行循环左移1个字节,第2行循环左移两个字节,第3行循环左移3个字节。如下图所示:
在这里插入图片描述
逆向行移位即是相反的操作,即:第一行保持不变,第二行循环右移1个字节,第三行循环右移两个字节,第四行循环左移3个字节。

关于扩散性:在密码学中,扩散性是指算法对输入的较小改变产生较大输出变化的能力,也就是就是让明文中的每一位影响密文中的许多位,或者说让密文中的每一位受明文中的许多位的影响.这样可以隐蔽明文的统计特性。比如,在对称加密算法中,如果明文和密文之间的转换具有较小的扩散性,则攻击者可能会使用差分攻击来破解密码。差分攻击就是利用密文的相似性来推断出密钥的技术。因此,扩散性是密码学算法的一个重要特性,通常用来衡量密码学算法的安全性。

扩散原则(Diffusion)就是应将明文的统计规律和结构规律散射到相当长的一段统计中去(Shannon的原话)。
也就是说让明文中的每一位影响密文中的尽可能多的位,或者说让密文中的每一位都受到明文中的尽可能多位的影响。
如果当明文变化一个比特时,密文有某些比特不可能发生变化,则这个明文就与那些密文无关,因而在攻击这个明文比比特时就可不利用那些密文比特。

关于差分攻击:(差分攻击是由Biham和Shamir于1991年提出的选择明文攻击方法。它是针对分组密码攻击最有效的方法之一。差分攻击通过分析特定明文差分对相对应密文差分的影响来提取密钥。差分分析现在被广泛应用于各种分组密码的攻击。

关于差分分析更加详细具体的内容见:

  1. 搞懂差分密码分析,这篇文章就够了!!链接16
  2. 差分分析(Differential Cryptandysis)方法 链接17
  3. Differential Cryptanalysis Tutorial 链接18

1.2.4 列混淆

列混淆是将状态数组的每一列乘以一个矩阵,其中乘法是在有限域GF(2^8)上进行的,分为正向列混淆和列混淆逆变换两种操作。正向列混淆用于加密操作,列混淆逆变换用于解密操作。
正向列混淆变换过程:
在这里插入图片描述
逆向列混淆变换可以再乘以矩阵的逆得到。

列混淆变换中,通过矩阵相乘的运算使得明文中的字节具有很好的混淆效果,而且经过几轮变换之后,列混淆变换和行移位变换使得所有输出位均与所有输入位相关。

关于混淆:在加密算法中,明文、密文、密钥之间的关系要足够复杂,防止统计分析攻击。

混淆原则(Confusion)就是将密文、明文、密钥三者之间的统计关系和代数关系变得尽可能复杂,使得敌手即使获得了密文和明文,也无法求出密钥的任何信息;即使获得了密文和明文的统计规律,也无法求出明文的新的信息。
可进一步理解为:

  • 明文不能由已知的明文,密文及少许密钥比特代数地或统计地表示出来。
  • 密钥不能由已知的明文,密文及少许密钥比特代数地或统计地表示出来

Q:AES算法最后一轮为什么没有列混淆?
A:详见 aes算法最后一轮为什么没有列混淆? 链接19

1.2.5 轮密钥加

密钥加是将轮密钥简单地与状态进行逐比特异或。这个操作相对简单,其依据的原理是“任何数和自身的异或结果为0”。加密过程中,每轮的输入与轮子密钥异或一次;因此,解密时再异或上该轮的轮子密钥即可恢复。
轮密钥加过程可以看成是字逐位异或的结果,也可以看成字节级别或者位级别的操作。也就是说,可以看成S0 S1 S2 S3 组成的32位字与W[4i]的异或运算。
在这里插入图片描述

1.3 AES加密算法的实现

1.3.1 python

AES256,CBC模式,字符集UTF-8

# 需要安装 pycryptodomex 库
from Cryptodome.Cipher import AES
import base64

# 加密
def aes_encrypt(key, content):
    key = key.encode()
    # 如果 key 不是 32 位的,则使用 hashlib 库进行 hash
    if len(key) != 32:
        import hashlib
        key = hashlib.sha256(key).hexdigest()
    # 如果 content 不是 16 位的倍数,则进行补全
    add = 16 - len(content.encode()) % 16
    content += '\0' * add
    cipher = AES.new(key, AES.MODE_CBC, b'0123456789012345')
    result = base64.b64encode(cipher.encrypt(content.encode()))
    return result.decode()

# 解密
def aes_decrypt(key, content):
    key = key.encode()
    # 如果 key 不是 32 位的,则使用 hashlib 库进行 hash
    if len(key) != 32:
        import hashlib
        key = hashlib.sha256(key).hexdigest()
    cipher = AES.new(key, AES.MODE_CBC, b'0123456789012345')
    content = base64.b64decode(content.encode())
    result = cipher.decrypt(content).decode()
    return result.rstrip('\0')
key = '1234567890abcdefghijklmnopqrstuv'
content = 'hello world'
encrypted = aes_encrypt(key, content)
decrypted = aes_decrypt(key, encrypted)
print(encrypted)
print(decrypted)
#wJFt54fc35P0QQo/ZFHakQ==
#hello world

1.3.2 go

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"log"
	"unicode/utf8"
)

func main() {
    
    
	key := []byte("your-aes-256-key-goes-here")
	plaintext := "This is the message to be encrypted with AES-256 in CBC mode."

	// 加密
	block, err := aes.NewCipher(key)
	if err != nil {
    
    
		log.Fatal(err)
	}
	iv := []byte("initialization-vector")
	encrypter := cipher.NewCBCEncrypter(block, iv)
	plaintextBytes := []byte(plaintext)
	paddedPlaintextBytes := pad(plaintextBytes, block.BlockSize())
	ciphertextBytes := make([]byte, len(paddedPlaintextBytes))
	encrypter.CryptBlocks(ciphertextBytes, paddedPlaintextBytes)
	ciphertext := base64.StdEncoding.EncodeToString(ciphertextBytes)

	// 解密
	ciphertextBytes, err = base64.StdEncoding.DecodeString(ciphertext)
	if err != nil {
    
    
		log.Fatal(err)
	}
	decrypter := cipher.NewCBCDecrypter(block, iv)
	plaintextBytes = make([]byte, len(ciphertextBytes))
	decrypter.CryptBlocks(plaintextBytes, ciphertextBytes)
	plaintextBytes = unpad(plaintextBytes)
	plaintext = string(plaintextBytes)

	fmt.Println(plaintext)
}

// pad 函数用于填充明文,使它的长度是 blockSize 的倍数。
func pad(src []byte, blockSize int) []byte {
    
    
	padding := blockSize - len(src)%blockSize
	padtext := bytes.Repeat([]byte{
    
    byte(padding)}, padding)
	return append(src, padtext...)
}

// unpad 函数用于去除填充的字节。
func unpad(src []byte) []byte {
    
    
	length := len(src)
	unpadding := int(src[length-1])
	if length < 4 {
    
    
		unpadding = length
	}
	return src[:(length - unpadding)]
}

1.3.3 Ruby

require 'openssl'
require 'base64'

def aes256_cbc_encrypt(key, iv, plaintext)
  cipher = OpenSSL::Cipher.new('AES-256-CBC')
  cipher.encrypt
  cipher.key = key
  cipher.iv = iv
  ciphertext = cipher.update(plaintext) + cipher.final
  Base64.strict_encode64(ciphertext)
end

def aes256_cbc_decrypt(key, iv, ciphertext)
  ciphertext = Base64.strict_decode64(ciphertext)
  cipher = OpenSSL::Cipher.new('AES-256-CBC')
  cipher.decrypt
  cipher.key = key
  cipher.iv = iv
  plaintext = cipher.update(ciphertext) + cipher.final
  plaintext.force_encoding('UTF-8')
end

key = 'your-aes-256-key-goes-here'
iv = 'initialization-vector'
plaintext = 'This is the message to be encrypted with AES-256 in CBC mode.'

# 加密
ciphertext = aes256_cbc_encrypt(key, iv, plaintext)

# 解密
plaintext = aes256_cbc_decrypt(key, iv, ciphertext)

puts plaintext

1.3.4 C语言

AES源码 链接20
AES256_CBC 链接21

此处链接21的源代码,仅用于学习
AES256 CBC

/* author:sanjeevmishra391
   from:https://github.com/sanjeevmishra391/AES256_CBC
   仅用于学习
*/
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<stdint.h>

uint32_t word[60];

uint8_t sBox[16][16] = {
    
    
        {
    
    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}};

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

uint32_t rCon[10] = {
    
    (uint32_t) 0x01000000, (uint32_t) 0x02000000, (uint32_t) 0x04000000, (uint32_t) 0x08000000, (uint32_t) 0x10000000, (uint32_t) 0x20000000, (uint32_t) 0x40000000, (uint32_t) 0x80000000, (uint32_t) 0x1B000000, (uint32_t) 0x36000000};

// key scheduling -- rotate word
uint32_t rotWord(uint32_t w){
    
    
    uint32_t temp = w>>24;
    w<<=8;
    w|=temp;
    return w;
}

// key scheduling -- substitution word
uint32_t subWord(uint32_t w) {
    
    
    uint32_t retme=0;
    int i;
    for(i=0;i<4;i++){
    
    
        retme<<=8;
        uint8_t temp = (w>>(8*(3-i)))& 0xff;
        uint8_t r = temp >> 4;
        uint8_t c = temp & 15;
        retme |= sBox[r][c];
    }
    return retme;
}

// key scheduling algorithm of AES-256
void aes_key_schedule_256(const uint8_t *key) {
    
    
	uint32_t temp;
	int i=0, k=0;
	printf("\nRound keys:\n");
	for(i=0; i<8; i++) {
    
    
		word[i] = (key[4*i]<<24 | key[4*i+1]<<16 | key[4*i+2]<<8 | key[4*i+3]);
		printf("%02x ", word[i]);
		k++;
	}

	for(i=8; i<120; i++) {
    
    
		temp = word[i-1];
		if(k%8==0) {
    
    
			printf("\n");
		}
		if(i%4==0) {
    
    
			temp = subWord(rotWord(temp)) ^ rCon[i/4-1];
		}
		word[i] = word[i-4] ^ temp;
		printf("%02x ", word[i]);
		k++;
	}
}

// subByte function
void subByte(uint8_t matrix[4][4]) {
    
    
	int i, j;
    for (i = 0; i < 4; i++) {
    
    
        for (j = 0; j < 4; j++) {
    
    
            uint8_t temp = matrix[i][j];
            uint8_t r = temp >> 4;
            uint8_t c = temp & 15;
            matrix[i][j] = sBox[r][c];
        }
    }
}

// inverse SubByte function
void invSubByte(uint8_t matrix[4][4]) {
    
    
	int i, j;
    for (i = 0; i < 4; i++) {
    
    
        for (j = 0; j < 4; j++) {
    
    
            uint8_t temp = matrix[i][j];
            uint8_t r = temp >> 4;
            uint8_t c = temp & 15;
            matrix[i][j] = invSBox[r][c];
        }
    }
}

// shift rows function
void shiftRows(uint8_t matrix[4][4]) {
    
    
	int i, k, j;
    for (i = 1; i < 4; i++) {
    
    
        int arr[4];
        for (k = i; k < 4; k++) {
    
    
            arr[k - i] = matrix[i][k];
        }
        for (j = 0; j < i; j++) {
    
    
            arr[4 - i + j] = matrix[i][j];
        }
        for (j = 0; j < 4; j++) {
    
    
            matrix[i][j] = arr[j];
        }
    }
}

// inverse shift rows function
void invShiftRows(uint8_t matrix[4][4]) {
    
    
	int i, k, j;
    for (i = 1; i < 4; i++) {
    
    
        int arr[4];
        for (k = i; k <= 4; k++) {
    
    
            arr[k] = matrix[i][k-i];
        }
        for (j = 0; j < i; j++) {
    
    
        	arr[j] = matrix[i][4-i+j];
        }
        for (j = 0; j < 4; j++) {
    
    
            matrix[i][j] = arr[j];
        }
    }
}

// helper function used in mix Column
uint8_t mult(uint8_t x)
{
    
    
	uint8_t temp = x, g;
	if(temp>>7 == 0) {
    
    
		g = x<<1;
	} else {
    
    
		g = (x<<1)^27;
	}
	return g;
}

// mix column function
void mixCol(uint8_t matrix[4][4]) {
    
    

	uint8_t G[4][4] = {
    
    
        {
    
    2, 3, 1, 1},
        {
    
    1, 2, 3, 1},
        {
    
    1, 1, 2, 3},
        {
    
    3, 1, 1, 2}};
    int8_t temp[4][4];

    int row, col, r;
    for (row = 0; row < 4; row++)
    {
    
    
        for (col = 0; col < 4; col++)
        {
    
    
            temp[row][col] = 0;
        }
    }
    // simple matrix multiplication
    for (row = 0; row < 4; row++)
    {
    
    
        for (col = 0; col < 4; col++)
        {
    
    
            for (r = 0; r < 4; r++)
            {
    
    
                if (G[row][r] == 2)
                {
    
    
                    temp[row][col] ^= mult(matrix[r][col]);
                }
                else if (G[row][r] == 3)
                {
    
    
                    temp[row][col] ^= (mult(matrix[r][col]) ^ matrix[r][col]);
                }
                else
                {
    
    
                    temp[row][col] ^= matrix[r][col];
                }
            }
        }
    }

    for (row = 0; row < 4; row++)
    {
    
    
        for (col = 0; col < 4; col++)
        {
    
    
            matrix[row][col] = temp[row][col];
        }
    }
}

// inverse mix column function
void invMixCol(uint8_t matrix[4][4]) {
    
    
	//  it is true that M^4=I. So, by performing this transformation trice you get its inverse (M^3=M^-1)
	mixCol(matrix);
	mixCol(matrix);
	mixCol(matrix);
}

// function to print state matrix
void printMatrix(uint8_t matrix[4][4]) {
    
    
	int i=0, j=0;
	printf("\n");
	for(i=0; i<4; i++) {
    
    
		for(j=0; j<4; j++) {
    
    
			printf("%02x ", matrix[i][j]);
		}
		printf("\n");
	}
}

// AES-256 Encryption
void aes_256_encryption(uint8_t MA[32], uint8_t iv[16], uint8_t key[32], uint8_t ciphertext[32]) {
    
    
	printf("\n\n----------------AES_256 Encryption process---------------\n");
	int sets = 0;
	printf("\nInitial key:\n");
	int i;
	for(i=0; i<32; i++) {
    
    
		printf("%02x ", key[i]);
	}
	printf("\n");

	aes_key_schedule_256(key);
	printf("\n");
	while(sets<2) {
    
    
		uint8_t message[16];
		printf("\nMessage part %d (16 bytes):\n", sets+1);
		for(i=0; i<16; i++) {
    
    
			message[i] = MA[16*sets+i];
			printf("%02x ", message[i]);
			message[i] = message[i] ^ iv[i];
		}
		printf("\n");

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

		int j=0, l=0, k=0;
		// Round 0 -- add round key to state matrix
	    for(i=0;i<4;i++){
    
    
	        for(j=0;j<4;j++) {
    
    
	        	uint8_t roundKey[4];
				roundKey[0] = word[i]>>24;
				roundKey[1] = word[i]>>16 & 0xFFFFFFFF;
				roundKey[2] = word[i]>>8 & 0xFFFFFFFFFFFFFFFF;
				roundKey[3] = word[i] & 0xFFFFFFFF;
	            matrix[j][i] ^= roundKey[j];
	        }
	    }

		// Rounds 1 to 13 -- SUBBYTE, SHIFTROWS, MIXCOL, ADDROUNDKEY
		l=4;
	    for(k=1;k<14;k++){
    
    
	        subByte(matrix);
	        shiftRows(matrix);
	        mixCol(matrix);
			for(i=0;i<4;i++){
    
    
	            for(j=0;j<4;j++){
    
    
	                uint8_t roundKey[4];
					roundKey[0] = word[l]>>24;
					roundKey[1] = word[l]>>16 & 0xFFFFFFFF;
					roundKey[2] = word[l]>>8 & 0xFFFFFFFFFFFFFFFF;
					roundKey[3] = word[l] & 0xFFFFFFFF;
		            matrix[j][i] ^= roundKey[j];
	            }
	            l++;
	        }
	    }

		// Round 14 -- SUBBYTE, SHIFTROWS, ADDROUNDKEY
	    subByte(matrix);
	    shiftRows(matrix);
	    for(i=0;i<4;i++){
    
    
	        for(j=0;j<4;j++){
    
    
	            uint8_t roundKey[4];
				roundKey[0] = word[l]>>24;
				roundKey[1] = word[l]>>16 & 0xFFFFFFFF;
				roundKey[2] = word[l]>>8 & 0xFFFFFFFFFFFFFFFF;
				roundKey[3] = word[l] & 0xFFFFFFFF;
	            matrix[j][i] ^= roundKey[j];
	        }
	        l++;
	    }

	    k=0;
		//  storing matrix into ciphertext array
	    for(i=0;i<4;i++) {
    
    
	        for(j=0;j<4;j++) {
    
    
	            ciphertext[16*sets+k] = matrix[j][i];
	            k++;
	        }
	    }

		// updating IV array with ciphertext of previous chunk
	    for(i=0; i<16; i++) {
    
    
	    	iv[i] = ciphertext[16*sets + i];
		}
		sets++;
	}
}

void aes_256_decryption(uint8_t ciphertext[32], uint8_t iv[16], uint8_t key[32], uint8_t decryptedtext[32]) {
    
    
	printf("\n\n----------------AES_256 Decryption process---------------\n");
	int sets = 0, i;
	// aes_key_schedule_256(key);

	while(sets<2) {
    
    

		uint8_t cipher[16];
		printf("\nCiphertext part %d (16 bytes):\n", sets+1);
		for(i=0; i<16; i++) {
    
    
			cipher[i] = ciphertext[16*sets+i];
			printf("%02x ", cipher[i]);
		}
		printf("\n");

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

		int j=0, l=56, k=0;
		// adding round key
	    for(i=0;i<4;i++){
    
    
	        for(j=0;j<4;j++) {
    
    
	        	uint8_t roundKey[4];
				roundKey[0] = word[l]>>24;
				roundKey[1] = word[l]>>16 & 0xFFFFFFFF;
				roundKey[2] = word[l]>>8 & 0xFFFFFFFFFFFFFFFF;
				roundKey[3] = word[l] & 0xFFFFFFFF;
	            matrix[j][i] ^= roundKey[j];
	        }
	        l++;
	    }

		l=52;
		// performing operations of 13 rounds
	    for(k=1;k<14;k++){
    
    
	        invShiftRows(matrix);
	        invSubByte(matrix);
			for(i=0;i<4;i++){
    
    
	            for(j=0;j<4;j++){
    
    
	                uint8_t roundKey[4];
					roundKey[0] = word[l]>>24;
					roundKey[1] = word[l]>>16 & 0xFFFFFFFF;
					roundKey[2] = word[l]>>8 & 0xFFFFFFFFFFFFFFFF;
					roundKey[3] = word[l] & 0xFFFFFFFF;
		            matrix[j][i] ^= roundKey[j];
	            }
	            l++;
	        }
	        l = l-8;
	        invMixCol(matrix);
	    }

	    invShiftRows(matrix);
	    invSubByte(matrix);
	    l=0;
	    for(i=0;i<4;i++){
    
    
	        for(j=0;j<4;j++){
    
    
	            uint8_t roundKey[4];
				roundKey[0] = word[l]>>24;
				roundKey[1] = word[l]>>16 & 0xFFFFFFFF;
				roundKey[2] = word[l]>>8 & 0xFFFFFFFFFFFFFFFF;
				roundKey[3] = word[l] & 0xFFFFFFFF;
	            matrix[j][i] ^= roundKey[j];
	        }
	        l++;
	    }

	    // xor of iv and decrypted text to get plaintext
	    k=0;
	    for(i=0; i<4; i++) {
    
    
	    	for(j=0; j<4; j++) {
    
    
		    	matrix[j][i] = matrix[j][i] ^ iv[k];
		    	decryptedtext[16*sets + k] = matrix[j][i];
		    	k++;
		    }
		}

		// updating IV array with ciphertext of previous chunk
		for(i=0; i<16; i++) {
    
    
			iv[i] = ciphertext[16*sets + i];
		}

		sets++;
	}

}

int main() {
    
    

	int i;

	uint8_t msg[32] = {
    
     	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
						0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };

	uint8_t iv[16] = {
    
     	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
						0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

	uint8_t K[32] = {
    
     	0xfa, 0x4b, 0xf4, 0x72, 0x1c, 0x9f, 0xc5, 0x2e, 0x9f, 0xca, 0x9c, 0xcf, 0x22, 0xce, 0xa9, 0xd2,
						0xfb, 0x44, 0x90, 0x0e, 0xf9, 0x48, 0x64, 0x62, 0xa9, 0x14, 0xfe, 0x12, 0x77, 0xd3, 0x59, 0x71 };

	uint8_t CA[32];
	aes_256_encryption(msg, iv, K, CA);

	printf("\nCiphertext: \n");
	for(i=0; i<32; i++) {
    
    
		printf("%02x ", CA[i]);
	}

	for(i=0; i<16; i++) {
    
    
		iv[i] = 0x00;
	}

	uint8_t MB[32];
	aes_256_decryption(CA, iv, K, MB);

	printf("\nDecrypted Text: \n");
	for(i=0; i<32; i++) {
    
    
		printf("%02x ", MB[i]);
	}
}

其他c的源码,AES128 ECB

#include <stdio.h>
#include <windows.h>

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

//辅助函数
/*有限域*2乘法 The x2time() function */
static unsigned char x2time(unsigned char x)
{
    
    
	if (x & 0x80)
	{
    
    
		return (((x << 1) ^ 0x1B) & 0xFF);
	}
	return x << 1;
}
/*有限域*3乘法 The x2time() function */
static unsigned char x3time(unsigned char x)
{
    
    
	return (x2time(x) ^ x);
}
/*有限域*4乘法 The x4time() function */
static unsigned char x4time(unsigned char x)
{
    
    
	return (x2time(x2time(x)));
}
/*有限域*8乘法 The x8time() function */
static unsigned char x8time(unsigned char x)
{
    
    
	return (x2time(x2time(x2time(x))));
}
/*有限域9乘法 The x9time() function */
static unsigned char x9time(unsigned char x)	//9:1001
{
    
    
	return (x8time(x) ^ x);
}
/*有限域*B乘法 The xBtime() function */
static unsigned char xBtime(unsigned char x)	//B:1011
{
    
    
	return (x8time(x) ^ x2time(x) ^ x);
}
/*有限域*D乘法 The xDtime() function */
static unsigned char xDtime(unsigned char x)	//D:1101
{
    
    
	return (x8time(x) ^ x4time(x) ^ x);
}
/*有限域*E乘法 The xEtime() function */
static unsigned char xEtime(unsigned char x)	//E:1110
{
    
    
	return (x8time(x) ^ x4time(x) ^ x2time(x));
}
/*第三类操作:列混合操作 MixColumns: Process the entire block*/
static void MixColumns(unsigned char *col)//列混合
{
    
    
	unsigned char tmp[4], xt[4];
	int i;
	for (i = 0; i<4; i++, col += 4)  //col代表一列的基地址,col+4:下一列的基地址
	{
    
    
		tmp[0] = x2time(col[0]) ^ x3time(col[1]) ^ col[2] ^ col[3];	//2 3 1 1
		tmp[1] = col[0] ^ x2time(col[1]) ^ x3time(col[2]) ^ col[3];	//1 2 3 1
		tmp[2] = col[0] ^ col[1] ^ x2time(col[2]) ^ x3time(col[3]);	//1 1 2 3
		tmp[3] = x3time(col[0]) ^ col[1] ^ col[2] ^ x2time(col[3]);	//3 1 1 2
//修改后的值 直接在原矩阵上修改
		col[0] = tmp[0];
		col[1] = tmp[1];
		col[2] = tmp[2];
		col[3] = tmp[3];
	}
}
//逆向列混淆
static void Contrary_MixColumns(unsigned char *col)
{
    
    
	unsigned char tmp[4];
	unsigned char xt2[4];//colx2
	unsigned char xt4[4];//colx4
	unsigned char xt8[4];//colx8
	int x;
	for (x = 0; x<4; x++, col += 4)
	{
    
    
		tmp[0] = xEtime(col[0]) ^ xBtime(col[1]) ^ xDtime(col[2]) ^ x9time(col[3]);
		tmp[1] = x9time(col[0]) ^ xEtime(col[1]) ^ xBtime(col[2]) ^ xDtime(col[3]);
		tmp[2] = xDtime(col[0]) ^ x9time(col[1]) ^ xEtime(col[2]) ^ xBtime(col[3]);
		tmp[3] = xBtime(col[0]) ^ xDtime(col[1]) ^ x9time(col[2]) ^ xEtime(col[3]);
		col[0] = tmp[0];
		col[1] = tmp[1];
		col[2] = tmp[2];
		col[3] = tmp[3];
	}
}
/*第二类操作:行移位:行左循环移位 ShiftRows:Shifts the entire block*/
static void ShiftRows(unsigned char *col)//正向行移位
{
    
    
	unsigned char t;
	t = col[1]; col[1] = col[5]; col[5] = col[9]; col[9] = col[13]; col[13] = t;
	t = col[2]; col[2] = col[10]; col[10] = t;
	t = col[6]; col[6] = col[14]; col[14] = t;
	t = col[15]; col[15] = col[11]; col[11] = col[7]; col[7] = col[3]; col[3] = t;
}
//逆向行移位
static void Contrary_ShiftRows(unsigned char *col)
{
    
    
	unsigned char t;
	t = col[13]; col[13] = col[9]; col[9] = col[5]; col[5] = col[1]; col[1] = t;
	t = col[2]; col[2] = col[10]; col[10] = t;
	t = col[6]; col[6] = col[14]; col[14] = t;
	t = col[3]; col[3] = col[7]; col[7] = col[11]; col[11] = col[15]; col[15] = t;
}
/*第一类操作:s盒字节代换替换 SubBytes*/
static void SubBytes(unsigned char *col)//字节代换
{
    
    
	int x;
	for (x = 0; x<16; x++)
	{
    
    
		col[x] = sbox[col[x]];
	}
}
//逆向字节代换
static void Contrary_SubBytes(unsigned char *col)
{
    
    
	int x;
	for (x = 0; x<16; x++)
	{
    
    
		col[x] = contrary_sbox[col[x]];
	}
}
/*第四类操作:轮密钥加 AddRoundKey*/
static void AddRoundKey(unsigned char *col, unsigned char *expansionkey, int round)//密匙加
{
    
    
	//扩展密钥:44*32bit =11*4* 4*8 =  16字节*11轮,每轮用16字节密钥
	//第0轮,只进行一次轮密钥加
	//第1-10轮,轮密钥加
	int x;
	for (x = 0; x<16; x++)	//每1轮操作:4*32bit密钥 = 16个字节密钥
	{
    
    
		col[x] ^= expansionkey[(round << 4) + x];
	}
}
/* AES加密总函数 10轮4类操作 Encrypt a single block with Nr Rounds(10,12,14)*/
void AesEncrypt(unsigned char *blk, unsigned char *expansionkey, int Nr)//加密一个区块
{
    
    
	//输入blk原文,直接在上面修改,输出blk密文
	//输入skey:
	//输入Nr = 10轮
	int round;
	//第1轮之前:轮密钥加
	AddRoundKey(blk, expansionkey, 0);
	//第1-9轮:4类操作:字节代换、行移位、列混合、轮密钥加
	for (round = 1; round <= (Nr - 1); round++)
	{
    
    
		SubBytes(blk);		//输入16字节数组,直接在原数组上修改
		ShiftRows(blk);		//输入16字节数组,直接在原数组上修改
		MixColumns(blk);	//输入16字节数组,直接在原数组上修改
		AddRoundKey(blk, expansionkey, round);
	}
	//第10轮:不进行列混合
	SubBytes(blk);
	ShiftRows(blk);
	AddRoundKey(blk, expansionkey, Nr);
}
//AES 解密总函数
void Contrary_AesEncrypt(unsigned char *blk, unsigned char *expansionkey, int Nr)
{
    
    
	int x;
	/* unsigned char *contrary_key=key;
	for(x=0;x<11;x++,key+=16)
	Contrary_MixColumns(key);*/
	AddRoundKey(blk, expansionkey, Nr);
	Contrary_ShiftRows(blk);
	Contrary_SubBytes(blk);
	for (x = (Nr - 1); x >= 1; x--)
	{
    
    
		AddRoundKey(blk, expansionkey, x);
		Contrary_MixColumns(blk);
		Contrary_ShiftRows(blk);
		Contrary_SubBytes(blk);
	}
	AddRoundKey(blk, expansionkey, 0);
}
/*//密钥编排,16字节--->44列32bit密钥生成--> 11组16字节:分别用于11轮 轮密钥加运算
Schedule a secret key for use.
*outkey[] must be 16*15 bytes in size
*Nk==number of 32 bit words in the key,e.g.,4,6,8
*Nr==number of rounds,e.g.,10,12,14
*/
void ScheduleKey(unsigned char *inkey, unsigned char *outkey, int Nk, int Nr)//安排一个保密密钥使用
{
    
    
	//inkey:初始16字节密钥key
	//outkey:11组*16字节扩展密钥expansionkey
	//Nk:4列
	//Nr:10轮round
	unsigned char temp[4], t;
	int x, i;
	/*copy the key*/
	//第0组:[0-3]直接拷贝
	for (i = 0; i<(4 * Nk); i++)
	{
    
    
		outkey[i] = inkey[i];
	}
	//第1-10组:[4-43]
	i = Nk;
	while (i<(4 * (Nr + 1))) //i=4~43 WORD 32bit的首字节地址,每一个4字节
	{
    
    //1次循环生成1个字节扩展密钥,4次循环生成一个WORD
	 //temp:4字节数组:代表一个WORD密钥
	 /*temp=w[i-1]*/
	 //i不是4的倍数的时候
	 //每个temp = 每个outkey32bit = 4字节
		for (x = 0; x<4; x++)
			temp[x] = outkey[(4 * (i - 1)) + x];	//i:32bit的首字节地址
													//i是4的倍数的时候
		if (i%Nk == 0)
		{
    
    
			/*字循环:循环左移1字节 RotWord()*/
			t = temp[0]; temp[0] = temp[1]; temp[1] = temp[2]; temp[2] = temp[3]; temp[3] = t;
			/*字节代换:SubWord()*/
			for (x = 0; x<4; x++)
			{
    
    
				temp[x] = sbox[temp[x]];
			}
			/*轮常量异或:Rcon[j]*/
			temp[0] ^= Rcon[(i / Nk) - 1];
		}
		/*w[i] = w[i-4]^w[i-1]*/
		for (x = 0; x<4; x++)
		{
    
    
			outkey[(4 * i) + x] = outkey[(4 * (i - Nk)) + x] ^ temp[x];
		}
		++i;
	}
}
int main(void) {
    
    
	/*
	pt:原文16字节-->密文
	key:原密钥16字节
	skey:密钥扩展44long
	sbox:s盒
	*/

	unsigned char pt[17], key[17];
	unsigned char expansionkey[15 * 16];
	int i;
	int j;
	printf("输入需要加密字符串: \n");//输入无格式的字符串字符个数不得少于六个!
	scanf("%s", pt);
	printf("输入加密密钥: \n");//输入加密钥匙密匙个数不得低于六个!
	scanf("%s", key);

	/*加密*/
	ScheduleKey(key, expansionkey, 4, 10);	//1、密钥扩展生成
	AesEncrypt(pt, expansionkey, 10);		//2、AES 加密
	printf("加密后的数据:  ");	//输出密码文件

	for (i = 0; i < 16; i++)
		printf("%02x ", pt[i]);
	printf("\n");

	/*解密*/
	Contrary_AesEncrypt(pt, expansionkey, 10);//AES 解密
	printf("解密后的数据: ");//将解密文件输出
	for (i = 0; i < 16; i++)
		printf("%c ", pt[i]);

	system("pause");
	return 0;
}

2.2 逆向

参考
1.了解常用加解密算法并简单逆向识别 链接22
2.AES算法逆向分析报告 链接23
3.逆向中常见的Hash算法和对称加密算法的分析 链接24
4.AES加密算法 链接25
5.讨论:加密算法特征码及其识别 链接26

关于AES的识别,一般有两种方法
1.基于S盒、逆S盒
大多数时候,AES算法往往采用查表法实现,此时的固定常量,尤其是S盒/逆S盒,可以作为一个非常明显的特征(有的时候可能要考虑端序)
如果发现程序中有 S 盒或者动态生成了 S 盒,那么该算法很有可能采用了 AES 加密(标准AES,未魔改S盒和逆S盒,不过为了魔改而魔改,不考虑魔改后的安全性等问题,意义不大;基本上看到S盒/逆S盒,95%就是AES加密算法,如果是混合加密,只能说加密的某个阶段使用的极有可能AES加密)
2.基于特征运算
轮函数包含四部分:字节替换、行位移、列混合、轮密钥加。识别基本AES算法的关键也就是识别该轮函数。
如:AES的行移位特征运算
在这里插入图片描述

其他方法
如果程序调用了一些加密库或者API,根据符号特征判断。

这个例子出自《加密与解密(第4版)》第6章的随书附带文件,AESKeyGenMe.exe
AESKeyGenMe.exe
查表法的核心思想是将字节代换层、ShiftRows层和MixColumn层融合为查找表:每个表的大小是32 bits(4字节)乘以256项,一般称为T盒(T-Box)或T表。加密过程4个表(Te),解密过程4个表(Td),共8个。每一轮操作都通过16次查表产生。虽然一轮就要经历16次查表,但这都简化了伽罗瓦域上的计算操作和矩阵乘法操作,对于计算机程序而言,这是更快的。

1.Detect it easy 搜索签名
在这里插入图片描述
上图中2和3均为AES的S盒与逆S盒,之所以能直接通过hex找出特征,是因为利用查表法,以空间换时间。
这里提一下,韩国商用密码ARIA,即ARIA加密算法,有两个S盒和逆S盒,其中第一个S盒/逆S盒与AES一致,可以从源码层面去对比S盒/逆S盒
上图中 第一列中的AES Rijndael Alogtable,即 Multiplication in GF(2^8) lookup table,用查表法实现列混淆。构造表 Alogtable 和 Logtable,则列混淆变换中所需要的有限域中的乘法可用查表法实现。

摘自维基百科-高级加密标准-加密算法优化 部分:
**使用32或更多比特寻址的系统,可以事先对所有可能的输入创建对应表,利用查表来实现SubBytes,ShiftRows和MixColumns步骤以达到加速的效果。**这么作需要产生4个表,每个表都有256个格子,一个格子记载32位的输出;约占去4KB(4096字节)存储器空间,即每个表占去1KB的存储器空间。如此一来,在每个加密循环中,只需要查16次表,作12次32位的XOR运算,以及AddRoundKey步骤中4次32位XOR运算。若使用的平台存储器空间不足4KB,也可以利用循环交换的方式一次查一个256格32位的表。

切换字节序为BE,出现T表特征,以及rcon
在这里插入图片描述
具体查表法实现AES源码,可以查看openssh中关于AES的源码 rijndael.c,也可以参照OpenSSL、开源加密库等源码
查表法中一般用static const声明常量,一般在PE文件的.text段

2.ida打开,定位关键函数sub_4010A0
在这里插入图片描述
这里使用IDA插件 Findcrypt,下图是使用后结果:
在这里插入图片描述
其中RijnDael_AES_LONG_4082DC和RijnDael_AES_LONG_inv_4083DC为S盒与逆S盒

在IDA插件目录中,找到规则并查看,为yara规则,实际上与Detect it easy搜索签名功能的本质一样。
在这里插入图片描述
可以参照源码对比逆向结果来推断是AES的具体那个过程,进一步明确是AES加密算法

当确定为AES算法之后,就需要去确定AES的加密模式、密钥等信息
此处为key,没找到IV,可以确定为ECB模式

在这里插入图片描述
关于AES加密的模式和填充,详见

如果是调用加密库或者系统自带API(如:Windows提供的CryptoAPI、BCrypt、NCrypt…)可能需要动调追参数/反汇编代码对比源码

3.小结

AES作为常见的对称加密算法之一,属于分组加密算法,也属于对称加密,采用相同密钥对明文进行加解密。在逆向过程中,S盒/逆S盒是其重要的特征,再结合特征运算可以进一步确定为AES加密算法。

猜你喜欢

转载自blog.csdn.net/weixin_51732593/article/details/128516206