I am implementing a python package which can encrypt and decrypt contents of QR code. I made a module named rsa_module.py
, which encrypts and decrypts messages, as follows:
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()
If I run this script then the message is being encrypted and decrypted successfully. But when I use the same functions in another module, which decrypts the message from the QR code, I get an error. The name of this qr code decryption model is decrypt_qr.py
, and the code for it as follows:
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()
If I run the decrypt_qr.py
, I receive the following error:
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'
But if I run the rsa_module.py
with just the message being passed then it does decrypt properly. Can anyone suggest where I am going wrong?
The encryption module, named encrypt_qr.py
, is as follows:
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()
If I run the encrypt_qr.py
script then the qr code is generated properly and contains encrypted byte stream, which is then used in the decryption script (decrypt_qr.py
).
You are passing in text, decoded from UTF-8:
decoded_result=codes[0].decode('utf8')
# ...
return de(decoded_result, password)
but your decrypt()
function expects this to be bytes
, because you opened the file in binary mode:
def decrypt(encrypted_message, secret_code):
# ...
with open('encrypted_data.txt', 'wb') as temp_file:
for item in (encrypted_message):
temp_file.write(item)
Pass in a sequence of bytes instead, not a single str
object:
decoded_result = codes[0]
# ...
return de([decoded_result], password)
Note that (encrypted_message)
in for item in (encrypted_message):
is the same thing as encrypted_message
without parentheses. The (...)
are only serving to group the element, no tuple is created. If you were to pass in a single bytes
object, you'd be iterating over a sequence of integers (representing the individual bytes in the object).
Also, there is no need to write the data to a file on disk. You could use a io.BytesIO()
object for an in-memory file, or just slice up the message in the right block sizes. Using a file on disk here actually makes the code more complicated than it is, and you hardcoded the /tmp
path (different OS-es use different paths, the tempfile
module can abstract this for you).