密码学:AES算法C语言完整简单实现
Posted 正义的伙伴-L
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了密码学:AES算法C语言完整简单实现相关的知识,希望对你有一定的参考价值。
文章目录
前言
最近密码学学到了分组密码体制。本文主要分享了AES算法的基础知识和C语言的简单实现。
一、AES简介
AES是分组密码,安全性良好。
与3DES比较:比3DES快、至少和3DES一样安全。
AES分组长度:128 bit
密钥长度:128/192/256 bit
对应的加密轮数:10/12/14 轮
【本文是以分组长度128bit,密钥长度128bit 为例】
二、加解密过程
1.常规版
AES加解密流程图:
以密钥长度128bit(10轮变换)为例
文字描述如下:
准备:明文plaintext [16]
初始密钥和 10 个扩展密钥 函数:密钥扩展函数 KeyExpansion()
一:初始变换。
轮密钥加 函数:AddRoundKey()
二:9轮循环
1.S盒字节代换 函数:SubBytes() InvSubBytes()
2.行移位 函数:ShiftRows() InvShiftRows()
3.列混合 函数: MixColumns() InvMixColumns()
字节乘法 函数:Mul()
4.轮密钥加 函数:AddRoundKey()
三:最终轮
1.S盒字节代换 函数:SubBytes() InvSubBytes()
2.行移位 函数:ShiftRows() InvShiftRows()
3.轮密钥加 函数:AddRoundKey()
AES解密算法顺序 从下往上,当然使用密钥顺序也是反的 。
2.优化版
AES加解密优化版流程图:
两个流程图找找不同?
- 优化版AES算法加解密是对称结构。
- 优化版AES算法每轮使用的密钥有所改变:MRK= InvMixColumns(ERK)。
原因:
- ByteSub( )与ShiftRows( )运算次序可交换
- MixColumns( )、 InvMixColumns( )关于异或有分配律
结论:MRK= InvMixColumns(ERK)。对于具体运算,感兴趣的朋友可以找找资料。
三、 各函数简要介绍
补充:
- bit、字、字节
1字=4字节=32bit
1个字母占一个字节,8bit - AES 分组用字节为单位的正方形矩阵描述,16字节的明文、密文和轮子密钥都以一个4x4的矩阵表示。该矩阵一列表示一字,4个字符。
例:
在这之后,就将对应的字符转化成16进制数(状态矩阵)。 - 异或的知识:相同为0,不同为1
1.密钥编排、密钥扩展函数 KeyExpansion()
密钥编排功能:首先初始条件里面应该有一个128bit(16个字母) 的初始密钥Key[16],由于AES有10轮变换,所以需要由初始密钥扩展出10轮密钥,分别进行每一轮变换。
结果:最终得到 扩展秘钥矩阵 k[11][16],其中k[0][16]就是初始密钥Key[16]。
过程:从初始密钥开始,矩阵按列展开,根据1个字分wi
对wi的操作:
- i!=4的倍数 w[i]=w[i-4]⊕ w[i-1]
- i==4的倍数 w[i]= w[i-4]⊕ T[i-1]
其中T变换:
①字循环(左移)
②字节代换(S盒)
③轮常量异或RC
RC的计算:RC:RC[1]=1 ; RC[i]=0x02*RC[i-1]
轮常量Rcon[i]是一个字,详细见下表:
注意:- T[i]变换是对w[i]一列进行操作。
- 矩阵按列展开看下面第4点的描述
- 字节代换需要先看下面第3点对S盒的介绍。
- RC[i]的计算要先看下面第5点对字节乘法的介绍
从上图可以看出当 i=4 的倍数 时,wi对应矩阵k[i]的下标为0,1,2 ,3
。
下面以计算 w4一列中第一个数 为例来具体阐述 i=4的倍数 时的变换,信息如图:
w[4]=w[0]⊕ T[3]
T[3]: 对w[3]变换
①字循环(左移): (n p q m) 对应下标 13,14,15,12
②字节代换(S盒) : (n p q m)
③轮常量异或RC ^( 0x01 0 0 0)
代码编写需与k[11][16]矩阵联系起来:
代码如下:
void KeyExpansion(unsigned char K[16],unsigned char k[11][16])
unsigned char RC[10];
RC[0]=1;
int i;
for(i=1;i<10;i++)
RC[i]=Mul(0x02,RC[i-1]);
for(i=0;i<16;i++)
k[0][i]=K[i];
for(i=1;i<11;i++)
k[i][0]=k[i-1][0]^S[k[i-1][13]]^RC[i-1];
k[i][1]=k[i-1][1]^S[k[i-1][14]];
k[i][2]=k[i-1][2]^S[k[i-1][15]];
k[i][3]=k[i-1][3]^S[k[i-1][12]];
k[i][4]=k[i-1][4]^k[i][0];
k[i][5]=k[i-1][5]^k[i][1];
k[i][6]=k[i-1][6]^k[i][2];
k[i][7]=k[i-1][7]^k[i][3];
k[i][8]=k[i-1][8]^k[i][4];
k[i][9]=k[i-1][9]^k[i][5];
k[i][10]=k[i-1][10]^k[i][6];
k[i][11]=k[i-1][11]^k[i][7];
k[i][12]=k[i-1][12]^k[i][8];
k[i][13]=k[i-1][13]^k[i][9];
k[i][14]=k[i-1][14]^k[i][10];
k[i][15]=k[i-1][15]^k[i][11];
2. 轮密钥加 函数:AddRoundKey()
解释:就是进行异或操作
密钥加运算的逆运算是自己。
void AddRoundKey( unsigned char *a , unsigned char *Key ) // 轮密钥加
for( int i = 0 ; i < 16 ; i ++ )
a[i] ^= Key[i] ;
3. S盒字节代换 函数:SubBytes()
性质:关于字节的非线性变换
利用:代换表(即S盒)。AES有S盒和逆S盒。
解释:在矩阵中,根据当前字符的下标,去找S盒中对应的字符,并进行替换。
S盒参数:
unsigned char S[256] = // S盒
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
;
unsigned char IS[256] = // S盒的逆
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对称加密算法AES原理及实现
AES是作为DES的替代标准出现的,全称Advanced Encryption Standard,即:高级加密标准。AES加密算法,经历了公开的选拔,最终2000年,由比利时密码学家Joan Daemen和Vincent Rijmen设计的Rijndael算法被选中,成为了AES标准。
AES明文分组长度为128位,即16个字节,密钥长度可以为16个字节、24个字节、或32个字节,即128位密钥、192位密钥、或256位密钥。
总体结构
AES中没有使用Feistel网络,其结构称为SPN结构。
和DES相同,AES也由多个轮组成,其中每个轮分为SubBytes、ShiftRows、MixColumns、AddRoundKey 4个步骤,即:字节代替、行移位、列混淆和轮密钥加。根据密钥长度不同,所需轮数也不同,128位、192位、256位密钥,分别需要10轮、12轮和14轮。第1轮之前有一次AddRoundKey,即轮密钥加,可以视为第0轮;之后1至N-1轮,执行SubBytes、ShiftRows、MixColumns、AddRoundKey;最后一轮仅包括:SubBytes、MixColumns、AddRoundKey。
AES总体结构示意图:
分组密码
密码算法可以分为分组密码和流密码两种
分组密码(block cipher)是每次只能处理特定长度的一块数据的一类密码算法,这里的“一块”就称为分组(block)。一个分组的比特数就称为分组长度(block lenght)。
例如 DES和3DES的分组长度都是64比特。AES的分组长度为128比特。
流密码(stream cipher)是对数据流进行连续处理的一类密码算法。流密码中一般以1比特、8比特、或32比特等为单位进行加密和解密。
分组密码处理完一个分组就结束了,因此不需要通过内部状态来记录加密的进度;相对地,流密码是对一串数据进行连续处理,因此需要保持内部状态。
模式
分组密码算法只能加密固定长度的分组,但是我们需要加密的明文长度可能会超过分组密码的分组长度,这时就需要对分组密码算法进行迭代,以便将一段很长的明文全部加密。而迭代的方法就称为分组密码的模式(mode)。
ECB模式:Electronic CodeBook mode(电子密码模式)
CBC模式:Cipher Block Chaining mode(密码分组链接模式)
CFB模式:Cipher FeedBack mode(密文反馈模式)
OFB模式:Output FeedBack mode(输出反馈模式)
CTR模式:CounTeR mode(计数器模式)
ECB模式存在很高的风险,下面举例后面4中模式的使用.
加密的过程中使用了随机流,所以每次加密的密文都不一样
CBC模式
func main() {
key := "1234567890asdfgh"
data := "hollo, world!"
cry := AesCBCEncrypt([]byte(data), []byte(key))
fmt.Println(hex.EncodeToString(cry))
oriData := AESCBCDECriypt(cry, []byte(key))
fmt.Println(string(oriData))
}
// AES也是对称加密 AES 是 DES 的替代品
// AES 密钥长度 只能是 16、24、32 字节
//加密
func AesCBCEncrypt(org []byte, key []byte) []byte {
//校验密钥
block,_ := aes.NewCipher(key)
//按照公钥长度 进行分组补码
org = PKCS7Padding(org, block.BlockSize())
//设置CBC的加密模式
blockMode := cipher.NewCBCEncrypter(block, key)
//加密处理
crypted := make([]byte, len(org))
blockMode.CryptBlocks(crypted, org)
return crypted
}
//解密
func AESCBCDECriypt(criptText []byte, key []byte) []byte {
//校验key的有效性
block,_:=aes.NewCipher(key)
//通过CBC模式解密
blockMode:=cipher.NewCBCDecrypter(block,key)
//实现解密
origData:=make([]byte,len(criptText))
blockMode.CryptBlocks(origData,criptText)
//去码
origData = PKCS7UnPadding(origData)
return origData
}
//PKCS5 分组长度只能为8
//PKCs7 分组长度 1- 255
func PKCS7Padding(org []byte, blockSize int) []byte {
pad := blockSize-len(org)%blockSize
padArr := bytes.Repeat([]byte{byte(pad)}, pad)
return append(org, padArr...)
}
func PKCS7UnPadding(cryptText []byte) []byte {
length := len(cryptText)
lastByte := cryptText[length - 1]
return cryptText[:length-int(lastByte)]
}
输出
ffa22c136fd3e944255d43e255c98ecc
hollo, world!
CFB模式
func main() {
key := []byte("1234567890asdfgh")
data := []byte("abc hello world!")
cry := AESCFBEncrypt(data, key)
fmt.Println(hex.EncodeToString(cry))
//fmt.Println(base64.StdEncoding.EncodeToString(cry))
ori := AESCFBDecrypt(cry, key)
fmt.Println(string(ori))
}
//CFB分组模式加密
func AESCFBEncrypt(oriData []byte, key []byte) []byte {
//校验密钥
block,_ := aes.NewCipher(key)
//拆分iv和密文
cipherText := make([]byte, aes.BlockSize + len(oriData))
iv := cipherText[:aes.BlockSize]
//向iv切片数组初始化 reader(随机内存流)
io.ReadFull(rand.Reader, iv)
//设置加密模式CFB
stream := cipher.NewCFBEncrypter(block,iv)
//加密
stream.XORKeyStream(cipherText[aes.BlockSize:], oriData)
return cipherText
}
//解密
func AESCFBDecrypt(cryptText []byte, key []byte) []byte {
//校验密钥
block,_ := aes.NewCipher(key)
//拆分iv 和密文
iv := cryptText[:aes.BlockSize]
cipherText := cryptText[aes.BlockSize:]
//设置解密模式
stream := cipher.NewCFBDecrypter(block, iv)
var des = make([]byte, len(cipherText))
//解密
stream.XORKeyStream(des, cipherText)
return des
}
输出
92e5c5d7bc54b337a7edbb548ee1a62c8c3c079b71f465a3f0566c0d74b8d513
abc hello world!
OFB模式
func main() {
key := []byte("1234567890asdfgh")
data := []byte("abcd hello world!")
cry := AESOFBEncrypt(data, key)
fmt.Println(hex.EncodeToString(cry))
ori := AESOFBDecrypt(cry, key)
fmt.Println(string(ori))
}
//AES OFB分组加密模式 CTR也是一样
func AESOFBEncrypt(plaintxt []byte, key []byte) []byte {
//校验密钥
block,_ := aes.NewCipher(key)
cipherText := make([]byte, aes.BlockSize + len(plaintxt))
iv := cipherText[:aes.BlockSize]
//向iv切片数组初始化 reader(随机内存流)
io.ReadFull(rand.Reader, iv)
//设置加密模式CFB
stream := cipher.NewOFB(block,iv)
//加密
stream.XORKeyStream(cipherText[aes.BlockSize:], plaintxt)
return cipherText
}
//解密
func AESOFBDecrypt(cryptText []byte, key []byte) []byte {
//校验密钥
block,_ := aes.NewCipher(key)
//拆分iv 和 密文
iv := cryptText[:aes.BlockSize]
plaintxt := make([]byte, len(cryptText)-aes.BlockSize)
//设置解密模式
stream := cipher.NewOFB(block, iv)
//解密
stream.XORKeyStream(plaintxt, cryptText[aes.BlockSize:])
return plaintxt
}
输出
9ee409f8513e3fcba2f1ba726da0b2a5d80251efa073544220b44c8e8fee18fce4
abcd hello world!
CTR模式
func main() {
key := []byte("1234567890asdfgh")
data := []byte("abcd hello world!")
cry := AESCTREncrypt(data, key)
fmt.Println(hex.EncodeToString(cry))
ori := AESCTRDecrypt(cry, key)
fmt.Println(string(ori))
}
//AES CTR分组加密模式
func AESCTREncrypt(plaintxt []byte, key []byte) []byte {
//校验密钥
block,_ := aes.NewCipher(key)
cipherText := make([]byte, aes.BlockSize + len(plaintxt))
iv := cipherText[:aes.BlockSize]
//向iv切片数组初始化 reader(随机内存流)
io.ReadFull(rand.Reader, iv)
//设置加密模式CTR
stream := cipher.NewCTR(block,iv)
//加密
stream.XORKeyStream(cipherText[aes.BlockSize:], plaintxt)
return cipherText
}
//解密
func AESCTRDecrypt(cryptText []byte, key []byte) []byte {
//校验密钥
block,_ := aes.NewCipher(key)
//拆分iv 和 密文
iv := cryptText[:aes.BlockSize]
plaintxt := make([]byte, len(cryptText)-aes.BlockSize)
//设置解密模式
stream := cipher.NewCTR(block, iv)
//解密
stream.XORKeyStream(plaintxt, cryptText[aes.BlockSize:])
return plaintxt
}
输出
c645da2d14896d2b75d41a538a5a3efe3c7721f51f2eb2e92b0c5b8ba141caf534
abcd hello world!
以上是关于密码学:AES算法C语言完整简单实现的主要内容,如果未能解决你的问题,请参考以下文章