Search code examples
python.netpython-3.xbouncycastleecdsa

Create keys with python and use it in .net


I try to create keys with the python package ecdsa, export it to der and use them with BouncyCastle under .Net (and vice versa).

This is my code in Python:

import base64
from ecdsa.keys import SigningKey
from ecdsa.curves import NIST521p, NIST384p, NIST256p

@classmethod
def CreateKey(self) -> SigningKey:
    privateKey = SigningKey.generate(NIST256p) 
    return privateKey

@classmethod     
def GetPublicKey(self, privateKey: SigningKey) -> str:
    publicKey = privateKey.get_verifying_key()
    der = publicKey.to_der()
    return base64.b64encode(der)

I get two strings that I want to import in .Net:

        Const plainDerBase64Pub = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyJW32bO4wswhE9ZC5klCjRNDJQSB0lIBphe9wIa/W2n3fBJ0cDpSpy9qlq2L6sa4W5lgRHYD9IyfETom6YOH/g=="
    Const plainDerBase64Priv = "MHcCAQEEIKjIPCG9CGMunu34jXnDZg1LmNYrcJo8EqzKbRu2E24MoAoGCCqGSM49AwEHoUQDQgAEyJW32bO4wswhE9ZC5klCjRNDJQSB0lIBphe9wIa/W2n3fBJ0cDpSpy9qlq2L6sa4W5lgRHYD9IyfETom6YOH/g=="

    Dim keyPriv = PrivateKeyFactory.DecryptKey({}, Base64.Decode(plainDerBase64Priv))
    Dim keyPub = PrivateKeyFactory.DecryptKey({}, Base64.Decode(plainDerBase64Pub))

But I get an exception:

Test method LicenseProtectorTest.KeyManagementTest.ImportKeyFromPythonTest threw exception: 
System.ArgumentException: Wrong number of elements in sequence
Parametername: seq
Result StackTrace:  
bei Org.BouncyCastle.Asn1.Pkcs.EncryptedPrivateKeyInfo..ctor(Asn1Sequence seq) in C:\BouncyCastle\crypto\src\asn1\pkcs\EncryptedPrivateKeyInfo.cs:Zeile 18.
   bei Org.BouncyCastle.Asn1.Pkcs.EncryptedPrivateKeyInfo.GetInstance(Object obj) in C:\BouncyCastle\crypto\src\asn1\pkcs\EncryptedPrivateKeyInfo.cs:Zeile 42.
   bei Org.BouncyCastle.Security.PrivateKeyFactory.DecryptKey(Char[] passPhrase, Asn1Object asn1Object) in C:\BouncyCastle\crypto\src\security\PrivateKeyFactory.cs:Zeile 196.
   bei Org.BouncyCastle.Security.PrivateKeyFactory.DecryptKey(Char[] passPhrase, Byte[] encryptedPrivateKeyInfoData) in C:\BouncyCastle\crypto\src\security\PrivateKeyFactory.cs:Zeile 182.
   bei LicenseProtectorTest.KeyManagementTest.ImportKeyFromPythonTest() in ...

Any idea what I'm doing wrong here?


Solution

  • I'm not familiar with the python library, but I was able to guess at the formats and the following code will parse the example data:

    using System;
    
    using Org.BouncyCastle.Asn1.Sec;
    using Org.BouncyCastle.Asn1.X9;
    using Org.BouncyCastle.Crypto.Parameters;
    using Org.BouncyCastle.Security;
    using Org.BouncyCastle.Utilities.Encoders;
    
    namespace BCTests
    {
        class MainClass
        {
            public static void Main(string[] args)
            {
                var plainDerBase64Pub = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyJW32bO4wswhE9ZC5klCjRNDJQSB0lIBphe9wIa/W2n3fBJ0cDpSpy9qlq2L6sa4W5lgRHYD9IyfETom6YOH/g==";
                var plainDerBase64Priv = "MHcCAQEEIKjIPCG9CGMunu34jXnDZg1LmNYrcJo8EqzKbRu2E24MoAoGCCqGSM49AwEHoUQDQgAEyJW32bO4wswhE9ZC5klCjRNDJQSB0lIBphe9wIa/W2n3fBJ0cDpSpy9qlq2L6sa4W5lgRHYD9IyfETom6YOH/g==";
    
                var pubKeyBytes = Base64.Decode(plainDerBase64Pub);
                var privKeyBytes = Base64.Decode(plainDerBase64Priv);
    
                var pubKey = PublicKeyFactory.CreateKey(pubKeyBytes);
    
                var privKeyStruct = ECPrivateKeyStructure.GetInstance(privKeyBytes);
                var x9 = ECNamedCurveTable.GetByName("P-256");
                var ec = new ECDomainParameters(x9.Curve, x9.G, x9.N, x9.H, x9.GetSeed());
                var privKey = new ECPrivateKeyParameters(privKeyStruct.GetKey(), ec);
            }
        }
    }
    

    pubKey and privKey are then ECPublicKeyParameters and ECPrivateKeyParameters that you can use with various algorithms.

    Note that DER is an encoding, not really a format. For the public key, the python code is producing a SubjectPublicKeyInfo, which is the standard X.509 format for public keys.

    There ought to be an option with the private key of generating a PrivateKeyInfo (or EncryptedPrivateKeyInfo using a password), both of these are formats from the PKCS#8 standard. If the python code can output one of these, then decoding the private key would be easier as just PrivateKeyFactory.CreateKey(bytes) or .DecryptKey(password, bytes).