使用PyCryptodome解密二维码出错

Error in Decryption of QR Code using PyCryptodome

我正在实现一个 python 包,它可以加密和解密 QR 码的内容。我做了一个模块,名字叫rsa_module.py,用来加密和解密消息,如下:

from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP
import os

def generate_keys(secret_code):
    key = RSA.generate(2048)
    encrypted_key = key.exportKey(passphrase=secret_code, pkcs=8,
                                  protection="scryptAndAES128-CBC")

    output_directory=os.path.dirname(os.path.abspath(__file__))+'/Keys/'
    if(os.path.exists(output_directory)):
        # Save encrypted private key
        file_out = open(output_directory + "/rsa_private_key.pem", "wb")
        file_out.write(encrypted_key)
        # Save public key
        file_out = open(output_directory + "/rsa_public_key.pem", "wb")
        file_out.write(key.publickey().exportKey())
    else:
        os.mkdir(output_directory)
        # Save encrypted private key
        file_out = open(output_directory + "/rsa_private_key.pem", "wb")
        file_out.write(encrypted_key)
        # Save public key
        file_out = open(output_directory + "/rsa_public_key.pem", "wb")
        file_out.write(key.publickey().exportKey())

def encrypt(message):
    output_directory=os.path.dirname(os.path.abspath(__file__))+'/Keys/'

    with open('encrypted_data.txt', 'wb') as out_file:
        recipient_key = RSA.import_key(
        open(output_directory + '/rsa_public_key.pem').read())
        session_key = get_random_bytes(16)
        cipher_rsa = PKCS1_OAEP.new(recipient_key)
        out_file.write(cipher_rsa.encrypt(session_key))

        cipher_aes = AES.new(session_key, AES.MODE_EAX)
        encoded = message.encode("latin-1")
        data = encoded
        ciphertext, tag = cipher_aes.encrypt_and_digest(data)
        out_file.write(cipher_aes.nonce)

        out_file.write(tag)

        out_file.write(ciphertext)

    with open('encrypted_data.txt', 'rb') as fobj:
        output = [l for l in fobj.readlines()]
    os.remove('encrypted_data.txt')
    return output

def decrypt(encrypted_message, secret_code):
    code = secret_code
    output_directory=os.path.dirname(os.path.abspath(__file__))+'/Keys/'

    with open('encrypted_data.txt', 'wb') as temp_file:
        for item in (encrypted_message):
            temp_file.write(item)
    with open('encrypted_data.txt', 'rb') as fobj:
        private_key = RSA.import_key(
        open(output_directory + '/rsa_private_key.pem').read(),
        passphrase=code)
        enc_session_key, nonce, tag, ciphertext = [ fobj.read(x) 
        for x in (private_key.size_in_bytes(), 
        16, 16, -1) ]
        cipher_rsa = PKCS1_OAEP.new(private_key)
        session_key = cipher_rsa.decrypt(enc_session_key)
        cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce)
        data = cipher_aes.decrypt_and_verify(ciphertext, tag)

    os.remove('encrypted_data.txt')
    return data.decode('utf8')

def main():
    generate_keys('secret one')
    encrypted = encrypt('blah blah blah blo')
    #encrypt_file('blah blah blah blo')
    print('Encryption Complete!')
    print('Decrypting message now....')
    print(encrypted)
    print(decrypt(encrypted, 'secret one'))
    #decrypt_file('secret one')

if __name__=='__main__': main()

如果我运行此脚本,则消息正在成功加密和解密。但是当我在另一个模块中使用相同的功能来解密来自二维码的消息时,我得到了一个错误。此二维码解密模型名称为decrypt_qr.py,其代码如下:

from qrtools import qrtools
from PIL import Image
import zbarlight
import os
from rsa_module import decrypt as de

def decrypt(file_name, password):
    keys_directory=os.path.dirname(os.path.abspath(__file__))+'/Keys/'
    private_key_path = keys_directory + '/rsa_private_key.pem'

    if(os.path.exists(private_key_path)):
        output_directory=os.path.dirname(os.path.abspath(__file__))+'/Output/'
        file_path = output_directory + file_name + '.PNG'
        with open(file_path, 'rb') as image_file:
            image = Image.open(image_file)
            image.load()
        codes = zbarlight.scan_codes('qrcode', image)
        decoded_result=codes[0].decode('utf8')
        print(codes[0].decode('utf8'))
        return de(decoded_result, password)
    else:
        print('No Public key available. Generate Public key and Private key first.')
        return None

def main():
    print(decrypt('my-qr','My secret'))

if __name__=='__main__': main()

如果我 运行 decrypt_qr.py,我收到以下错误:

Traceback (most recent call last):   
  File "decrypt_qr.py", line 28, in <module>
    if __name__=='__main__': main()   
  File "decrypt_qr.py", line 26, in main
    print(decrypt('my-qr','My secret'))   
  File "decrypt_qr.py", line 20, in decrypt
    return de(decoded_result, password)   
  File "/Users/king/Documents/pyWorkspace/Encrypted_QR_Code_Using_AES/rsa_module.py", line 93, in decrypt
    temp_file.write(item)
TypeError: a bytes-like object is required, not 'str'

但是如果我 运行 rsa_module.py 只传递消息,那么它会正确解密。谁能告诉我哪里出错了?

名为encrypt_qr.py的加密模块如下:

from generate_qr import make_qr_and_save
from rsa_module import encrypt as en
from rsa_module import generate_keys
import os

def encrypt(message, filename, password, size=3):
    generate_keys(password)
    keys_directory=os.path.dirname(os.path.abspath(__file__))+'/Keys/'
    public_key_path = keys_directory + '/rsa_public_key.pem'

    if(os.path.exists(public_key_path)):
        encrypted_message = en(message)
        print('\n')
        print(encrypted_message)
        make_qr_and_save(encrypted_message, filename, size)
    else:
        print('No Public key available. Generate Public key and Private key first.')
        return None

def main():
    encrypt('Buzz off!','my-qr','My secret')

if __name__=='__main__': main()

如果我运行 encrypt_qr.py 脚本然后正确生成二维码并包含加密的字节流,然后用于解密脚本(decrypt_qr.py)。

您正在传递 文本,从 UTF-8 解码:

decoded_result=codes[0].decode('utf8')
# ...
return de(decoded_result, password)   

但是您的 decrypt() 函数期望它是 bytes,因为您以二进制模式打开文件:

def decrypt(encrypted_message, secret_code):
    # ...
    with open('encrypted_data.txt', 'wb') as temp_file:
        for item in (encrypted_message):
            temp_file.write(item)  

改为传入字节序列,而不是单个 str 对象:

decoded_result = codes[0]
# ...
return de([decoded_result], password)   

注意for item in (encrypted_message):中的(encrypted_message)和没有括号的encrypted_message是一样的。 (...) 仅用于对元素进行分组,不会创建元组。如果您要传递单个 bytes 对象,您将迭代一系列整数(表示对象中的各个字节)。

此外,无需将数据写入磁盘上的文件。您可以为 in-memory 文件使用 io.BytesIO() 对象,或者只是将消息分割成正确的块大小。这里使用磁盘上的文件实际上使代码比实际更复杂,并且您硬编码了 /tmp 路径(不同的 OS-es 使用不同的路径, tempfile 模块可以为您抽象它) .