密码学: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加解密优化版流程图:

两个流程图找找不同?

  1. 优化版AES算法加解密是对称结构
  2. 优化版AES算法每轮使用的密钥有所改变:MRK= InvMixColumns(ERK)。

原因:

  1. ByteSub( )与ShiftRows( )运算次序可交换
  2. MixColumns( )、 InvMixColumns( )关于异或有分配律
    结论:MRK= InvMixColumns(ERK)。对于具体运算,感兴趣的朋友可以找找资料。

三、 各函数简要介绍

补充:

  1. bit、字、字节
    1字=4字节=32bit
    1个字母占一个字节,8bit
  2. AES 分组用字节为单位的正方形矩阵描述,16字节的明文、密文和轮子密钥都以一个4x4的矩阵表示。该矩阵一列表示一字,4个字符。
    例:

    在这之后,就将对应的字符转化成16进制数(状态矩阵)。
  3. 异或的知识:相同为0,不同为1

1.密钥编排、密钥扩展函数 KeyExpansion()

密钥编排功能:首先初始条件里面应该有一个128bit(16个字母) 的初始密钥Key[16],由于AES有10轮变换,所以需要由初始密钥扩展出10轮密钥,分别进行每一轮变换。
结果:最终得到 扩展秘钥矩阵 k[11][16],其中k[0][16]就是初始密钥Key[16]。

过程:从初始密钥开始,矩阵按列展开,根据1个字分wi
对wi的操作:

  1. i!=4的倍数 w[i]=w[i-4]⊕ w[i-1]
  2. 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]是一个字,详细见下表:

    注意:
    1. T[i]变换是对w[i]一列进行操作。
    2. 矩阵按列展开看下面第4点的描述
    3. 字节代换需要先看下面第3点对S盒的介绍。
    4. 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语言完整简单实现的主要内容,如果未能解决你的问题,请参考以下文章

基于C语言的AES加密算法实现

如何实现C语言的DES加密算法实现,请关注

用JAVA实现了AES128加密,求用C语言解密的代码,谢谢各位大神了!

用C语言来实现DES加密算法(很急)两天内

求一个用c语言写的DES加密算法~~

数据结构 图的基本操作要C语言的完整代码!!