使用Python進行AES加密解密功能實現
PyCrypto是一款非常實用的Python加密模組,最近寫了一個檔案加密指令碼需要用到AES加密,和大家分析一下心得。
下載與安裝:PyCrypto專案已經於2015年7月停止了,下面是官方的下載地址。 https://www.dlitz.net/software/pycrypto/
如果是linux系統,PyCrypto的安裝非常簡單,解壓直接安裝即可:
python setup.py build
python setup.py install
但是在windows下就非常麻煩了,網上常見的方法都是需要MinGW或者VisualStudio。不過你也可以在下面的連線中獲取windows適用的pycrypto安裝包。
http://www.voidspace.org.uk/python/modules.shtml#pycrypto
OK,接下來是正題。
首先介紹一下專案背景,我的電腦裡有一個檔案,名叫PW.txt,這個檔案裡儲存的都是我在各類網站論壇的賬號密碼,雖然方便,但是!這種明文儲存的方式讓我很沒有安全感。恰逢最近在學Python,於是就寫了一個用AES方式對檔案加密的小指令碼。這裡先和大家大家分享一下使用PyCrypto實現對字串的加密與解密。
1.預處理
AES有三種金鑰長度16(*AES-128*), 24 (*AES-192*), 和 32 (*AES-256*),在對字元進行加密時,密碼和明文長度必須為16,24,或32。
因此要對密碼和明文進行預處理,確保密碼長度為16,24或32,明文長度為16,24或32的整數倍,這裡以16(*AES-128*)為例,程式碼如下:
# 補全字元
def align(str, isKey=False):
# 如果接受的字串是密碼,需要確保其長度為16
if isKey:
if len(str) > 16:
return str[0:16]
else:
return align(str)
# 如果接受的字串是明文或長度不足的密碼,則確保其長度為16的整數倍
else:
zerocount = 16-len(str) % 16
for i in range(0, zerocount):
str = str + '\0'
return str
2.加密
要呼叫PyCrypto的AES加密模組,首先匯入AES的包,另外為了確保編碼的統一,我選擇將密文儲存為16進位制,因此還需要從binascii中匯入b2a_hex和a2b_hex。
from Crypto.Cipher import AES
from binascii import b2a_hex
from binascii import a2b_hex
這裡加密函式的流程是:預處理密碼和明文->初始化AES->加密->轉碼->輸出結果,程式碼如下:
# ECB模式加密
def encrypt_ECB(str, key):
# 補全字串
str = align(str)
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_ECB)
# 加密
cipher = AESCipher.encrypt(str)
return b2a_hex(cipher)
這裡使用的是ECB的加密模式,關於加密模式,AES共有五種加密模式(ECB,CBC,PCBC,CFB,OFB,CTR),感興趣的同學可以自行查閱相關資料。
3.解密
這裡解密的流程是:預處理密碼->初始化AES->轉碼->解密->輸出結果,程式碼如下:
<code class="language-python"># ECB模式解密
def decrypt_ECB(str, key):
# 補全字串
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_ECB)
# 解密
paint = AESCipher.decrypt(a2b_hex(str))
# 去除/0 、
paint = paint.rstrip('\0')
return paint</code>
4.AES加密模式——CBC
上面的示例程式碼使用的是ECB模式進行加密解密,這種模式比較簡單,並且安全性相對較差,關於這一點,wiki上有張圖我覺得十分形象。
(https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Initialization_vector_.28IV.29)
大部分場景下,我們會使用CBC模式來進行AES加密,和ECB相比,CBC引入了初始向量IV(Initialization vector),每一次加密都使用隨機產生的初始向量可以大大提高密文的安全性(這裡的示例程式碼使用固定的IV),程式碼如下。
<code class="language-python"></code>
<code class="language-python"># CFB模式加密
def encrypt_CFB(str, key):
# 補全字串,雖然明文長度沒有限制,但是密碼仍然需要16位
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_CFB, '1234567890123456')
# 加密
cipher = AESCipher.encrypt(str)
return b2a_hex(cipher)
# CFB模式解密
def decrypt_CFB(str, key):
# 補全字串
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_CFB, '1234567890123456')
# 解密
paint = AESCipher.decrypt(a2b_hex(str))
# 去除/0
paint = paint.rstrip('\0')
return paint</code>
5. AES加密模式——CFB
除了CBC模式,這裡再介紹一種加密模式——CFB模式,這個模式下明文長度可以不為16的整數倍,程式碼如下:
# CFB模式加密
def encrypt_CFB(str, key):
# 補全字串,雖然明文長度沒有限制,但是密碼仍然需要16位
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_CFB, '1234567890123456')
# 加密
cipher = AESCipher.encrypt(str)
return b2a_hex(cipher)
# CFB模式解密
def decrypt_CFB(str, key):
# 補全字串
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_CFB, '1234567890123456')
# 解密
paint = AESCipher.decrypt(a2b_hex(str))
# 去除/0
paint = paint.rstrip('\0')
return paint6.
完整的例項程式碼如下
<code class="language-python">
# -*- coding: UTF-8 -*-
from Crypto.Cipher import AES
from binascii import b2a_hex
from binascii import a2b_hex
# 補全字元
def align(str, isKey=False):
# 如果是密碼,需要確保其長度為16
if isKey:
if len(str) > 16:
return str[0:16]
else:
return align(str)
# 如果是被加密字串或長度不足的密碼,則確保其長度為16的整數倍
else:
zerocount = 16-len(str) % 16
for i in range(0, zerocount):
str = str + '\0'
return str
# ECB模式加密
def encrypt_ECB(str, key):
# 補全字串
str = align(str)
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_ECB)
# 加密
cipher = AESCipher.encrypt(str)
return b2a_hex(cipher)
# ECB模式解密
def decrypt_ECB(str, key):
# 補全字串
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_ECB)
# 解密
paint = AESCipher.decrypt(a2b_hex(str))
# 去除/0
paint = paint.rstrip('\0')
return paint
# CBC模式加密
def encrypt_CBC(str, key):
# 補全字串
str = align(str)
key = align(key, True)
# 初始化AES,引入初始向量
AESCipher = AES.new(key, AES.MODE_CBC, '1234567890123456')
# 加密
cipher = AESCipher.encrypt(str)
return b2a_hex(cipher)
# CBC模式解密
def decrypt_CBC(str, key):
# 補全字串
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_CBC, '1234567890123456')
# 解密
paint = AESCipher.decrypt(a2b_hex(str))
# 去除/0
paint = paint.rstrip('\0')
return paint
# CFB模式加密
def encrypt_CFB(str, key):
# 補全字串,雖然明文長度沒有限制,但是密碼仍然需要16位
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_CFB, '1234567890123456')
# 加密
cipher = AESCipher.encrypt(str)
return b2a_hex(cipher)
# CFB模式解密
def decrypt_CFB(str, key):
# 補全字串
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_CFB, '1234567890123456')
# 解密
paint = AESCipher.decrypt(a2b_hex(str))
# 去除/0
paint = paint.rstrip('\0')
return paint
# OFB模式加密
def encrypt_OFB(str, key):
# 補全字串
str = align(str)
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_OFB, '1234567890123456')
# 加密
cipher = AESCipher.encrypt(str)
return b2a_hex(cipher)
# OFB模式解密
def decrypt_OFB(str, key):
# 補全字串
key = align(key, True)
# 初始化AES
AESCipher = AES.new(key, AES.MODE_OFB, '1234567890123456')
# 解密
paint = AESCipher.decrypt(a2b_hex(str))
# 去除/0
paint = paint.rstrip('\0')
return paint
# 先設定一段明文和密碼
Text = 'Suprise!!****** *****r!'
key = 'mor'
# ECB模式加密
ciphertext = encrypt_ECB(Text, key)
print ("ECB模式密文:" + ciphertext)
# ECB模式解密
plaintext = decrypt_ECB(ciphertext, key)
print ("ECB模式明文:" + plaintext)
# CBC模式加密
ciphertext = encrypt_CBC(Text, key)
print ("CBC模式密文:" + ciphertext)
# CBC模式解密
plaintext = decrypt_CBC(ciphertext, key)
print ("CBC模式明文:" + plaintext)
# CFB模式加密
ciphertext = encrypt_CFB(Text, key)
print ("CFB模式密文:" + ciphertext)
# CFB模式解密
plaintext = decrypt_CFB(ciphertext, key)
print ("CFB模式明文:" + plaintext)
# OFB模式加密
ciphertext = encrypt_OFB(Text, key)
print ("OFB模式密文:" + ciphertext)
# OFB模式解密
plaintext = decrypt_OFB(ciphertext, key)
print ("OFB模式明文:" + plaintext)</code>