AES是一种对称加密,所谓对称加密就是加密与解密使用的秘钥是一个。
为了解决一个问题临时查到一个代码套用进去,或许可以迅速解决问题,但是遇到新的问题还需要再次查询,这种我认为还是比较浪费时间的。
环境安装 1 2 3 pip uninstall crypto pip uninstall pycryptodome pip install pycryptodome
前面两个卸载命令是为了防止一些安装环境问题,具体请看文章
加密模式 AES 加密最常用的模式就是 ECB模式 和 CBC 模式,当然还有很多其它模式,他们都属于AES加密。ECB模式和CBC 模式俩者区别就是 ECB 不需要 iv偏移量,而CBC需要。
AES加密使用参数 以下参数都是在python中使用的。
参数 作用及数据类型 秘钥 加密的时候用秘钥,解密的时候需要同样的秘钥才能解出来; 数据类型为bytes 明文 需要加密的参数; 数据类型为bytes 模式 aes 加密常用的有 ECB 和 CBC 模式(我只用了这两个模式,还有其他模式);数据类型为aes类内部的枚举量 iv 偏移量 这个参数在 ECB 模式下不需要,在 CBC 模式下需要;数据类型为bytes
下面简单的一个例子ECB模式加密解密 :
1 2 3 4 5 6 7 8 9 10 from Crypto.Cipher import AESpassword = b'1234567812345678' text = b'abcdefghijklmnhi' aes = AES.new(password,AES.MODE_ECB) en_text = aes.encrypt(text) print ("密文:" ,en_text) den_text = aes.decrypt(en_text) print ("明文:" ,den_text)
输出:
1 2 密文: b'WU\xe0\x0e\xa3\x87\x12\x95\\]O\xd7\xe3\xd4 )' 明文: b'abcdefghijklmnhi'
以上是针对ECB模式的加密解密,从这个例子中可以看出参数中有几个限制。
秘钥必须为16字节或者16字节的倍数的字节型数据。 明文必须为16字节或者16字节的倍数的字节型数据,如果不够16字节需要进行补全,关于补全规则,后面会在补全模式中具体介绍。 通过CBC模式例子:
1 2 3 4 5 6 7 8 9 10 11 from Crypto.Cipher import AESpassword = b'1234567812345678' iv = b'1234567812345678' text = b'abcdefghijklmnhi' aes = AES.new(password,AES.MODE_CBC,iv) en_text = aes.encrypt(text) print ("密文:" ,en_text) aes = AES.new(password,AES.MODE_CBC,iv) den_text = aes.decrypt(en_text) print ("明文:" ,den_text)
输出:
1 2 密文: b'\x93\x8bN!\xe7~>\xb0M\xba\x91\xab74;0' 明文: b'abcdefghijklmnhi'
通过上面CBC模式的例子,可以简单看出CBC模式与ECB模式的区别:AES.new() 解密和加密重新生成了aes对象,加密和解密不能调用同一个aes对象,否则会报错TypeError: decrypt() cannot be called after encrypt()。
总结:
在Python中进行AES加密解密时,所传入的密文、明文、秘钥、iv偏移量、都需要是bytes(字节型)数据。python 在构建aes对象时也只能接受bytes类型数据。
当秘钥,iv偏移量,待加密的明文,字节长度不够16字节或者16字节倍数的时候需要进行补全。
CBC模式需要重新生成AES对象,为了防止这类错误,我写代码无论是什么模式都重新生成AES对象。
编码模式 前面说了,python中的 AES 加密解密,只能接受字节型(bytes)数据。而我们常见的 待加密的明文可能是中文,或者待解密的密文经过base64编码的,这种都需要先进行编码或者解码,然后才能用AES进行加密或解密。反正无论是什么情况,在python使用AES进行加密或者解密时,都需要先转换成bytes型数据。
我们以ECB模式针对中文明文进行加密解密举例:
1 2 3 4 5 6 7 8 9 10 11 from Crypto.Cipher import AESpassword = b'1234567812345678' text = "好好学习天天向上" .encode('gbk' ) aes = AES.new(password,AES.MODE_ECB) print (len (text))en_text = aes.encrypt(text) print ("密文:" ,en_text) den_text = aes.decrypt(en_text) print ("明文:" ,den_text.decode("gbk" ))
输出:
1 2 3 16 密文: b'=\xdd8k\x86\xed\xec\x17\x1f\xf7\xb2\x84~\x02\xc6C' 明文: 好好学习天天向上
对于中文明文,我们可以使用encode()函数进行编码,将字符串转换成bytes类型数据,而这里我选择gbk编码,是为了正好能满足16字节,utf8编码是一个中文字符对应3个字节。这里为了举例所以才选择使用gbk编码。
在解密后,同样是需要decode()函数进行解码的,将字节型数据转换回中文字符(字符串类型)。
现在我们来看另外一种情况,密文是经过base64编码的(这种也是非常常见的,很多网站也是这样使用的),我们用 http://tool.chacuo.net/cryptaes/ 这个网站举例子:
模式:ECB
密码: 1234567812345678
字符集:gbk编码
输出: base64
我们来写一个python 进行aes解密:
1 2 3 4 5 6 7 8 9 10 from Crypto.Cipher import AESimport base64password = b'1234567812345678' aes = AES.new(password,AES.MODE_ECB) en_text = b"Pd04a4bt7Bcf97KEfgLGQw==" en_text = base64.decodebytes(en_text) den_text = aes.decrypt(en_text) print ("明文:" ,den_text.decode("gbk" ))
输出:
这里的 b"Pd04a4bt7Bcf97KEfgLGQw=="
是一个bytes数据, 如果你传递的是一个字符串,你可以直接使用 encode()
函数 将其转换为 bytes类型数据。
1 2 3 4 5 6 7 8 9 10 from Crypto.Cipher import AESimport base64password = b'1234567812345678' aes = AES.new(password,AES.MODE_ECB) en_text = "Pd04a4bt7Bcf97KEfgLGQw==" .encode() en_text = base64.decodebytes(en_text) den_text = aes.decrypt(en_text) print ("明文:" ,den_text.decode("gbk" ))
因为无论是 utf8
和 gbk
编码,针对英文字符编码都是一个字符对应一个字节,所以这里**encode()**函数主要作用就是转换成bytes数据,然后使用base64进行解码。
hexstr,base64编码解码例子:
1 2 3 4 5 6 7 8 9 10 11 import base64import binasciidata = "hello" .encode() data = base64.b64encode(data) print ("base64编码:" ,data)data = base64.b64decode(data) print ("base64解码:" ,data)data = binascii.b2a_hex(data) print ("hexstr编码:" ,data)data = binascii.a2b_hex(data) print ("hexstr解码:" ,data)
输出:
1 2 3 4 base64编码: b'aGVsbG8=' base64解码: b'hello' hexstr编码: b'68656c6c6f' hexstr解码: b'hello'
这里要说明一下,有一些AES加密,所用的秘钥,或者IV向量是通过 base64编码或者 hexstr编码后的。针对这种,首先要进行的就是进行解码,都转换回 bytes数据,再次强调,python实现 AES加密解密传递的参数都是 bytes(字节型) 数据。
另外,我记得之前的 pycryptodome库,传递IV向量时,和明文时可以直接使用字符串类型数据,不过现在新的版本都必须为 字节型数据了,可能是为了统一好记。
填充模式 前面我使用秘钥,还有明文,包括IV向量,都是固定16字节,也就是数据块对齐了。而填充模式就是为了解决数据块不对齐的问题,使用什么字符进行填充就对应着不同的填充模式
AES补全模式常见有以下几种:
模式 意义 ZeroPadding 用b’\x00’进行填充,这里的0可不是字符串0,而是字节型数据的b’\x00’ PKCS7Padding 当需要N个数据才能对齐时,填充字节型数据为N、并且填充N个 PKCS5Padding 与PKCS7Padding相同,在AES加密解密填充方面我没感到什么区别 no padding 当为16字节数据时候,可以不进行填充,而不够16字节数据时同ZeroPadding一样
这里有一个细节问题,我发现很多文章说的也是不对的。
ZeroPadding填充模式的意义:很多文章解释是当为16字节倍数时就不填充,然后当不够16字节倍数时再用字节数据0填充,这个解释是不对的,这解释应该是no padding的,而ZeroPadding是不管数据是否对其,都进行填充,直到填充到下一次对齐为止,也就是说即使你够了16字节数据,它会继续填充16字节的0,然后一共数据就是32字节。
这里可能会有一个疑问,为什么是16字节 ,其实这个是 数据块的大小,网站上也有对应设置,网站上对应的叫128位,也就是16字节对齐,当然也有192位(24字节),256位(32字节)。
本文在这个解释之后,后面就说数据块对齐问题了,而不会再说16字节倍数了。
除了no padding 填充模式,剩下的填充模式都会填充到下一次数据块对齐为止,而不会出现不填充的问题。
PKCS7Padding和 PKCS5Padding需要填充字节对应表:
明文长度值(mod 16) 添加的填充字节数 每个填充字节的值 0 16 0x10 1 15 0x0F 2 14 0x0E 3 13 0x0D 4 12 0x0C 5 11 0x0B 6 10 0x0A 7 9 0x09 8 8 0x08 9 7 0x07 10 6 0x06 11 5 0x05 12 4 0x04 13 3 0x03 14 2 0x02 15 1 0x01
这里可以看到,当明文长度值已经对齐时(mod 16 = 0),还是需要进行填充,并且填充16个字节值为0x10。ZeroPadding填充逻辑也是类似的,只不过填充的字节值都为0x00,在python表示成 b’\x00’。
填充完毕后,就可以使用 AES进行加密解密了,当然解密后,也需要剔除填充的数据,无奈Python这些步骤需要自己实现(如果有这样的库还请评论指出)。
python的完整实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 from Crypto.Cipher import AESimport base64import binasciiclass MData (): def __init__ (self, data = b"" ,characterSet='utf-8' ): self.data = data self.characterSet = characterSet def saveData (self,FileName ): with open (FileName,'wb' ) as f: f.write(self.data) def fromString (self,data ): self.data = data.encode(self.characterSet) return self.data def fromBase64 (self,data ): self.data = base64.b64decode(data.encode(self.characterSet)) return self.data def fromHexStr (self,data ): self.data = binascii.a2b_hex(data) return self.data def toString (self ): return self.data.decode(self.characterSet) def toBase64 (self ): return base64.b64encode(self.data).decode() def toHexStr (self ): return binascii.b2a_hex(self.data).decode() def toBytes (self ): return self.data def __str__ (self ): try : return self.toString() except Exception: return self.toBase64() class AEScryptor (): def __init__ (self,key,mode,iv = '' ,paddingMode= "NoPadding" ,characterSet ="utf-8" ): ''' 构建一个AES对象 key: 秘钥,字节型数据 mode: 使用模式,只提供两种,AES.MODE_CBC, AES.MODE_ECB iv: iv偏移量,字节型数据 paddingMode: 填充模式,默认为NoPadding, 可选NoPadding,ZeroPadding,PKCS5Padding,PKCS7Padding characterSet: 字符集编码 ''' self.key = key self.mode = mode self.iv = iv self.characterSet = characterSet self.paddingMode = paddingMode self.data = "" def __ZeroPadding (self,data ): data += b'\x00' while len (data) % 16 != 0 : data += b'\x00' return data def __StripZeroPadding (self,data ): data = data[:-1 ] while len (data) % 16 != 0 : data = data.rstrip(b'\x00' ) if data[-1 ] != b"\x00" : break return data def __PKCS5_7Padding (self,data ): needSize = 16 -len (data) % 16 if needSize == 0 : needSize = 16 return data + needSize.to_bytes(1 ,'little' )*needSize def __StripPKCS5_7Padding (self,data ): paddingSize = data[-1 ] return data.rstrip(paddingSize.to_bytes(1 ,'little' )) def __paddingData (self,data ): if self.paddingMode == "NoPadding" : if len (data) % 16 == 0 : return data else : return self.__ZeroPadding(data) elif self.paddingMode == "ZeroPadding" : return self.__ZeroPadding(data) elif self.paddingMode == "PKCS5Padding" or self.paddingMode == "PKCS7Padding" : return self.__PKCS5_7Padding(data) else : print ("不支持Padding" ) def __stripPaddingData (self,data ): if self.paddingMode == "NoPadding" : return self.__StripZeroPadding(data) elif self.paddingMode == "ZeroPadding" : return self.__StripZeroPadding(data) elif self.paddingMode == "PKCS5Padding" or self.paddingMode == "PKCS7Padding" : return self.__StripPKCS5_7Padding(data) else : print ("不支持Padding" ) def setCharacterSet (self,characterSet ): ''' 设置字符集编码 characterSet: 字符集编码 ''' self.characterSet = characterSet def setPaddingMode (self,mode ): ''' 设置填充模式 mode: 可选NoPadding,ZeroPadding,PKCS5Padding,PKCS7Padding ''' self.paddingMode = mode def decryptFromBase64 (self,entext ): ''' 从base64编码字符串编码进行AES解密 entext: 数据类型str ''' mData = MData(characterSet=self.characterSet) self.data = mData.fromBase64(entext) return self.__decrypt() def decryptFromHexStr (self,entext ): ''' 从hexstr编码字符串编码进行AES解密 entext: 数据类型str ''' mData = MData(characterSet=self.characterSet) self.data = mData.fromHexStr(entext) return self.__decrypt() def decryptFromString (self,entext ): ''' 从字符串进行AES解密 entext: 数据类型str ''' mData = MData(characterSet=self.characterSet) self.data = mData.fromString(entext) return self.__decrypt() def decryptFromBytes (self,entext ): ''' 从二进制进行AES解密 entext: 数据类型bytes ''' self.data = entext return self.__decrypt() def encryptFromString (self,data ): ''' 对字符串进行AES加密 data: 待加密字符串,数据类型为str ''' self.data = data.encode(self.characterSet) return self.__encrypt() def __encrypt (self ): if self.mode == AES.MODE_CBC: aes = AES.new(self.key,self.mode,self.iv) elif self.mode == AES.MODE_ECB: aes = AES.new(self.key,self.mode) else : print ("不支持这种模式" ) return data = self.__paddingData(self.data) enData = aes.encrypt(data) return MData(enData) def __decrypt (self ): if self.mode == AES.MODE_CBC: aes = AES.new(self.key,self.mode,self.iv) elif self.mode == AES.MODE_ECB: aes = AES.new(self.key,self.mode) else : print ("不支持这种模式" ) return data = aes.decrypt(self.data) mData = MData(self.__stripPaddingData(data),characterSet=self.characterSet) return mData if __name__ == '__main__' : key = b"1234567812345678" iv = b"0000000000000000" aes = AEScryptor(key,AES.MODE_CBC,iv,paddingMode= "ZeroPadding" ,characterSet='utf-8' ) data = "好好学习" rData = aes.encryptFromString(data) print ("密文:" ,rData.toBase64()) rData = aes.decryptFromBase64(rData.toBase64()) print ("明文:" ,rData)
对其进行了封装,加密和解密返回的数据类型可以使用toBase64()
,toHexStr()
进行编码。另外我没有对key和iv进行补全,可以使用MData 类自己实现,更多详细使用可以通过源码中注释了解。
目前以上常用的ECB,CBC这种常用的加密模式会是在漏洞扫描时报安全提醒,并推荐使用GCM模式。
常规示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import base64from Cryptodome.Cipher import AESfrom django.conf import settingsclass AESUtils : def __init__ (self, key: str , nonce: str ): self.key = key.encode(encoding='utf-8' ) self.nonce = nonce.encode(encoding='utf-8' ) def encrypt (self, plaintext: str ): """ 加密函数 @param plaintext: 加密明文 @return: 返回加密密文和tag """ cipher = AES.new(self.key, AES.MODE_GCM, nonce=self.nonce) ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode(encoding='utf-8' )) return base64.b64encode(ciphertext), base64.b64encode(tag) def decrypt (self, ciphertext: str , tag: str ) -> str : """ 解密函数 @param ciphertext: 加密密文 @param tag: tag @return: 加密明文 """ cipher = AES.new(self.key, AES.MODE_GCM, nonce=self.nonce) plaintext = cipher.decrypt_and_verify(base64.b64decode(ciphertext), base64.b64decode(tag)) return plaintext.decode() clientAES = AESUtils(key=settings.AES_HELPER_DEFAULT_KEY, nonce=settings.AES_HELPER_DEFAULT_IV) if __name__ == '__main__' : key = 'oVPSRgw8o1IWmkab' nonce = 'KLyWDN7IivzcIgcd' client = AESUtils(key=key, nonce=nonce) ciphertext, tag = client.encrypt('hello中国' ) res = client.decrypt(ciphertext, tag) print (res)
由于以上方式在与第三方对接时诸多不变,以下为在此基础上的简化升级版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 import base64import typingfrom Cryptodome.Cipher import AESclass AESGCMClient (object ): """ 可参考在线gcm加密对照 - https://try8.cn/tool/cipher/aes """ def __init__ ( self, _key: typing.Union [str , bytes ], _associated: typing.Union [str , bytes ], _nonce: typing.Union [str , bytes ], ): self.key = _key.encode() if isinstance (_key, str ) else _key self.associated = _associated.encode() if isinstance (_associated, str ) else _associated self.nonce = _nonce.encode() if isinstance (_nonce, str ) else _nonce def encrypt (self, content: str ) -> str : """ GCM encrypt :params content: 待加密内容 > ciphertext: 加密后的bytes,可用base64.b64encode(ciphertext).decode('utf-8')解码 > tag: 加密后的bytes,可用base64.b64encode(tag).decode('utf-8')解码 :return : 将密文与tag内容一起返回,后续通过位数切割还原密文与tag,进行解密操作 """ if not content: return '' cipher = AES.new(key=self.key, mode=AES.MODE_GCM, nonce=self.nonce) cipher.update(assoc_data=self.associated) cipher_text, tag = cipher.encrypt_and_digest(plaintext=content.encode('utf-8' )) return base64.b64encode(cipher_text + tag).decode('utf-8' ) def decrypt (self, content: str ) -> str : """ GCM decrypt """ if not content: return '' try : cipher = AES.new(key=self.key, mode=AES.MODE_GCM, nonce=self.nonce) cipher.update(assoc_data=self.associated) en_data = base64.b64decode(content.encode('utf-8' )) cipher_text, received_mac_tag = en_data[:-16 ], en_data[-16 :] plaintext = cipher.decrypt_and_verify(ciphertext=cipher_text, received_mac_tag=received_mac_tag) return plaintext.decode() except Exception as e: return '' if __name__ == '__main__' : key = 'pRVIc4bvJ97bFaEL6s2n211brjgw6c9f' nonce = 'qNvaCUrnxYNouXPxjDh4WY4gooky2HhN' client = AESGCMClient(_key=key, _associated="123456" , _nonce=nonce) ciphertext = client.encrypt('hello中国' ) print (f"ciphertext={ciphertext} " ) res = client.decrypt(ciphertext) print (res)
再次进阶,上述版本key和nonce都是固定的,官方其实建议nonce最好不要固定不变,由此我们可以尝试动态生成nonce,包含在加密数据中,后续对方解密的时候根据位数先取出nonce,再进行解密,这样双方对接的就只有一个key了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 import base64from Cryptodome.Cipher import AESfrom Cryptodome.Random import get_random_bytesclass AESUtils : def __init__ (self, key: str ): self.key = key.encode(encoding='utf-8' ) def encrypt (self, plaintext: str ): """ 加密函数 @param plaintext: 加密明文 @return: 返回加密密文和tag """ _nonce = get_random_bytes(32 ) cipher = AES.new(self.key, AES.MODE_GCM, nonce=_nonce) _ciphertext, _tag = cipher.encrypt_and_digest(plaintext.encode(encoding='utf-8' )) return base64.b64encode(_ciphertext + _tag + _nonce).decode('utf-8' ) def decrypt (self, content: str ) -> str : """ 解密函数 @param content: 加密密文 @return: 加密明文 """ if not content: return '' try : content = base64.b64decode(content.encode()) _ciphertext, _tag, _nonce = content[:-48 ], content[-48 :-32 ], content[-32 :] cipher = AES.new(self.key, AES.MODE_GCM, nonce=_nonce) plaintext = cipher.decrypt_and_verify(_ciphertext, _tag) return plaintext.decode() except Exception as e: print (e) return '' if __name__ == '__main__' : key = 'pRVIc4bvJ97bFaEL6s2n211brjgw6c2x' client = AESUtils(key=key) ciphertext = client.encrypt('26617053184' ) print ('加密结果:' ) print ("ciphertext:" , ciphertext, type (ciphertext)) res = client.decrypt(ciphertext) print ('解密结果:' , res)
golang的完整实现 最近在看golang,想着按照python项目去封装一套脚手架,日常项目中aes也是少不了的,这里就封装了下,代码中都含有注释,所以这里直接上代码:
golang中这几个包都是内置的,不用另外安装哟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 package mainimport ( "bytes" "crypto/aes" "crypto/cipher" "encoding/base64" "errors" "fmt" ) func main () { aes := Aes{ Key: []byte ("Ay4j79B6XlKyOmzVVOsWCh5fUkHNUhav" ), Iv: []byte ("M033mWWXiCvdg2tq" ), } data := []byte ("01234567891234567" ) enData, err := aes.encrypt(data) fmt.Println("enData:" , enData, string (enData)) fmt.Println("err:" , err) deData, err1 := aes.decrypt(string (enData)) fmt.Println("deData:" , deData, string (deData)) fmt.Println("err1:" , err1) } type Aes struct { Key []byte Iv []byte } func (a *Aes) encrypt(data []byte ) ([]byte , error ) { block, err := aes.NewCipher(a.Key) if err != nil { return nil , err } blockSize := block.BlockSize() encryptBytes := a.pkcs7Padding(data, blockSize) crypted := make ([]byte , len (encryptBytes)) mode := cipher.NewCBCEncrypter(block, a.Iv) mode.CryptBlocks(crypted, encryptBytes) encryptData := base64.StdEncoding.EncodeToString(crypted) return []byte (encryptData), nil } func (a *Aes) decrypt(encryData string ) ([]byte , error ) { block, err := aes.NewCipher(a.Key) if err != nil { return nil , err } baseDeData, err := base64.StdEncoding.DecodeString(encryData) if err != nil { return nil , err } decrypted := make ([]byte , len (baseDeData)) mode := cipher.NewCBCDecrypter(block, a.Iv) mode.CryptBlocks(decrypted, baseDeData) decrypted, err = a.pkcs7UnPadding(decrypted) if err != nil { return nil , err } return decrypted, nil } func (a *Aes) pkcs7Padding(data []byte , blockSize int ) []byte { padding := blockSize - len (data)%blockSize padText := bytes.Repeat([]byte {byte (padding)}, padding) return append (data, padText...) } func (a *Aes) 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 }
版权声明:转自CSDN博主「Hello_wshuo」的原创文章。 原文链接:https://blog.csdn.net/chouzhou9701/article/details/122019967