how to decode IPFS private and public key in DER/PEM format that can work with the pycryptodome library(for Python 3)? I get the keys from the IPFS configuration file as strings, so I will not explain this process here.
What I'm trying to do:
import base64, Crypto
publicKey = "CAASpgIwgE ... jkupAgMBAAE="
privateKey = "CAASqQkwgg ... Xmzva/Km7A=="
publicKey = base64.b64decode(publicKey)
key = Crypto.PublicKey.RSA.import_key(publicKey)
crypter = Crypto.Cipher.PKCS1_OAEPPKCS1_OAEP.new(key)
encryptedData = crypter.encrypt(data.encode())
result = base64.b64encode(encryptedData).decode()
I get the following exception:
key = Crypto.PublicKey.RSA.importKey(publicKey)
File "/usr/local/lib/python3.6/site-packages/Crypto/PublicKey/RSA.py", line 754, in import_key
raise ValueError("RSA key format is not supported")
Similar problem with privateKey. In what format is the key and how to convert it to an acceptable format?
import_key function source code is there: https://github.com/Legrandin/pycryptodome/blob/master/lib/Crypto/PublicKey/RSA.py#L682
The solution was not as straightforward as it seemed to me at first glance.
For a start, you need to understand that the contents of the PrivateKey and PublicKey variables are not just a pure key encoded in Base64, it is a protobuf object serialized in ByteArray and then encoded in Base64. In order to get a key out of it, you first need to get a schema of this object, which is available by reference.
We save this file and follow the instructions on this page. In short, I ran the command protoc --python_out=. crypto.proto
to create a Python module called crypto_pb2.py
.
All preparations are complete and now go to the code:
import crypto_pb2
import base64
publicKey = "CAASpgIwgE ... jkupAgMBAAE="
privateKey = "CAASqQkwgg ... Xmzva/Km7A=="
You must first decode the base64 string to a byte array:
decoded = base64.b64decode(publicKey)
This function deserializes an array of bytes into a familiar Python protobuf object, I took it from this answer and modified a little:
def deserialize(byte_message, proto_type):
module_, class_ = proto_type.rsplit('.', 1)
class_ = getattr(crypto_pb2, class_) # crypto_pb2 is a name of module we recently created and imported
rv = class_()
rv.ParseFromString(byte_message) # use .SerializeToString() to reverse operation
return rv
Further we call the function, pass the decoded base64 and the name of the class to which it corresponds (PublicKey
for publicKey
and PrivateKey
for privateKey
), I'm interested in the Data
property.
publicKey = deserialize(decoded, 'crypto.pb.PublicKey').Data
Now you can transfer it to the import_key function as ByteArray. Do not perform additional conversions.
key = Crypto.PublicKey.RSA.import_key(publicKey)
crypter = Crypto.Cipher.PKCS1_OAEPPKCS1_OAEP.new(key)
encryptedData = crypter.encrypt(data.encode())
result = base64.b64encode(encryptedData).decode()