aes加密算法简单说明

AES加密有三种规格,如下所示
在这里插入图片描述
分组长度的意思是,每次只能加密指定长度,比如AES-128,则每次只能加密16个字节,分若干次加密。

下面按照128位密钥进行说明

下面说明下aes的加密过程,如下所示:
在这里插入图片描述
加密过程中,需要将明文放到矩阵里面,密码也会放置于一个矩阵,故对于128位aes而言,每次加密16个字符,密钥也需要是16个字符,如果加密明文不是16的倍数,需要补齐,密钥不足16个字符,也需要补齐。

对于加密内容而言,常用的是pkcs7补齐法,举例如下:
1.明文字符串为123456789,补齐为123456789\a\a\a\a\a\a\a
因为明文9个字符,需要补齐为16个,尚缺少7个字符,故需要补7个7,ascii码7对应的字符为\a;这样加密后,再解密时,得到的字符串123456789\a\a\a\a\a\a\a,最后一个字符为\a,ascii码为7,故按照pkcs7规则,从最后面去掉7个字符,得到原字符串123456789。
2.明文字符串为1234567890,补齐为1234567890\x06\x06\x06\x06\x06\x06
因为明文10个字符,需要补齐为16个,尚缺少6个字符,故需要补6个6,ascii码6对应的字符为\x06。
3.明文1234567890123456,刚好为16个字节,此时依然需要补齐,否则按照pkcs7的规则,解密后得到的明文,要取最后一个字符,ascii是几,就要去掉对应长度的字符。故如果不补齐,会发生错误。明文1234567890123456补齐为1234567890123456\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10,
\x10的ascii代表16。
4.明文为12345678901234561,补齐为12345678901234561\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f
5.明文为空字符串时,补齐为\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10

密码补齐说明:
一般的博客中,没密码补齐之说,本人觉得密码存在补齐之说,就拿go的aes而言,如果密钥不足16个字节,则aes.NewCipher创建密钥时直接报错。
就目前网上的主流做法是,密钥不足16个字节,补齐16个字节,后面补的字符的ascii为0.
下面的网址是一个aes在线加密的例子:
https://tool.lmeee.com/jiami/aes
在这里插入图片描述
这个例子里面,密钥是123456789,内部会补7个ascii为0的字符。

我们在写代码,进行密钥补齐的时候,可以自定义,比如针对123456789,可以补齐为1234567897777777,然后用此密钥加密解密,也是可以的。

现在介绍下aes的ecb模式,很明显,对于明文12345678901234561234567890123456,刚好为32字节,再加密的时候,首先会进行补齐操作,后面会多出16个ascii为0的空字符,共48个字符。
加密时,16个字节一次,一共经过了3次,由于前16个字节和中间16个字节都是1234567890123456,故其加密结果应该是一样的,下面的结果中,密文经过base64处理,展示如红色方框,可以看到,在这个长字符串中,并没有发现两段一样的字符串。这不禁让我们对加密结果产生怀疑。
在这里插入图片描述
其实这是因为输出结果为base64的缘故,base64是将原来的6位输出为8位,而16*8不是6的倍数,故前面16个字节无法刚好转换成base64编码,一定要借助后面的字符。故base64上没有两段相同的,是正常的。

如果我们将输出结果格式设置为hex,即16进制,效果如下:
在这里插入图片描述
很明显,我们可以看到两段一样的字符串,即前面的32个字符和中间的32个字符是一样的。

所以ecb加密方式存在着这种问题:明文段一样,对应的加密结果也一样,存在着安全隐患。
下图即ecb加密模式,若明文1,明文3一样,则密文1和密文3也一样。
在这里插入图片描述

为了解决ecb带来的这种问题,引入新的加密模式,后面的明文在加密前,先和前面的加密结果做混合运算,如下所示:
在这里插入图片描述
即,第二个明文在加密前,先和第一段明文的密文做混合;
第三个明文加密前,先和第二段明文的密文做混合。
由于第一段明文前面没有密文,故而需要一个初始向量,这种加密模式叫做cbc。

aes本身加密的细节,此处没说,读者可以参考AES加密算法的详细介绍与实现

