pythonPython

python实现RSA与AES混合加密

2019-05-09  本文已影响0人  就知道写代码

RSA加密

这是一种非对称加密算法,密钥分为公钥和私钥。通常私钥长度有512bit,1024bit,2048bit,4096bit,长度越长,越安全,但是生成密钥越慢,加解密也越耗时。一般公钥用来加密数据,私钥用来解密数据,并且为防止有人冒充发送密文,私钥又可以负责签名,公钥负责验证。公钥可以分发给其他系统,但是私钥一般保存在唯一的系统中。

AES加密

这是一种对称加密算法,加密和解密公用同一密钥。密钥最长只有256个bit,执行速度快,易于硬件实现。通常发送方使用密钥加密数据后会使用口令对密钥进行加密,然后把密文和加密后的密钥一同发送给接收方,接收方接收到数据后首先使用口令对密钥进行解密,在使用密钥对密文进行解密。

以上两种加密方式都有各自的特点,在互联网领域有着广泛的应用,我们需要根据不同的场景使用不同的加密解密方案。
一般需要加密传输的时候存在以下两种场景:

客户端发送加密数据,服务端解密后返回明文:

例如用户在客户端提交个人信息时,客户端把个人信息加密后发送给服务端,服务端再返回code或者提交成功的提示

客户端发送加密数据,服务端解密后同样返回密文

例如用户登录时,客户端发送加密后的账号密码,服务端解密后生成对应的token,并且对token进行加密,客户端收到返回的token再对其进行解密

如何生成RSA公钥和私钥可以参考我的另一篇文章
RSA公钥和私钥的生成以及PKCS#1与PKCE#8格式的转换

如何在python实现RSA?

可以直接用python的rsa库

import rsa

(pubkey, privkey) = rsa.newkeys(1024)
print('公钥:\n%s' % pubkey)
print('私钥:\n%s' % privkey)
message = 'hello'
encrypt_text = rsa.encrypt(message.encode(), pubkey)
print('加密后的密文:\n%s' % encrypt_text)
decrypt_text = rsa.decrypt(encrypt_text, privkey)
print('解密后的明文:\n%s' % decrypt_text)
运行结果

但是这只是简单的实现,中间省去了很多细节,这就导致我们以后跨语言加密解密的时候可能会出问题。比如java在实现RSA加密解密的时候有很多参数需要设置,如果java和python之间的参数不能保持一致,则会导致用java加密的密文用python解密不了,或者用python加密的密文用java解密不了。所以还是推荐使用可以设置更多参数的第三方RSA库。python的第三方RSA库有crypto、pycrypto和pycryptodome。crypto不推荐使用,已经停止维护了。可以使用pycrypto,不过我的电脑安装pycrypto会报错。


错误信息

提示我安装Microsoft Visual C++ 14.0,所以我直接用的pycryptodome,其实都差不多。

Config配置类

