• 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))
    	}
    }
    
    
  • 相关阅读:
    【Eclipse】Plug-in Development 插件的安装
    【C++】侯捷:C++面向对象-笔记-02
    Kubernetes 能取代 Docker吗?
    ffmpeg命令分析-yuv封装mp4
    【无标题】
    微信小程序之组件(一)
    Mysql创建视图中文乱码修改docker里的配置
    WebDAV与FTP的区别
    内网IP端口提供外网连接访问?快解析动态域名与内网映射P2P穿透方案
    股票量化交易接口的功能逻辑
  • 原文地址:https://blog.csdn.net/tusong86/article/details/127093448