下面给出aes加密的一个go实现,里面采取的是128为的cbc模式,补齐方式为pkcs7。
对于init函数,本人将里面的代码注释掉了,如果放开,则加密密码就为1663897791,补齐方式跟在线网址的保持了一致。注释的情况下,密码为1663897791666666,不需要补齐。

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"errors"
	"fmt"
)

//加密过程:
//  1、处理数据,对数据进行填充,采用PKCS7(当密钥长度不够时,缺几位补几个几)的方式。
//  2、对数据进行加密,采用AES加密方法中CBC加密模式
//  3、对得到的加密数据,进行base64加密,得到字符串
// 解密过程相反

//16,24,32位字符串的话,分别对应AES-128,AES-192,AES-256 加密方法
//key不能泄露
//var PwdKey = []byte("ABCDABCDABCDABCD")
var PwdKey = []byte("1663897791666666")
var Iv = []byte("0123456789abcdef")

func init() {
    
    

	/*
		PwdKey[10] = 0
		PwdKey[11] = 0
		PwdKey[12] = 0
		PwdKey[13] = 0
		PwdKey[14] = 0
		PwdKey[15] = 0
	*/
}

//pkcs7Padding 填充
func pkcs7Padding(data []byte, blockSize int) []byte {
    
    
	//判断缺少几位长度。最少1,最多 blockSize
	padding := blockSize - len(data)%blockSize
	//补足位数。把切片[]byte{byte(padding)}复制padding个
	padText := bytes.Repeat([]byte{
    
    byte(padding)}, padding)
	return append(data, padText...)
}

//pkcs7UnPadding 填充的反向操作
func pkcs7UnPadding(data []byte) ([]byte, error) {
    
    
	length := len(data)
	if length == 0 {
    
    
		return nil, errors.New("加密字符串错误!")
	}
	//获取填充的个数
	unPadding := int(data[length-1])
	return data[:(length - unPadding)], nil
}

//AesEncrypt 加密
func AesEncrypt(data []byte, key []byte) ([]byte, error) {
    
    
	//创建加密实例
	block, err := aes.NewCipher(key)
	if err != nil {
    
    
		return nil, err
	}
	//判断加密快的大小
	blockSize := block.BlockSize()
	//填充
	encryptBytes := pkcs7Padding(data, blockSize)
	//初始化加密数据接收切片
	crypted := make([]byte, len(encryptBytes))
	//使用cbc加密模式
	blockMode := cipher.NewCBCEncrypter(block, Iv)
	//执行加密
	blockMode.CryptBlocks(crypted, encryptBytes)
	return crypted, nil
}

//AesDecrypt 解密
func AesDecrypt(data []byte, key []byte) ([]byte, error) {
    
    
	//创建实例
	block, err := aes.NewCipher(key)
	if err != nil {
    
    
		return nil, err
	}
	//获取块的大小
	//blockSize := block.BlockSize()
	//使用cbc
	blockMode := cipher.NewCBCDecrypter(block, Iv)
	//初始化解密数据接收切片
	crypted := make([]byte, len(data))
	//执行解密
	blockMode.CryptBlocks(crypted, data)
	//去除填充
	crypted, err = pkcs7UnPadding(crypted)
	if err != nil {
    
    
		return nil, err
	}
	return crypted, nil
}

//EncryptByAes Aes加密 后 base64 再加
func EncryptByAes(data []byte) (string, error) {
    
    
	res, err := AesEncrypt(data, PwdKey)
	if err != nil {
    
    
		return "", err
	}
	return base64.StdEncoding.EncodeToString(res), nil
}

//DecryptByAes Aes 解密
func DecryptByAes(data string) ([]byte, error) {
    
    
	dataByte, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
    
    
		return nil, err
	}
	return AesDecrypt(dataByte, PwdKey)
}

func main() {
    
    
	encryptStr, err := EncryptByAes([]byte("1234567890"))
	if err == nil {
    
    
		var decryptByte []byte
		decryptByte, err = DecryptByAes(encryptStr)
		fmt.Println(string(decryptByte))
	}
}

猜你喜欢

转载自blog.csdn.net/tusong86/article/details/127093448