class Config:
    PUBLIC_KEY = '''
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDv/LnAoDkyewwjwqwgi9VSg51F
    +tUJ8cGwL6Rqdf5ZXrRCHI1KLjOxdFbzB81YjS76cOzezQRz2vuYDo7OvLfYSjFI
    fmukUxN+EliKkg0TwswylVroLBW9OKN70Zd62dc+gfkA3Vu8cDoRKzz6BKpo4yDo
    0D3FOsbNEj80opjmtQIDAQAB
    '''
    PRIVATE_KEY = '''
    MIICXAIBAAKBgQDv/LnAoDkyewwjwqwgi9VSg51F+tUJ8cGwL6Rqdf5ZXrRCHI1K
    LjOxdFbzB81YjS76cOzezQRz2vuYDo7OvLfYSjFIfmukUxN+EliKkg0TwswylVro
    LBW9OKN70Zd62dc+gfkA3Vu8cDoRKzz6BKpo4yDo0D3FOsbNEj80opjmtQIDAQAB
    AoGADuZtDgWkp3q2TT4X+8lSzFW5nQ+uzHhDI1JB7g43ZYsYvAYTy6hEs17ayyoP
    2NCjOw9p1Yd7IEpXVqCIw1M6QsfGdshy1NStsGpDHQYBBd8XiT8cWUaT/nmq5dEs
    i0wOITMZePLgI5/5pD4M6DIEJKskM+Rzlo47AiyRchL6pqECQQD+XAZNCl6R5wjI
    DrqW4v6Vw8mhdaPnQhPexmhHa1f9D7sA32A2H2N8M3dUDOwuG+DJhPkjVaQtFvT8
    mjDjSZTdAkEA8Yj4hncF/WnLTDSXmiWfpNwYwjfpjOj8e4/5rWHF1jWZMgl0l1AS
    Otna2dIbXp64dqsInITJTIDSQpbxuhrvuQJBAN9Ee6toLLa5KzYf55zGR13Ca9wz
    3NkDYVmsop/+E0/oXOdZK6SWTMcajeXTKgUXJ2r8M4vWgrOpcQXBeqQnVGkCQDYX
    e7j5bOD80Wemm5EM/fy4wd61ENvazbiKXNske17msAFRtsewSfTeFzIS6Mg++Yax
    9QLAhihY7T22ejo4kBkCQBdg2yKHQrmG+njGfLsdQG9MARFlnOfohoBFQTYdtrmf
    5JRNfwtPiis2YaoM2gP7z2qaunYbibDV5SYmtdD8GK0=
    '''

RSA工具类

import base64

from Crypto.Cipher import PKCS1_OAEP
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

from app.config import Config


class RSACipher():
    '''
    RSA加密、解密、签名、验签工具类
    '''

    def encrypt(self, key, text):
        '''
        加密方法
        :param key: 公钥
        :param text: 需要加密的明文
        :return: 加密后的密文
        '''
        public_key = RSA.importKey(base64.b64decode(key))
        cipher = PKCS1_OAEP.new(public_key, hashAlgo=SHA256)
        return base64.b64encode(cipher.encrypt(text.encode())).decode()

    def decrypt(self, key, text):
        '''
        解密方法
        :param key: 私钥
        :param text: 加密后的密文
        :return: 解密后的明文
        '''
        private_key = RSA.importKey(base64.b64decode(key))
        cipher = PKCS1_OAEP.new(private_key, hashAlgo=SHA256)
        return cipher.decrypt(base64.b64decode(text)).decode()

    def sign(self, key, text):
        '''
        签名方法
        :param key: 私钥
        :param text: 需要签名的文本
        :return: 签名信息
        '''
        private_key = RSA.importKey(base64.b64decode(key))
        hash_value = SHA256.new(bytes(text, encoding="utf-8"))
        signer = PKCS1_v1_5.new(private_key)
        signature = signer.sign(hash_value)
        return base64.b64encode(signature).decode()

    def verify(self, key, text, signature):
        '''
        验签方法
        :param key: 公钥
        :param text: 需要验签的文本
        :param signature: 签名信息
        :return: 验签结果
        '''
        public_key = RSA.importKey(base64.b64decode(key))
        hash_value = SHA256.new(bytes(text, encoding="utf-8"))
        verifier = PKCS1_v1_5.new(public_key)
        return verifier.verify(hash_value, base64.b64decode(signature))

if __name__ == '__main__':
    text = 'hello'
    cipher = RSACipher()
    # 加密
    encrypt_text = cipher.encrypt(Config.PUBLIC_KEY, text)
    print('加密后:\n%s' % encrypt_text)
    # 签名
    signature = cipher.sign(Config.PRIVATE_KEY, text)
    print('签名:\n%s' % signature)
    # 解密
    decrypt_text = cipher.decrypt(Config.PRIVATE_KEY, encrypt_text)
    print('解密后:\n%s' % decrypt_text)
    # 验签
    result = cipher.verify(Config.PUBLIC_KEY, decrypt_text, signature)
    print('验签:\n%s' % result)

运行结果:

