简体   繁体   中英

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.

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"))

First of all, big thanks to Tobias K. for the short and clear answer. It worked flawlessly.

But when I tried to decrypt the server messages, pycryptodome kept returning sentinel instead of decrypted data. I decided to look into the library source code, and check what would .decrypt() return if I comment out this lines in .../Crypto/Cipher/PKCS1_v_1_5.py :

if  not em.startswith(b'\x00\x02') or sep < 10:
    return sentinel

And to my surprise it returned the decrypted message! Not sure that I didn't break something, but it works. I guess it was a bug? This is aleready fixed in their github repo, but the version is not released yet

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM