pythoncryptographyrsa

Python library for RSA public decryption


I'm searching for a python library that is capable if decrypting and encrypting RSA (RSA_PKCS1_PADDING to be precise) with a public key. I've aleready tried pycryptodome, cryptography and rsa and all of them cannot decrypt using public key. I have searched through hundreds of posts, and all answers are useless, so to filter them:

  1. I am not confusing a public key with a private key
  2. Public decryption is possible (I was able to do it here)
  3. There is no other way around. I literally need to send public encrypted messages to the server, and recieve private encrypted messages and decrypt them with the public key

Ideally it should be something like nodejs's crypto.publicDecrypt() and crypto.publicEncrypt(). Please help me to find even if not a library, but just a function that is capable of doing it. I looked through the hundreds of posts and I'm feel like I'm going insane. Thank you.


Solution

  • It is as you say indeed possible to encrypt with private and decrypt with public, the mathematical symmetry in RSA allows just swapping e/d in the keys and then calling the encrypt/decrypt functions.

    This being said, I want to emphasize that I'm not a crypto expert and cannot say for certain that this doesn't compromise security.

    So, you could extend the RSA-Key class with that swapped logic, use blackmagic to swap the implementation of the loaded key, and pass it to the normal functions:

    from Crypto.PublicKey.RSA import RsaKey
    from Crypto.PublicKey import RSA
    from Crypto.Cipher import PKCS1_OAEP
    from Crypto.Math.Numbers import Integer
    
    class SwappedRsaKey(RsaKey):
        def _encrypt(self, plaintext):
            # normally encrypt is p^e%n
            return int(pow(Integer(plaintext), self._d, self._n))
        def _decrypt(self, ciphertext):
            # normally decrypt is c^d%n
            return int(pow(Integer(ciphertext), self._e, self._n))
    
    data = "I met aliens in UFO. Here is the map.".encode("utf-8")
    
    # It's important to also use our swapped logic in encryption step, otherwise the lib would still use e&n (the private contains all 3 values).
    
    private_key = RSA.import_key(open("mykey.pem").read())
    private_key.__class__ = SwappedRsaKey
    public_key = RSA.import_key(open("mykey.pub").read())
    public_key.__class__ = SwappedRsaKey
    
    cipher_priv = PKCS1_OAEP.new(private_key)
    cipher_pub = PKCS1_OAEP.new(public_key)
    
    enc_data = cipher_priv.encrypt(data)
    
    # Decrypt again, just a showcase to prove we can get the value back
    dec_data = cipher_pub.decrypt(enc_data)
    
    print(dec_data.decode("utf-8"))