加密后:
f/Ju3QYLvlhGd+EKxlH/MZQX9g2XQ8nkTq5zWP7sCnoQUmPZPAb1H8//j+2B9Uz7HdiUtys2wtk1eWsD7/TSsWz5XDWA/LFXcN0r4AdJnaI1HvLZ8625Ahh3YYia8DmW7j7IvhNDJIES1otlPKgfRb7srDr+p7x/YzrZTFp66PM=
签名:
N5CAgMhhNzTNeyjY8bCSxPOcrUL48ZIDz4LpJGuncAsDwn/sFYr2Tba57DZF2XoXXCLt+3A46Wk7O+758XmxIDmOlOzdIqBtq6X9Ya9W/1f8mR2igc1xTvfNGt5Eb5xLjmATEk6grFx0bzMd4SJg0p5ayEH4fyCrXMlCKarEsCs=
解密后:
hello
验签:
True

签名有什么用?

下面我们模拟两个场景

第一个场景:客户端发送加密数据给服务端

第二个场景:服务端发送数据给客户端

在第一个场景中,即使客户端发送的加密消息被截获,也不会造成泄密,因为只有服务端用私钥才能解密,而第二个场景中即使签名和数据都被截获,也没有危险性,因为只有服务端的私钥才能进行签名,所以即使知道消息内容也无法伪造带签名的回复给客户端,防止了消息的纂改。

虽然上面的场景中防止了泄密和纂改的问题,但是仍然存在安全隐患。在第一个场景中虽然解决了泄密的问题,但是可以通过公钥加密假信息发送给服务端。而第二个场景中,虽然解决了纂改的问题但是造成了数据的泄露。所以我们可以改进一下验证的方式,客户端和服务端同时生成一套公钥私钥,并且把客户端的公钥和服务端的私钥进行交换。

综合上面两个场景:客户端发送加密数据给服务端

Config配置类

