Windows10 下安装 PyCrypto 以及迁移到 PyCryptodome
在 Windows 10 上安装 PyCtypto
PyCrypto 是一个 Python 加密库,核心使用 C 实现,因此在安装的过程中需要编译。
最简单的按照方法莫过于寻找编译好的 exe 版本进行安装。但由于这个库已经 3 年多没有维护了,目前能找到的编译好的版本基本上都针对较老的 Python 版本,例如 Python 3.3/3.5 等等,这些 exe 版本都无法在我需要的环境中安装成功。
我的环境:
- Windows 10 x64
- Python 3.6.2
要成功安装,首先必须安装 Microsoft 的编译工具。如果已经安装了 Visual Studio ,则可以跳过这一步。若还没有,而且后续也没有使用 VS 的需求,可以下载独立的编译工具 Visual C++ 2015 Build Tools 。
使用 pip 安装:
1pip install pycrypto
在安装过程中会出现编译失败。这是由于新的 python 源码 include\pyport.h
不再包含 #include < stdint.h >
,导致 intmax_t
未定义。
我们需要在编译环境中设置 CL 参数才能成功编译。
执行下面命令的时候需要注意两点:
- 如果使用的是独立版本的 Visual C++ Build Tool,文件夹可能会不同,请自行修改。
- 需要在同一个 Shell 会话中执行下面所有命令。这意味着如果你使用了 Python 虚拟环境,需要先进入虚拟环境,再执行
vcvarsall
以及set CL...
。
1C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>vcvarsall
2set CL=-FI"%VCINSTALLDIR%\INCLUDE\stdint.h"
3pip install pycrypto
4C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC>pip install pycrypto
5Collecting pycrypto
6 Using cached pycrypto-2.6.1.tar.gz
7Installing collected packages: pycrypto
8 Running setup.py install for pycrypto ... done
9Successfully installed pycrypto-2.6.1
迁移到 PyCryptodome
上面已经提到, PyCrypto 已经三年多没更新了 。因此有人在 PyCrypto 的 Issues 列表中 号召 PyCrypto 的使用者 迁移到 PyCryptodome 。我已经完成了迁移,下面记录一下迁移过程。
下面这段代码是使用 PyCrypto 库进行 AES 对称加密的封装,其中 key
代表密钥, plain_text
和 cipher_text
分别代表明文和密文。
1import binascii
2from Crypto.Cipher import AES
3
4def _encrypt(key, plain_text):
5 mod = len(plain_text) % 16
6 if mod > 0:
7 # 补齐16的倍数
8 zero = '\0' * (16 - mod)
9 plain_text += zero
10 IV = 16 * ''
11 aes = AES.new(key, AES.MODE_ECB, IV)
12 cipher_text = binascii.hexlify(aes.encrypt(plain_text)).decode()
13 return cipher_text
14
15def _decrypt(key, cipher_text):
16 IV = 16 * ''
17 aes = AES.new(key, AES.MODE_ECB, IV)
18 plain_text = aes.decrypt(binascii.unhexlify(cipher_text)).decode().rstrip('\0')
19 return plain_text
PyCryptodome 使用相同的包名和模块名,因此需要先删除 PyCrypto,然后安装 PyCryptodome :
1pip uninstall pycrypto
2pip install pycryptodome
原来的代码如果一字不改,在调用 _encrypt
进行加密的时候会报错:
1TypeError: IV is not meaningful for the ECB mode
这是因为 IV 仅应用于 MODE_CBC
, MODE_CFB
, MODE_OFB
和 MODE_OPENPGP
, AES 文档 中有说明。
PyCryptodome 的作者在 Compatibility with PyCrypto 中也提到:
Symmetric ciphers do not have ECB as default mode anymore. ECB is not semantically secure and it exposes correlation across blocks. An expression like AES.new(key) will now fail. If ECB is the desired mode, one has to explicitly use AES.new(key, AES.MODE_ECB).
关于 ECB 模式不是语义安全的说法,这里有更详细介绍: Why shouldn't I use ECB encryption? 。
不过我们就不深究安全性的问题了。去掉 IV:
1def _encrypt(key, plain_text):
2 aes = AES.new(key, AES.MODE_ECB)
3 cipher_text = binascii.hexlify(aes.encrypt(plain_text)).decode()
4 return cipher_text
出现新的错误:
TypeError: Only byte strings can be passed to C code
这是因为加密时提供的字符串必须使用 byte
格式而不能使用 str
格式。是所有 str.encode()
进行转换即可。
1def _encrypt(key, plain_text):
2 aes = AES.new(key.encode(), AES.MODE_ECB)
3 cipher_text = binascii.hexlify(aes.encrypt(plain_text.encode())).decode()
4 return cipher_text
迁移成功的完整代码如下所示:
1def _encrypt(key, plain_text):
2 mod = len(plain_text) % 16
3 if mod > 0:
4 # 补齐16的倍数
5 zero = '\0' * (16 - mod)
6 plain_text += zero
7 aes = AES.new(key.encode(), AES.MODE_ECB)
8 cipher_text = binascii.hexlify(aes.encrypt(plain_text.encode())).decode()
9 return cipher_text
10
11
12def _decrypt(key, cipher_text):
13 aes = AES.new(key.encode(), AES.MODE_ECB)
14 plain_text = aes.decrypt(binascii.unhexlify(cipher_text)).decode().rstrip('\0')
15 return plain_text
去掉解密后字符串结尾的控制字符
有时候(尤其是解密使用其它语言加密的密文时),解密后的字符串末尾为了补全长度,带有用于填充的控制字符。这些控制字符一般是 \0
,使用 rstrip('\0')
就可以去掉。
但当我解密使用 Node.js 加密的密文时,结果是这样的:
1HelloWorld!你好世界!\x06\x06\x06\x06\x06\x06
因为字符串的长度必须是 16 的整数倍,Node.js 在字符串最后填充了具体的位数。如果是需要补 6 字符,它会填充 6 个 \0x06
。如果需要补 3 个字符,则填充 3 个 \0x03
。由于缺少的字符数不能确定,使用 rstrip('\0')
就不行了。
可以使用正则来处理:
1_zero_re = re.compile(r'[\x00-\x1F]+$')
2plain_text = _zero_re.sub('', plain_text)
其它的 Python 密码学库
cryptography includes both high level recipes and low level interfaces to common cryptographic algorithms such as symmetric ciphers, message digests, and key derivation functions.
PyNaCl: Python binding to the libsodium library
PyNaCl is a Python binding to libsodium, which is a fork of the Networking and Cryptography library. These libraries have a stated goal of improving usability, security and speed. It supports Python 2.7 and 3.3+ as well as PyPy 2.6+.
参考文档
- How to install pycrypto on Windows
- Compatibility with PyCrypto
- Why shouldn't I use ECB encryption?
- READ THIS IF YOU WANT HELP
- Best Python package to use for cryptography?
- 文章ID:2655
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/migrate-pycryto-to-pycryptodome/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。