python中的AES加密與解密

AES簡介

高級加密標準(英語:Advanced Encryption Standard,縮寫:AES),在密碼學中又稱Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。這個標準用來替代原先的DES,已經被多方分析且廣爲全世界所使用。通過五年的甄選流程,高級加密標準由美國國家標準與技術研究院(NIST)於2001年11月26日發佈於FIPS PUB 197,並在2002年5月26日成爲有效的標準。2006年,高級加密標準已然成爲對稱密鑰加密中最流行的算法之一。java

AES加密有AES-12八、AES-19二、AES-256三種,分別對應三種密鑰長度128bits(16字節)、192bits(24字節)、256bits(32字節)。固然,密鑰越長,安全性越高,加解密花費時間也越長。默認的是AES-128,其安全性徹底夠用。python

AES算法爲最多見的對稱加密算法(微信小程序加密傳輸就是用這個加密算法的)。對稱加密算法也就是加密和解密用相同的密鑰,具體的加密流程以下圖:算法

 

AES只是個基本算法,實現AES有幾種模式,主要有ECB、CBC、CFB和OFB這幾種(其實還有個CTR):小程序

1.ECB模式(電子密碼本模式:Electronic codebook)
ECB是最簡單的塊密碼加密模式,加密前根據加密塊大小(如AES爲128位)分紅若干塊,以後將每塊使用相同的密鑰單獨加密,解密同理。微信小程序

2.CBC模式(密碼分組連接:Cipher-block chaining)
CBC模式對於每一個待加密的密碼塊在加密前會先與前一個密碼塊的密文異或而後再用加密器加密。第一個明文塊與一個叫初始化向量的數據塊異或。安全

3.CFB模式(密文反饋:Cipher feedback)
與ECB和CBC模式只可以加密塊數據不一樣,CFB可以將塊密文(Block Cipher)轉換爲流密文(Stream Cipher)。微信

4.OFB模式(輸出反饋:Output feedback)
OFB是先用塊加密器生成密鑰流(Keystream),而後再將密鑰流與明文流異或獲得密文流,解密是先用塊加密器生成密鑰流,再將密鑰流與密文流異或獲得明文,因爲異或操做的對稱性因此加密和解密的流程是徹底同樣的。函數

代碼實現與解析

1. ECB模式加密

from Crypto.Cipher import AES
import base64
 
class AEScoder():
  def __init__(self):
    self.__encryptKey = "iEpSxImA0vpMUAabsjJWug=="
    self.__key = base64.b64decode(self.__encryptKey)
  # AES加密
  def encrypt(self,data):
    BS = 16
    pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
    cipher = AES.new(self.__key, AES.MODE_ECB)
    encrData = cipher.encrypt(pad(data))
    #encrData = base64.b64encode(encrData)
    return encrData
  # AES解密
  def decrypt(self,encrData):
    #encrData = base64.b64decode(encrData)
    #unpad = lambda s: s[0:-s[len(s)-1]]
    unpad = lambda s: s[0:-s[-1]]
    cipher = AES.new(self.__key, AES.MODE_ECB)
    decrData = unpad(cipher.decrypt(encrData))
    return decrData.decode('utf-8')

簡析1:這裏採用了面向對象的寫法,建立了一個類,同時也偷懶直接把密鑰寫死成了類的屬性。若是有靈活修改密鑰的需求,將密鑰做爲參數傳進去便可。 
簡析2:例子裏用了ECB模式,這是AES加密最簡單也是很經常使用的模式。另一個經常使用模式是CBC,會比ECB模式多一個初始偏移向量iv:cipher = AES.new(self.__key, AES.MODE_CBC, iv)。 
簡析3:pad和unpad分別是填充函數和逆填充函數。由於AES加密對加密文本有長度要求,必須是密鑰字節數的倍數。這裏的encryptKey在通過base64解碼後的長度是16個字節。 spa

 

填充算法拓展
這裏採用的填充算法其實有個專有名詞,叫pkcs7padding。 
簡單解釋就是缺幾位就補幾:填充字符串由一個字節序列組成,每一個字節填充該填充字節序列的長度。 
若是要填充8個字節,那麼填充的字節的值就是0x08;要填充7個字節,那麼填入的值就是0x07;以此類推。 
若是文本長度正好是BlockSize長度的倍數,也會填充一個BlockSize長度的值。這樣的好處是,根據最後一個Byte的填充值便可知道填充字節數。

實際上,java中實現AES加密算法的默認模式是Cipher.getInstance("AES/ECB/PKCS5Padding") 
PKCS#5在填充方面,是PKCS#7的一個子集:PKCS#5只是對於8字節(BlockSize=8)進行填充,填充內容爲0x01-0x08;可是PKCS#7不單單是對8字節填充,其BlockSize範圍是1-255字節。 
然而由於AES並無64位(8字節)的塊, 若是採用PKCS5, 那麼實質上就是採用PKCS7。

 

2. CBC模式

from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex

# 若是text不足16位的倍數就用空格補足爲16位
def add_to_16(text):
    if len(text.encode('utf-8')) % 16:
        add = 16 - (len(text.encode('utf-8')) % 16)
    else:
        add = 0
    text = text + ('\0' * add)
    return text.encode('utf-8')

# 加密函數
def encrypt(text):
    key = '9999999999999999'.encode('utf-8')
    mode = AES.MODE_CBC
    iv = b'qqqqqqqqqqqqqqqq'
    text = add_to_16(text)
    cryptos = AES.new(key, mode, iv)
    cipher_text = cryptos.encrypt(text)
    # 由於AES加密後的字符串不必定是ascii字符集的,輸出保存可能存在問題,因此這裏轉爲16進制字符串
    return b2a_hex(cipher_text)

# 解密後,去掉補足的空格用strip() 去掉
def decrypt(text):
    key = '9999999999999999'.encode('utf-8')
    iv = b'qqqqqqqqqqqqqqqq'
    mode = AES.MODE_CBC
    cryptos = AES.new(key, mode, iv)
    plain_text = cryptos.decrypt(a2b_hex(text))
    return bytes.decode(plain_text).rstrip('\0')

if __name__ == '__main__':
    e = encrypt("hello world")  # 加密
    d = decrypt(e)  # 解密
    print("加密:", e)
    print("解密:", d)
相關文章
相關標籤/搜索