I need to encrypt some message before sending it as request body in an API. I have to do this in Python and the library I used is Pycryptodome. From hereon, when I use the term API, I am referring to the API endpoint provided by the vendor and not the Pycryptodome API. The receiver(API vendor) shared the certificate (RSA 2048 bits) which I need to use to encrypt this message. This certificate is in binary format (DER) and has .cer
as the extension. I need to import this using Pycryptodome and from the docs (https://pycryptodome.readthedocs.io/en/latest/src/public_key/rsa.html), I find that the import_key
method accepts X.509 certificates in either binary or PEM encoding. The API documentation says I need to use RSA/NONE/OAEPWithSHA1AndMGF1Padding
for encryption. This is my code.
def encrypt_message(message):
with open('/home/krishna/work/.certificates/random_certificate.cer', 'rb') as _file:
key_contents = _file.read()
recipient_key = RSA.import_key(key_contents)
cipher_rsa = PKCS1_OAEP.new(recipient_key)
return base64.b64encode(cipher_rsa.encrypt(message.encode())).decode()
I read the PKCS1_OAEP.py
file, the default mask generation function is MGF1, default hash algorithm is SHA1. Hence, I did not specify these parameters in the new
function in the above code. Also, I need to send this encrypted message as a base64 string (UTF-8). After reading the API documentation and the Pycryptodome documentation, I felt this code is sufficient for encryption. Also, this code is somewhat standard and is almost the same as the one in the Pycryptodome documentation. However, I get a decryption error and the vendor tells me this is due to "BAD PADDING EXCEPTION". This should not happen since I used OAEP for padding and the vendor confirmed they are using OAEP for padding as well. This is very hard for me to debug as I do not possess the vendor's private key.
So I tried generating my own pair of private and public keys (RSA-2048) and checked if I am able to decrypt my message. And duh, I am able to decrypt my message. Here is the code for encryption and decryption using my own keys.
def encrypt_message(message):
with open('my_pub.pem', 'rb') as _file:
pub = _file.read()
recipient_key = RSA.importKey(pub)
cipher_rsa = PKCS1_OAEP.new(recipient_key)
return base64.b64encode(cipher_rsa.encrypt(message.encode())).decode()
def decrypt_message(gibberish):
with open('my_priv.pem', 'rb') as _file:
priv = _file.read()
pvt_key = RSA.importKey(priv)
cipher_rsa = PKCS1_OAEP.new(pvt_key)
return cipher_rsa.decrypt(base64.b64decode(gibberish.encode())).decode()
So where I am going wrong? They key difference between this test and the actual API call is that my keys are generated in PEM format and have .pem
as the extension. In the case of the API call, I have to use a .cer
certificate in DER format. But I am assuming Pycryptodome is handling this for me (from the docs).
I have a few more questions regarding this.
- What is NONE in
RSA/NONE/OAEPWithSHA1AndMGF1Padding
? - What is the difference between
import_key()
andimportKey()
in Pycryptodome? There are 2 methods and it does not look likeimportKey()
has some documentation to it. - Can padding exceptions occur while using OAEP? From what I read from the internet, OAEP is the best method out there, not only in terms of adding randomness during encryption, but I also read that padding exceptions are very rare when this is used.
I really need to know what is going wrong here as I am dead clueless. Any suggestions or help will be appreciated.