class Config:
    CLIENT_PUBLIC_KEY = '''
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDv/LnAoDkyewwjwqwgi9VSg51F
    +tUJ8cGwL6Rqdf5ZXrRCHI1KLjOxdFbzB81YjS76cOzezQRz2vuYDo7OvLfYSjFI
    fmukUxN+EliKkg0TwswylVroLBW9OKN70Zd62dc+gfkA3Vu8cDoRKzz6BKpo4yDo
    0D3FOsbNEj80opjmtQIDAQAB
    '''
    CLIENT_PRIVATE_KEY = '''
    MIICXAIBAAKBgQDv/LnAoDkyewwjwqwgi9VSg51F+tUJ8cGwL6Rqdf5ZXrRCHI1K
    LjOxdFbzB81YjS76cOzezQRz2vuYDo7OvLfYSjFIfmukUxN+EliKkg0TwswylVro
    LBW9OKN70Zd62dc+gfkA3Vu8cDoRKzz6BKpo4yDo0D3FOsbNEj80opjmtQIDAQAB
    AoGADuZtDgWkp3q2TT4X+8lSzFW5nQ+uzHhDI1JB7g43ZYsYvAYTy6hEs17ayyoP
    2NCjOw9p1Yd7IEpXVqCIw1M6QsfGdshy1NStsGpDHQYBBd8XiT8cWUaT/nmq5dEs
    i0wOITMZePLgI5/5pD4M6DIEJKskM+Rzlo47AiyRchL6pqECQQD+XAZNCl6R5wjI
    DrqW4v6Vw8mhdaPnQhPexmhHa1f9D7sA32A2H2N8M3dUDOwuG+DJhPkjVaQtFvT8
    mjDjSZTdAkEA8Yj4hncF/WnLTDSXmiWfpNwYwjfpjOj8e4/5rWHF1jWZMgl0l1AS
    Otna2dIbXp64dqsInITJTIDSQpbxuhrvuQJBAN9Ee6toLLa5KzYf55zGR13Ca9wz
    3NkDYVmsop/+E0/oXOdZK6SWTMcajeXTKgUXJ2r8M4vWgrOpcQXBeqQnVGkCQDYX
    e7j5bOD80Wemm5EM/fy4wd61ENvazbiKXNske17msAFRtsewSfTeFzIS6Mg++Yax
    9QLAhihY7T22ejo4kBkCQBdg2yKHQrmG+njGfLsdQG9MARFlnOfohoBFQTYdtrmf
    5JRNfwtPiis2YaoM2gP7z2qaunYbibDV5SYmtdD8GK0=
    '''
    SERVER_PUBLIC_KEY = '''
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDxpMZQCTykdwUcUzyHgd6Q79de
    4/F26bhIpOVCDpWNxlLQFdbGneTTQ1AJz/wwfNMgEPMnJvV3ZLrbqH9uV5W+8NG0
    UDaXyZYo8fXhfD7Aeret6/CgH1iZamzR4DfADCvT+V81cjeGIhJ1JSYfxGIsC4mM
    35TZ5p530ayOJ1KPHwIDAQAB
    '''
    SERVER_PRIVATE_KEY = '''
    MIICXQIBAAKBgQDxpMZQCTykdwUcUzyHgd6Q79de4/F26bhIpOVCDpWNxlLQFdbG
    neTTQ1AJz/wwfNMgEPMnJvV3ZLrbqH9uV5W+8NG0UDaXyZYo8fXhfD7Aeret6/Cg
    H1iZamzR4DfADCvT+V81cjeGIhJ1JSYfxGIsC4mM35TZ5p530ayOJ1KPHwIDAQAB
    AoGBAL8E8ZvNYXnleE3G4t9/41ARuOATMws8gOg0KeMJImI7t7U0vl6t7HixCnFn
    T8WIt2Du5Tg7DOo/35LK5Ul1xTHtYmQBdxTbg1WT89s3RWEvL4epHZQxzCQFJ1Pz
    zjDFifPNDEA7ZME7sx/E2qPDinAD+JHNELhtNMDq5rhPuYGRAkEA/W6mZCbDfrY9
    /qcbOe2s8ugeoDqoJueP7owfrut11KzvUn+e2rIBeDn1BZbWlYY5VDEq1nIKen+B
    0su4QUiiaQJBAPQXjC+YgyBzoEyMLPiA+eRC28zuwP2LXGJ1FNPZpCgnaJzF215H
    vhISwKX+b1/WZq3qBHyQnbE5zBHEIn5I5EcCQQC6X9k15dv3H4bP84xuOX/q0xFS
    vFBU7A5JW/sg5EAvO0502S21nxq9k8HBboA4ThFy/QWH1y4lkAelQfQq7oOhAkBh
    Jk4hU249KEgQr2nmrk7HTuT0t8IQJ7tpZHgZqXHwmV7FpuocqCk6QER0zMO/PTI4
    3f9TJKvescZK++lOoexZAkB0XNnpYA4fOMKhyKbcGvpqKLFb3e2ks5LbjgeAkETt
    dKipEh1RbiPrJeOwOChsx/51/cnVJrabE50AJV8AXM3e
    '''

RSA工具类

import base64

from Crypto.Cipher import PKCS1_OAEP
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

from app.config import Config


