AES/CBC/PKCS5Padding到底是什么
为什么JAVA里指定算法时,写的是AES/CBC/PKCS5Padding,每个都是什么含义,又有什么作用。
简单的说:拿到一个原始数据以后,首先需要对数据进行分组,分组以后如果长度不满足分组条件,需要进行补齐,最后形成多个分组,在使用加解密算法,对这多个分组进行加解密。所以这个过程中,AES,CBC,PKCS5Padding缺一不可。
PKCS5Padding和PKCS7Padding
在对数据进行加解密时,通常将数据按照固定的大小(block size)分成多个组,那么随之就产生了一个问题,如果分到最后一组,不够一个block size了,要怎么办?此时就需要进行补齐操作。
补齐规则:The value of each added byte is the number of bytes that are added, i.e. N bytes, each of value N are added.
举例:
36位的UUID,如果按照block size=16字节(即128比特),那么就需要补齐到48位,差12个字节。那么最后填充的12个字节的内容,都是字节表示的0x0c(即12)。
填充规则

PKCS5Padding=PKCS7Padding
AES加解密算法
AES对称加解密算法,是2000年在一堆加解密候选算法中,选出了由比利时科学家发明的Rijndeal的分组密码算法。在AES的规格中,分组长度固定为128比特,秘钥长度只有128,192和256比特三种。
AES算法,也是需要经过很多轮的加密和解密。每一轮分为
SubBytes
输入分组是128比特,即16个字节。16个字节构成一个4X4的正方形。对16个字节中的每个字节,从替换表(可以认为是一个key value的对照表)中找到对应的替换字节

ShiftRows:对行进行移位

MixColumns:对其中一列(4个字节)进行bit运算

AddRoundKey:使用轮秘钥将前三步的输入,进行异或,得到最终结果

这就最终完成了一轮运算
AES加解密需要计算多少轮:10到14轮

分组模式CBC
以上的AES算法,仅仅描述了,对于一个数据块,如果进行加密解密。然后在真实世界中,数据大小不一,这就需要对原始数据按照固定的块大小(block size)进行分组。ECB模式的分组强烈不建议使用,所以简单讨论下CBC模式

根据图示,在CBC模式下,使用AES加解密方式进行分组加解密时,需要用到的两个参数
go编程
// 补齐算法
func PKCSPadding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func PKCSUnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
// 注意:本示例中轮秘钥和初始化向量使用相同的字节数据,真实场景不推荐
func AesEncryptCBC(origData, key []byte) ([]byte, error) {
// AES算法的轮秘钥 key
block, err := aes.NewCipher(key)
if err != nil {
log.Errorf(" encrypt error %v", err)
return nil, err
}
blockSize := block.BlockSize()
origData = PKCSPadding(origData, blockSize)
// key[:blockSize]初始化向量
origData = PKCSPadding(origData, blockSize)
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
crypted := make([]byte, len(origData))
blockMode.CryptBlocks(crypted, origData)
log.Infof(" encrypt base64 string %s", base64.StdEncoding.EncodeToString(crypted))
return crypted, nil
}
func AesDecryptCBC(crypted, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
log.Errorf(" encrypt error %v", err)
return nil, err
}
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
origData := make([]byte, len(crypted))
blockMode.CryptBlocks(origData, crypted)
origData = PKCSUnPadding(origData)
return origData, nil
}
java编程
// 注意:本示例中轮秘钥和初始化向量使用相同的字节数据,真实场景不推荐
public static byte[] decrypt(byte[] data, byte[] key) {
try {
// 轮秘钥
SecretKey secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// 初始化,设置为解密模式,设置初始化向量
cipher.init(Cipher.DECRYPT_MODE, k, new IvParameterSpec(key));
return cipher.doFinal(data);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
结论
任何编程语言,原理最重要。不知道原理,只靠拷贝粘贴是没有用的。