I am trying to implement symmetric and asymmetric encryption in a program that has the ability to sign and encrypt, and then verify and decrypt files. I am using the cryptography hazmat primitives library. I am running into an error that is causing my RSA encryption to fail when I try to encrypt it with my public RSA key. I am pulling the public key from a separate file. I know that my key needs to be a <class 'cryptography.hazmat.bindings._rust.openssl.rsa.RSAPublicKey'> type in order for the encryption to work. I commented out the try/except lines for now.
What is confusing me is that, after opening the public key in the read_public_key function, the type is <class 'cryptography.hazmat.bindings._rust.openssl.rsa.RSAPublicKey'>, but then when I check the type in the encrypt_aes_key_rsa function, it is now bytes. I cannot figure where and why this change is occurring.
I am able to get the encryption to work using just RSA keys and without signing before encrypting, so I am assuming the issue is coming from the signing function and/or the AES encryption, but I cannot for the life of me figure out what the issue is. Any help would be greatly appreciated, thanks!
from cryptography.hazmat.primitives.asymmetric import padding, utils, rsa
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.serialization import load_pem_public_key, load_pem_private_key, load_der_public_key, load_der_private_key
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import sys, argparse, os
#Argument parser for encypt and decrypt commands
def parse_args():
parser = argparse.ArgumentParser(description='Encrypt/Decrypt')
parser.add_argument('-e', '--encrypt', nargs=4)
parser.add_argument('-d', '--decrypt', nargs=4)
return parser.parse_args()
#Serialize the key from the public key PEM file
def read_public_key(public_key_pem):
with open(public_key_pem, 'rb') as key_file:
public_key = load_pem_public_key(key_file.read())
return public_key
#Serialize the key from the private key PEM file
def read_private_key(private_key_pem):
with open(private_key_pem, 'rb') as key_file:
private_key = load_pem_private_key(key_file.read(),password = None)
return private_key
#Encrypt the message with AES key
def encrypt_message_aes(message, aes_key):
iv = os.urandom(16)
cipher = Cipher(algorithms.AES(aes_key), modes.GCM(iv))
encryptor = cipher.encryptor()
ciphertext = encryptor.update(message) + encryptor.finalize()
return ciphertext + iv
#Encrypt the AES key using RSA
def encrypt_aes_key_rsa(public_key, aes_key):
encrypted_aes_key = public_key.encrypt(
aes_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
return encrypted_aes_key
#Decrypt the ciphertext file using the recipient's private key
def decrypt_message_aes(encrypted_message, key):
iv = encrypted_message[:16]
ciphertext = encrypted_message[16:]
cipher = Cipher(algorithms.AES(key), modes.GCM(iv), backend=default_backend())
decryptor = cipher.decryptor()
padded_message = decryptor.update(ciphertext) + decryptor.finalize()
padding_len = padded_message[-1]
return padded_message[:-padding_len]
#Decrypt the AES key
def decrypt_aes_key_rsa(encrypted_aes_key, private_key):
aes_key = private_key.decrypt(
encrypted_aes_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
return aes_key
#Sign message with sender's private key
def sign_message(private_key, message):
signature = private_key.sign(
message,
padding.PKCS1v15(),
hashes.SHA256()
)
return signature
#Verify the message signature
def verify_signature(message, signature, public_key):
public_key.verify(
signature,
message,
padding.PKCS1v15(),
hashes.SHA256()
)
return True
#Main function
def main():
args = parse_args()
#Encrypt command
if args.encrypt:
public_key_pem = args.encrypt[0]
public_key = read_public_key(public_key_pem)
private_key_pem = args.encrypt[1]
private_key = read_private_key(private_key_pem)
ciphertext_file = args.encrypt[3]
input_plaintext_file = args.encrypt[2]
with open(input_plaintext_file, "rb") as file:
message = file.read()
with open("aes_key_file.pem", "rb") as file:
aes_key = file.read()
encrypt_message_aes(message, aes_key)
encrypt_aes_key_rsa(aes_key, public_key_pem)
signature = sign_message(message, public_key_pem)
with open(ciphertext_file, "wb") as file:
file.write(ciphertext + iv + signature)
with open("encrypted_aes_key_file", "wb") as f:
f.write(encrypted_aes_key)
with open("signature_file", "wb") as f:
f.write(signature)
#Decrypt command
if args.decrypt:
private_key_pem = args.decrypt[0]
public_key_pem = args.decrypt[1]
ciphertext_file = args.decrypt[2]
input_plaintext_file = args.decrypt[3]
with open("encrypted_message_file", "rb") as f:
encrypted_message = f.read()
with open("encrypted_aes_key_file", "rb") as f:
encrypted_aes_key = f.read()
with open("signature_file", "rb") as f:
signature = f.read()
with open(ciphertext_file, "rb") as file:
encrypted_message = file.read()
decrypted_aes_key = decrypt_aes_key_rsa(encrypted_aes_key, private_key_pem)
decrypted_message = decrypt_message_aes(encrypted_message, decrypted_aes_key)
is_valid_signature = verify_signature(encrypted_message, signature, public_key_pem)
with open(input_plaintext_file, "wb") as file:
file.write(is_valid_signature)
if __name__ == "__main__":
main()
encrypt_aes_key_rsa(aes_key, public_key_pem).