Search code examples
pythonencryptionaespycryptoencryption-asymmetric

Why doesn't PyCrypto allow for encoding with the private and decoding with the public key to verify the sendersidentity


I've been playing around with the PyCrypto Library with the intention to make a system that works like this:

  1. Create a RSA keyset for both the sender and the receiver and a seperate AES key
  2. The sender:
    1. Encrypts the message with the AES key using the ECB encryption mode ECB
    2. Encrypts the AES key with the senders private key
    3. Concats their own name to the result of the last step
    4. Encrypts the result from the last step with the receivers public key
    5. Combines the results from step 1 and 4 into 1 message and sends it to the receiver
  3. And the receiver
    1. Splits the message into the actual message and the double encrypted key (which also contains the senders name)
    2. Decrypts the double encrypted key using their own private key
    3. Uses the name of the sender to retrieve the senders public key
    4. Decrypts the AES key with the senders public key
    5. Decrypts the message with the AES key

But I'm having trouble with the double encryption. The problem is that I can't decrypt something with the public key when it is encrypted with the private key. I understand that it is because ARS isn't supposed to work backwards, but I want to do this to verify the senders identity to the receiver. I don't know how else to do this.

This is the current code (I know it's messy, I'm a beginner in python and totally new to encryption):

from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import AES, PKCS1_OAEP
from termcolor import colored
import base64, os


def generate_keys_rsa():
    modulus_length = 256*4
    privatekey = RSA.generate(modulus_length, Random.new().read)
    publickey = privatekey.publickey()
    return privatekey, publickey


def encrypt_message_rsa(a_message, publickey):
    encryptor = PKCS1_OAEP.new(publickey)
    encrypted_msg = encryptor.encrypt(a_message)
    encoded_encrypted_msg = base64.b64encode(encrypted_msg)
    return encoded_encrypted_msg


def decrypt_message_rsa(encoded_encrypted_msg, privatekey):
    decoded_msg = base64.b64decode(encoded_encrypted_msg)
    decryptor = PKCS1_OAEP.new(privatekey)
    decoded_decrypted_msg = decryptor.decrypt(decoded_msg)
    return decoded_decrypted_msg


def generate_key_aes():
    aes_key_length = 32
    secret_key = os.urandom(aes_key_length)
    encoded_secret_key = base64.b64encode(secret_key)
    return encoded_secret_key


def encrypt_message_aes(private_msg, encoded_secret_key, padding_character):
    secret_key = base64.b64decode(encoded_secret_key)
    cipher = AES.new(secret_key, AES.MODE_ECB)
    padded_private_msg = private_msg + (padding_character * ((16 - len(private_msg)) % 16))
    encrypted_msg = cipher.encrypt(padded_private_msg.encode("utf-8"))
    encoded_encrypted_msg = base64.b64encode(encrypted_msg)
    return encoded_encrypted_msg


def decrypt_message_aes(encoded_encrypted_msg, encoded_secret_key, padding_character):
    secret_key = base64.b64decode(encoded_secret_key)
    cipher = AES.new(secret_key, AES.MODE_ECB)
    encrypted_msg = base64.b64decode(encoded_encrypted_msg)
    decrypted_msg = cipher.decrypt(encrypted_msg)
    unpadded_private_msg = decrypted_msg.rstrip(padding_character)
    return unpadded_private_msg.decode("ascii")


#         PROCESS         #
# 'rec' means 'received'  #
# 'enc' means 'encrypted' #
# 'dec' means 'decrypted' #
# 'pad' means 'padding'   #
# 'div' means 'division'  #
#         PROCESS         #

raw_txt = input("Message to send: ")
txt = str(raw_txt)

pad_char = "{"
div_char = "|"

# ASSEMBLY #
privatekey0_RSA , publickey0_RSA = generate_keys_rsa()
privatekey1_RSA , publickey1_RSA = generate_keys_rsa()
key_AES = generate_key_aes()

enc_txt = encrypt_message_aes(txt, key_AES, pad_char)
enc_key = encrypt_message_rsa(key_AES, privatekey0_RSA)

msg = (
        pad_char.encode("utf-8") +
        div_char.encode("utf-8") +
        enc_txt +
        div_char.encode("utf-8") +
        enc_key
)

# DISASSEMBLY #
rec_pad = msg[: 1]
rec_div = chr(msg[1])
rec_enc_txt = msg[2: (msg[2:].find(rec_div.encode("utf-8")) + 2)]
rec_enc_key = msg[(msg[2:].find(rec_div.encode("utf-8")) + 3):]
rec_dec_key = decrypt_message_rsa(rec_enc_key, publickey0_RSA)
rec_dec_txt = decrypt_message_aes(rec_enc_txt, rec_dec_key, rec_pad)

# RESULT LOG #
print()
print(colored("Total message: ", "blue", None, ["bold"]), "%s" % msg)
print()
print(colored(" Original key_AES: ", "blue", None, ["bold"]), "%s - (%d)" % (key_AES, len(key_AES)))
print(colored("Encrypted key_AES: ", "blue", None, ["bold"]), "%s - (%d)" % (enc_key, len(enc_key)))
print(colored("Decrypted key_AES: ", "blue", None, ["bold"]), "%s - (%d)" % (rec_dec_key, len(rec_dec_key)))
print(colored("Decrypted key_AES == Original key_AES: ", "blue", None, ["bold"]), colored("%s" % (rec_dec_key == key_AES), "green" if (rec_dec_key == key_AES) else "red", None, ["bold"]))
print()
print(colored(" Original text: ", "blue", None, ["bold"]), "%s - (%d)" % (txt, len(txt)))
print(colored("Encrypted text: ", "blue", None, ["bold"]), "%s - (%d)" % (enc_txt, len(enc_txt)))
print(colored("Decrypted text: ", "blue", None, ["bold"]), "%s - (%d)" % (rec_dec_txt, len(rec_dec_txt)))
print(colored("Decrypted text == Original text: ", "blue", None, ["bold"]), colored("%s" % (rec_dec_txt == txt), "green" if (rec_dec_txt == txt) else "red", None, ["bold"]))

So, do you know either a solution to use ARS backwards, or an alternative way of verifying the senders identity?


Solution

  • an alternative way of verifying the senders identity

    Could that be a digital signature? It is supported by PyCryptodome and uses RSA.