We have a legacy python2 application that uses an RSA private key to decrypt a base64-encoded file. How do we port this to python3?
Python2.7 code, this does work:
def _unlock_keys(self, passphrase):
#get user priv key passphrase and perform unlock of randbits
master_key = None
master_key_path = 'master.pem'
with open(master_key_path) as f:
master_key = RSA.importKey(f.read(), passphrase)
with open('host_keys/part.A') as f:
enc_bits = f.read()
self.part_A = master_key.decrypt(b64decode(enc_bits))
# self.part_A has the data we need
Python3 code, this does not work:
This is what we've tried on the python3 code, but so far it decrypts to the empty bytestring b'':
def _unlock_keys(self, passphrase):
#get user priv key passphrase and perform unlock of randbits
master_key = None
master_key_path = 'master.pem'
with open(master_key_path) as f:
master_key = RSA.importKey(f.read(), passphrase)
with open('host_keys/part.A') as f:
enc_bits = f.read()
# Note: PKCS1_OAEP doesn't work because this is raw, unpadded:
decryptor = PKCS1_v1_5.new(master_key)
self.part_A = decryptor.decrypt(b64decode(enc_bits), "error")
print(self.part_A)
# self.part_A prints as b''
OpenSSL command that works
We can use OpenSSL to decrypt it as follows. Note the -raw argument because there is no PKCS padding, and this is PKCS v1.5:
openssl rsautl -pkcs -raw -decrypt -in <(base64 -d host_keys/part.A) -out /proc/self/fd/1 -inkey master.pem
Enter pass phrase for master.pem:
<correct output>
(Maybe I should have posed the question "how can I implement this openssl command in python3?" but methinks that would be an XY question...)
Key format
In case it helps, this is what the private key format looks like.
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,<REDACTED>
...
-----END RSA PRIVATE KEY-----
I cannot reproduce the result being empty, but the fact that you were doing raw decryption in Python 2.7 and PKCS#1 v1.5 decryption in Python 3 is definitely an issue. Those are not compatible, and I'm surprised you didn't get errors (or maybe you did and swallowed them?).
Raw RSA encryption is so vulnerable it's practically useless, but in the interest of migrating your data, here's how you do raw decryption in newer versions of Crypto (now available as
pip install pycryptodome):Note that the inputs and outputs must be numbers, so we need
bytes_to_longandlong_to_bytes.