class RSACipher():
    """
    RSA加密、解密、签名、验签工具类
    """

    def encrypt(self, key, raw):
        """
        加密方法
        :param key: 公钥
        :param raw: 需要加密的明文 bytes
        :return: base64编码的密文 bytes
        """
        public_key = RSA.importKey(base64.b64decode(key))
        cipher = PKCS1_OAEP.new(public_key, hashAlgo=SHA256)
        return base64.b64encode(cipher.encrypt(raw))

    def decrypt(self, key, enc):
        """
        解密方法
        :param key: 私钥
        :param enc: base64编码的密文 bytes
        :return: 解密后的明文 bytes
        """
        private_key = RSA.importKey(base64.b64decode(key))
        cipher = PKCS1_OAEP.new(private_key, hashAlgo=SHA256)
        return cipher.decrypt(base64.b64decode(enc))

    def sign(self, key, text):
        """
        签名方法
        :param key: 私钥
        :param text: 需要签名的文本 bytes
        :return: base64编码的签名信息 bytes
        """
        private_key = RSA.importKey(base64.b64decode(key))
        hash_value = SHA256.new(text)
        signer = PKCS1_v1_5.new(private_key)
        signature = signer.sign(hash_value)
        return base64.b64encode(signature)

    def verify(self, key, text, signature):
        """
        验签方法
        :param key: 公钥
        :param text: 需要验签的文本 bytes
        :param signature: base64编码的签名信息 bytes
        :return: 验签结果 bool
        """
        public_key = RSA.importKey(base64.b64decode(key))
        hash_value = SHA256.new(text)
        verifier = PKCS1_v1_5.new(public_key)
        return verifier.verify(hash_value, base64.b64decode(signature))

if __name__ == '__main__':
    # 客户端代码
    text = b'hello server!'
    cipher = RSACipher()
    # 使用服务端公钥加密
    encrypt_text = cipher.encrypt(Config.SERVER_PUBLIC_KEY, text)
    print('加密后:\n%s' % encrypt_text)
    # 使用客户端私钥签名
    signature = cipher.sign(Config.CLIENT_PRIVATE_KEY, encrypt_text)
    print('签名:\n%s' % signature)

    # 服务端代码
    # 使用客户端公钥验签
    result = cipher.verify(Config.CLIENT_PUBLIC_KEY, encrypt_text, signature)
    print('验签:\n%s' % result)
    # 使用服务端私钥解密
    decrypt_text = cipher.decrypt(Config.SERVER_PRIVATE_KEY, encrypt_text)
    print('解密后:\n%s' % decrypt_text)

运行结果:

加密后:
b'miG43R3Xz5hyUntwfvToY96rhqgvsXzZGW6+VErgTdgs2Ew/wDlBlhjgpJQt3b13Rnq0m3sMydw7jly7/yRGW/eWaeOhuJRUQ6y1pGbvt3jIgnJvsj3XwT+S1Zah5Po87c51rQ7UamVmUvh4qFCZLzYdgXGfLLeknOTzVd+WIJo='
签名:
b'sG4qoMTccZOlrSzn6y16qGAVNtdBdnbduHPbNbEmCo+7Pe2AUg4VRK9VjG67Hqitwoa5AmHWe2M4DyolhEuuSYwrriYnSR2y6flBv0yb0G0CqJ4VODYVDlRrKTLLnTBnHoBeKNhqruir5JJ5cB9oE8XnE5rGI1yCPPhtBRjUXMM='
验签:
True
解密后:
b'hello server!'

如何在python中实现AES加密?

AES工具类

import base64

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes


class AESCipher:
    """
    AES加密、解密工具类
    """

    def __init__(self, key):
        self.key = key
        # 这里直接用key充当iv
        self.iv = key

    def encrypt(self, raw):
        """
        加密方法
        :param raw: 需要加密的密文 str
        :return: base64编码的密文 str
        """
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        return base64.b64encode(cipher.encrypt(self.__pad(raw).encode())).decode()

    def decrypt(self, enc):
        """
        解密方法
        :param enc: base64编码的密文 str
        :return: 解密后的明文 str
        """
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        return self.__unpad(cipher.decrypt(base64.b64decode(enc)).decode())

    def __pad(self, text):
        # 填充方法,加密内容必须为16字节的倍数
        text_length = len(text)
        amount_to_pad = AES.block_size - (text_length % AES.block_size)
        if amount_to_pad == 0:
            amount_to_pad = AES.block_size
        pad = chr(amount_to_pad)
        return text + pad * amount_to_pad

    def __unpad(self, text):
        # 截取填充的字符
        pad = ord(text[-1])
        return text[:-pad]


if __name__ == '__main__':
    # 随机生成16位aes密钥
    cipher = AESCipher(get_random_bytes(16))
    text = "hello"
    encrypt = cipher.encrypt(text)
    print('加密后:\n%s' % encrypt)
    decrypt = cipher.decrypt(encrypt)
    print('解密后:\n%s' % decrypt)

