背景:工作需要次和,部分?jǐn)?shù)據(jù)進行了加密傳輸呆盖,對方使用了AES對密碼進行了加密拖云,需要獲取到解密的數(shù)據(jù)。
目標(biāo):通過密鑰成功解密文件应又。
關(guān)鍵詞:AES_ECB宙项,AES_CBC,Java和Python的AES加密問題丁频,
一杉允、遇到的問題
收到密鑰&加密文件邑贴,如下席里。需要通過密鑰對信息進行解密。
已知是AES進行的加密數(shù)據(jù)
key = 'Fcniggersm'
message = 'gYknrv3zMWYXEpRLDL0n8q+6s68DKapAfRpBDhN1XGM='
二拢驾、AES算法簡介
AES算法詳解:高級加密標(biāo)準(zhǔn),它是一種對稱加密算法奖磁,AES只有一個密鑰,這個密鑰既用來加密繁疤,也用于解密咖为。
AES加密方式有五種:ECB, CBC, CTR, CFB, OFB。
從安全性角度推薦CBC加密方法稠腊,本文介紹了CBC,ECB兩種加密方法的python實現(xiàn)躁染。
CBC加密需要一個十六位的key(密鑰)和一個十六位iv(偏移量)
ECB加密不需要iv
接收到的數(shù)據(jù)中只有密鑰而沒有偏移量。故使用ECB處理密文架忌。
三吞彤、準(zhǔn)備工作
python 在 Windows下使用AES時要安裝的是pycryptodome 模塊
python 在 Linux下使用AES時要安裝的是pycrypto模塊
import base64
from Crypto.Cipher import AES
from Crypto import Random
import os
import base64
import json
四、AES-ECB 解密&加密
1、密鑰處理
直接處理密鑰會報錯:‘AES key must be either 16, 24, or 32 bytes long’
因為AES接收的key&vi都必須是有固定長度饰恕。
對Key 進行填充至符合規(guī)格挠羔。
def add_to_16(text):
while len(text) % 16 != 0:
text += '\0'
return (text)
key = 'Fcniggersm'
key = add_to_16(key)
2、密文處理
有可能處理密文時候會報錯:'Error: Incorrect padding'
這是因為密文長度不符合規(guī)格埋嵌,對base64解碼的string補齊等號就可以了
def decode_base64(data):
missing_padding = 4-len(data)%4
if missing_padding:
data += b'='*missing_padding
return (data)
message = 'gYknrv3zMWYXEpRLDL0n8q+6s68DKapAfRpBDhN1XGM='
encrypt_data = message
encrypt_data = decode_base64(encrypt_data)
3破加、解密處理
解密成功獲取到a,再對a進行解碼處理獲取數(shù)據(jù)雹嗦。
cipher = AES.new(key)
result2 = base64.b64decode(encrypt_data)
a = cipher.decrypt(result2)
a = a.decode('utf-8','ignore')
a = a.rstrip('\n')
a = a.rstrip('\t')
a = a.rstrip('\r')
a = a.replace('\x06','')
print('\n','data:',a)
#data: 儒雅隨和范舀,加大力度
4、加密處理
同理可以對字符進行加密處理了罪,執(zhí)行AES加密及解密方法尿背。
# encoding:utf-8
def encrypt(data, password):
bs = AES.block_size
pad = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
cipher = AES.new(password)
data = cipher.encrypt(pad(data))
return (data)
if __name__ == '__main__':
data = 'ni hao'
password = 'aesrsasec' #16,24,32位長的密碼
password = add_to_16(password)
encrypt_data = encrypt(data, password)
encrypt_data = base64.b64encode(encrypt_data)
print ('encrypt_data:', encrypt_data)
五、AES-CBC 解密&加密
CBC & ECB相比多出了一個vi(偏移量)捶惜。
cipher = AES.new(self.__key, AES.MODE_CBC, iv)
python AES 雙向?qū)ΨQ加密解密
# encoding:utf-8
import base64
from Crypto.Cipher import AES
from Crypto import Random
def encrypt(data, password):
bs = AES.block_size
pad = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
iv = Random.new().read(bs)
cipher = AES.new(password, AES.MODE_CBC, iv)
data = cipher.encrypt(pad(data))
data = iv + data
return (data)
def decrypt(data, password):
bs = AES.block_size
if len(data) <= bs:
return (data)
unpad = lambda s : s[0:-ord(s[-1])]
iv = data[:bs]
cipher = AES.new(password, AES.MODE_CBC, iv)
data = unpad(cipher.decrypt(data[bs:]))
return (data)
if __name__ == '__main__':
data = 'd437814d9185a290af20514d9341b710'
password = '78f40f2c57eee727a4be179049cecf89' #16,24,32位長的密碼
encrypt_data = encrypt(data, password)
encrypt_data = base64.b64encode(encrypt_data)
print ('encrypt_data:', encrypt_data)
encrypt_data = base64.b64decode(encrypt_data)
decrypt_data = decrypt(encrypt_data, password)
print ('decrypt_data:', decrypt_data)
六田藐、Java加密&python解密報錯。
實際AES工作中發(fā)現(xiàn)吱七,對方Java加密的文件&自己Python加密的生成的密文不同汽久,導(dǎo)致跨平臺解密錯誤。
可能錯誤的原因:
1踊餐、填充(padding)算法
2景醇、分段大小(segment size)
可參考AES_CFB跨平臺錯誤
7、Linux上解密報錯
在windows上運行無誤吝岭,但是在Lnuix上運行報錯三痰。
①、報錯 new() missing 1 requied positional argument: 'mode'
linux上需要添加窜管,第二參數(shù)散劫,選擇加密模式(本次選擇ECB)
cipher = AES.new(key ,AES.MODE_ECB)
②、報錯 Object type <class 'str'> cannot be passed to C code
linux上傳入的key必須是字節(jié)的形式幕帆,對key 進行處理
key = key.encode('utf-8')
cipher = AES.new(key ,AES.MODE_ECB)
八获搏、小結(jié)
1、處理AES加密時候一定要約定好具體的加密方法失乾。
2常熙、Key,vi 必須是合規(guī)的長度碱茁。
3裸卫、密文也需要做填充處理。
4纽竣、windows ECB自動添加cipher參數(shù)墓贿,且key可以為字符串。linux上需要填寫完整的參數(shù),將key處理成字節(jié)。
九募壕、其他
1调炬、pad和unpad分別是填充函數(shù)和逆填充函數(shù)。因為AES加密對加密文本有長度要求舱馅,必須是密鑰字節(jié)數(shù)的倍數(shù)缰泡。這里的encryptKey在經(jīng)過base64解碼后的長度是16個字節(jié)。
2代嗤、實際上AES加密有AES-128棘钞、AES-192、AES-256三種干毅,分別對應(yīng)三種密鑰長度128bits(16字節(jié))宜猜、192bits(24字節(jié))、256bits(32字節(jié))硝逢。當(dāng)然姨拥,密鑰越長,安全性越高渠鸽,加解密花費時間也越長叫乌。默認(rèn)的是AES-128,其安全性完全夠用徽缚。
填充算法拓展
這里采用的填充算法其實有個專有名詞憨奸,叫pkcs7padding。
簡單解釋就是缺幾位就補幾:填充字符串由一個字節(jié)序列組成凿试,每個字節(jié)填充該填充字節(jié)序列的長度排宰。
如果要填充8個字節(jié),那么填充的字節(jié)的值就是0x08;要填充7個字節(jié),那么填入的值就是0x07那婉;以此類推板甘。
如果文本長度正好是BlockSize長度的倍數(shù),也會填充一個BlockSize長度的值吧恃。這樣的好處是虾啦,根據(jù)最后一個Byte的填充值即可知道填充字節(jié)數(shù)麻诀。
實際上痕寓,java中實現(xiàn)AES加密算法的默認(rèn)模式是Cipher.getInstance("AES/ECB/PKCS5Padding")
PKCS#5在填充方面,是PKCS#7的一個子集:PKCS#5只是對于8字節(jié)(BlockSize=8)進行填充蝇闭,填充內(nèi)容為0x01-0x08呻率;但是PKCS#7不僅僅是對8字節(jié)填充,其BlockSize范圍是1-255字節(jié)呻引。
然而因為AES并沒有64位(8字節(jié))的塊, 如果采用PKCS5, 那么實質(zhì)上就是采用PKCS7礼仗。