Windows安装Crypto

m2crypto vs pycrypto vs pycryptodome

  • m2crypto:30+的星星,windows的兼容性较差,pip上面默认的版本不是whl包(是源码包),导致基本上无法在windows上pip安装,只能借助于appveyor编译好的版本进行安装
  • pycrypto:很久没有维护了,最后提交已经是2014年,不建议使用
  • pycryptodome:克隆pycrypto的增强版本,1.3k+星星,linux和windows的兼容性较好。建议使用此版本

安装m2crypto

通过appveyor资源安装

pip 默认的包是源码包,windows不可以直接pip安装,只能通过其它办法安装

appveyor为开源项目Windowsg环境自动构建工具,开源项目包含appveyor.yml,则代表支持自动构建,可以直接在appveyor上下载相关编译好的资源

项目CI地址

https://ci.appveyor.com/project/m2crypto/m2crypto

选择和自己匹配的Environment

Environment: 主要是python的版本

Environment: PYTHON=C:\Python37-x64, PYTHON_VERSION=3.7.x, PYTHON_ARCH=64, OPENSSL_PATH=C:\OpenSSL-1-1-Win64, PYWIN32=pywin32-222.win-amd64-py3.7.exe, PYWIN32_RELEASE=b222, APPVEYOR_BUILD_WORKER_IMAGE=Visual Studio 2019

选择artifacts里面的资源

https://ci.appveyor.com/project/m2crypto/m2crypto/build/job/6l7jos7oylu9exmy/artifacts

编译好的资源

https://ci.appveyor.com/api/buildjobs/6l7jos7oylu9exmy/artifacts/dist%2FM2Crypto-0.36.0-cp37-cp37m-win_amd64.whl

安装

# pip install https://ci.appveyor.com/api/buildjobs/6l7jos7oylu9exmy/artifacts/dist%2FM2Crypto-0.36.0-cp37-cp37m-win_amd64.whl
pip install M2Crypto-0.36.0-cp37-cp37m-win_amd64.whl

安装pycryptodome

https://ci.appveyor.com/project/Legrandin/pycryptodome

安装

pip 默认的包是whl,windows可以直接pip安装

pip install pycryptodome

加密实现

参考文档:https://gist.github.com/Frizz925/ac0fb026314807959db5685ac149ed67

下面的加密主要实现了:

  • 将原密码进行md5计算:实现了密码的兼用性
  • iv的随机性
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time    : 2020-09-01 14:26
# @Author  : zaza
# @Email   : 260458726@qq.com
# @File    : crypto.py

import random
import string
from base64 import b64encode, b64decode
from Crypto.Cipher import AES
from Crypto.Util import Padding
from hashlib import md5


class SymmetricEncryption(object):
    """
    >>> # 加、解密测试
    >>> s = SymmetricEncryption(key='zaza')
    >>> # 加密
    >>> data = s.encrypt('zaza')
    >>> # 解密
    >>> s.decrypt(data)
    'zaza'

    >>> # 自定义向量加、解密测试
    >>> s = SymmetricEncryption(key='yaya')
    >>> data = s.encrypt('yaya', '47a79039cc54d688')
    >>> print(data)
    w9Nd2IHjHgCU8QaD4txfYw==.NDdhNzkwMzljYzU0ZDY4OA==
    >>> s.decrypt(data)
    'yaya'

    >>> # key长度正常测试
    >>> s = SymmetricEncryption(key='YWySvcV)k6Y!lcgd2njF*OqJW^uiz&qG', md5_key=False)
    >>> data = s.encrypt('zaza', '47a79039cc54d688')
    >>> print(data)
    Ec7rjeW7uP0J5ps9U+MLTg==.NDdhNzkwMzljYzU0ZDY4OA==
    >>> s.decrypt(data)
    'zaza'

    >>> # key长度异常测试
    >>> s = SymmetricEncryption(key='yaya', md5_key=False)
    >>> s.encrypt('zaza')
    Traceback (most recent call last):
      ...
    ValueError: Incorrect AES key length (4 bytes)
    """
    def __init__(self, key, md5_key=True):
        """
        对称加密,使用aes_256_cbc进行加解密
        :param key: 支持string和bytes类型
        :param md5_key: 将key进行md5计算,成为最终的加密key,经过md5计算保证了key长度为32位
        """
        __key = key if type(key) is bytes else key.encode('utf-8')
        if md5_key:
            self.key = md5(__key).hexdigest().encode('utf-8')
        else:
            self.key = __key
        self.mode = AES.MODE_CBC
        self.bs = AES.block_size

    @staticmethod
    def generate_key(length=32):
        """
        密码生成器
        :param length: 默认为32位长度
        :return: 返回随机字符串
        """
        chars = string.ascii_letters + string.digits + '!@#$%^&*()'
        rnd = random.SystemRandom()
        return ''.join(rnd.choice(chars) for i in range(length))

    def encrypt(self, plaintext, iv=None):
        """
        将明文转换为加密字符串:"{0}.{1}".format(body, iv)
        解密数据为:由点进行数据分隔,body,iv数据为base64的字符串
        :param plaintext: 需要加密的字符串
        :param iv: 初始化向量,长度为16位
        :return: "{0}.{1}".format(body, iv)
        """
        plaintext = plaintext if type(plaintext) is bytes else plaintext.encode('utf-8')
        if iv is None:
            iv = self.generate_key(length=16).encode('utf-8')
        else:
            iv = iv if type(iv) is bytes else iv.encode('utf-8')
        plaintext = Padding.pad(plaintext, self.bs)
        cipher = AES.new(self.key, self.mode, iv)
        # 将body,iv转换成base64的字符串类型
        body = b64encode(cipher.encrypt(plaintext)).decode('utf-8')
        iv = b64encode(iv).decode('utf-8')
        result = "{0}.{1}".format(body, iv)
        return result

    def decrypt(self, ciphertext):
        """
        数据格式:"{0}.{1}".format(body, iv) 数据为base64的字符串
        解密数据为:由点进行数据分隔,body,iv数据为base64的字符串
        :param ciphertext:
        :return:
        """
        body, iv = ciphertext.split(".")
        body = b64decode(body.encode('utf-8'))
        iv = b64decode(iv.encode('utf-8'))
        cipher = AES.new(self.key, self.mode, iv)
        body = Padding.unpad(cipher.decrypt(body), self.bs).decode('utf-8')
        return body


if __name__ == '__main__':
    import doctest

    doctest.testmod(verbose=True)

参考