运行结果:

加密后:
2IVIT2hWtZo1WQ2xMd0LUg==
解密后:
hello server!

RSA+AES混合使用

客户端发送加密信息给服务端流程

代码模拟客户端向服务端发送加密信息:

from Crypto.Random import get_random_bytes

from app.AESCipher import AESCipher
from app.RSACipher import RSACipher
from app.config import Config

# 客户端代码
text = 'hello server!'

# 随机生成aes的密钥
aes_key = get_random_bytes(16)
print('随机生成的aes密钥:\n%s' % aes_key)

aes_cipher = AESCipher(aes_key)
rsa_cipher = RSACipher()

# 使用aes密钥对数据进行加密
encrypt_text = aes_cipher.encrypt(text)
print('经过aes加密后的数据:\n%s' % encrypt_text)

# 使用客户端私钥对aes密钥签名
signature = rsa_cipher.sign(Config.CLIENT_PRIVATE_KEY, aes_key)
print('签名:\n%s' % signature)

# 使用服务端公钥加密aes密钥
encrypt_key = rsa_cipher.encrypt(Config.SERVER_PUBLIC_KEY, aes_key)
print('加密后的aes密钥:\n%s' % encrypt_key)

# 客户端发送密文、签名和加密后的aes密钥
print('\n************************分割线************************\n')
# 接收到客户端发送过来的signature encrypt_key encrypt_text

# 服务端代码
# 使用服务端私钥对加密后的aes密钥解密
aes_key = rsa_cipher.decrypt(Config.SERVER_PRIVATE_KEY, encrypt_key)
print('解密后的aes密钥:\n%s' % aes_key)

# 使用客户端公钥验签
result = rsa_cipher.verify(Config.CLIENT_PUBLIC_KEY, aes_key, signature)
print('验签结果:\n%s' % result)

# 使用aes私钥解密密文
aes_cipher = AESCipher(aes_key)
decrypt_text = aes_cipher.decrypt(encrypt_text)
print('经过aes解密后的数据:\n%s' % decrypt_text)

运行结果:

随机生成的aes密钥:
b't\xb6\x8da\x8f2fa\xdc\x05\xc0\xff\x97\x84\xa2\xa9'
经过aes加密后的数据:
9TbA3VDRrLV9cz/ky/dUAQ==
签名:
b'o5sdTd+0vBUNV2zRslIvUppRIDpb4HYfMhEAQEogih5nQmLvTy6h/1hIoUS240KasoiewUAfV+V9VABkNszIzWKh1B0HKpItwpzLDmbTd3iqE2BlIx3C3b+ak1Y14YTdI626RqhrCZQMAvlxerWWYu+sMCBCTfNt+WkKNiUUJvM='
加密后的aes密钥:
b'Ybpxv9jtLhKLiUiIBTR7aRmOivvbh9KTIqjM9rMIqn4PezRftBqDa8HU4vn2y/2PEsPUY8tLbCkGC1E07d93ZrNrEX9PbU0zYTeWpRpJwfF0yWB41RonyWxQIO4cETOtqEitnRs4DRehqgVufU1SeEx45Wwy7wCGBOnudoo4bpA='

************************分割线************************

解密后的aes密钥:
b't\xb6\x8da\x8f2fa\xdc\x05\xc0\xff\x97\x84\xa2\xa9'
验签结果:
True
经过aes解密后的数据:
hello server!

服务端发送加密信息给客户端同理,只是加密方从客户端变成了服务端,解密方从服务端变成了客户端。


最后附上java版RSA和AES混合加密文章链接与相关源码:
java实现RSA与AES混合加密
python,java跨语言RSA+AES混合加密解密以及踩过的那些坑
https://github.com/taoyimin/rsa-aes-python
https://github.com/taoyimin/rsa-aes-java

上一篇下一篇

猜你喜欢

热点阅读