1

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

Solinnen
  • 330
  • 1
  • 12

1 Answers1

2

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()
Solinnen
  • 330
  • 1
  • 12
  • the link reference is broken, could you please show what is inside of the imported module "crypto_pb2" ? I appreciate it – Lara Sep 25 '21 at 17:21