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>