Search code examples
c#cryptographyopensslbouncycastlepyopenssl

Convert Python (pyelliptic, openssl) ecc key and sign to c# (BouncyCastle?)


Project https://github.com/Bitmessage/PyBitmessage use the library https://github.com/yann2192/pyelliptic for use openssl.

pyelliptic test case:

#!/usr/bin/python
import pyelliptic
from pyelliptic import arithmetic as a

#privkey  = a.changebase('02ca0020215c5516f277ac6246cbbaad81cd848328bf9bf11e98959e2b991191a71ad81a',16,256)
pubkey    = a.changebase('02ca0020012e0e59b564c025b15a587da5d33d3599df5e04deca47c783eaed25ebe5af46002032e00af993efc71a2c033a45187918f5b3c03e0e7bb539cecdc0aaa237717db1',16,256)
signature = a.changebase('30450221008538ac52dbe2b67148e99f23ad78b4c6c4939a26d789ece590c6f1e44a271454022027d4a09e5e74bb3445019a557bd2202154d2510a4df939b9f4645b311255ee37',16,256)

ecc = pyelliptic.ECC(curve='secp256k1',pubkey=pubkey)
print ecc.verify(signature,'hello')

I port PyBitmessage to c# https://github.com/sharpbitmessage/SharpBitmessage/ . For openssl I use BouncyCastle.

My test case is (write False):

using System;
using System.Text;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Utilities.Encoders;

namespace ConsoleApplication2
{
internal class Program
{
    //private static readonly byte[] _privkey  = "02ca0020215c5516f277ac6246cbbaad81cd848328bf9bf11e98959e2b991191a71ad81a".HexToBytes();
    private static readonly byte[] _pubkey =
        Hex.Decode(
            "02ca0020012e0e59b564c025b15a587da5d33d3599df5e04deca47c783eaed25ebe5af46002032e00af993efc71a2c033a45187918f5b3c03e0e7bb539cecdc0aaa237717db1");

    private static readonly byte[] _signature =
        Hex.Decode(
            "30450221008538ac52dbe2b67148e99f23ad78b4c6c4939a26d789ece590c6f1e44a271454022027d4a09e5e74bb3445019a557bd2202154d2510a4df939b9f4645b311255ee37");

    private static readonly byte[] _hello = Encoding.ASCII.GetBytes("hello");

    private static byte[] ConvertKeyFormat(byte[] k)
    {
        // convert key to 04012e0e59b564c025b15a587da5d33d3599df5e04deca47c783eaed25ebe5af4632e00af993efc71a2c033a45187918f5b3c03e0e7bb539cecdc0aaa237717db1
        byte[] result = new byte[k.Length-4+1-2];
        result[0] = 4;
        Buffer.BlockCopy(k,4     ,result,1 ,32);
        Buffer.BlockCopy(k,4+32+2,result,33,32);
        return result;
    }

    private static void Main()
    {
        var signer = new ECDsaSigner();

        X9ECParameters secp256K1 = SecNamedCurves.GetByName("secp256k1");
        ECDomainParameters ecParams = new ECDomainParameters(secp256K1.Curve, secp256K1.G, secp256K1.N, secp256K1.H);
        ECPublicKeyParameters param = new ECPublicKeyParameters(ecParams.Curve.DecodePoint(ConvertKeyFormat(_pubkey)), ecParams);

        signer.Init(false, param);

        DerSequence seq = (DerSequence)(new Asn1InputStream(_signature)).ReadObject();
        DerInteger r = (DerInteger)seq[0];
        DerInteger s = (DerInteger)seq[1];

        Console.WriteLine(signer.VerifySignature(_hello, r.Value, s.Value));
    }
}
}

How verify signature from pyelliptic in c#?


Solution

  • I refused from BouncyCastle in favor of OpenSSL.

    Working code below (comments is python code from pyelliptic):

    using System;
    using System.Text;
    using OpenSSL.Core; // I get Native.cs from http://openssl-net.sourceforge.net/
    using Org.BouncyCastle.Utilities.Encoders; // only for Hex.Decode
    
    namespace openssl
    {
    class Program
    {
        private static readonly byte[] _signature =
            Hex.Decode(             "30450221008538ac52dbe2b67148e99f23ad78b4c6c4939a26d789ece590c6f1e44a271454022027d4a09e5e74bb3445019a557bd2202154d2510a4df939b9f4645b311255ee37");
    
        private static readonly byte[] _hello = Encoding.ASCII.GetBytes("hello");
    
        static void Main(string[] args)
        {
    //curve    = pubkey[0:2])[0]                      !!! 714
    //tmplen1  = unpack('!H', pubkey[2:4])[0]         !!!  32
    //pubkey_x = pubkey[4:4 + tmplen1]
    //tmplen2  = unpack('!H', pubkey[4 + tmplen1:4 + tmplen1 + 2])[0]
    //pubkey_y = pubkey[4 + tmplen1 + 2:4 + tmplen1 + 2 + tmplen2]
    
        //private static readonly byte[] _pubkey =
        //  Hex.Decode(
        //      "02ca" +
        //      "0020" +
        //      "012e0e59b564c025b15a587da5d33d3599df5e04deca47c783eaed25ebe5af46" +    !!!!!
        //      "0020" +
        //      "32e00af993efc71a2c033a45187918f5b3c03e0e7bb539cecdc0aaa237717db1");    !!!!!
    
            string pubkey_x = "012e0e59b564c025b15a587da5d33d3599df5e04deca47c783eaed25ebe5af46";
            string pubkey_y = "32e00af993efc71a2c033a45187918f5b3c03e0e7bb539cecdc0aaa237717db1";
    
    //digest = OpenSSL.malloc(0, 64)
    //dgst_len = OpenSSL.pointer(OpenSSL.c_int(0))
            byte[] digest = new byte[64];
            uint dgst_len = 0;
    
    //key = OpenSSL.EC_KEY_new_by_curve_name(curve)
    //if key == 0:
    //  raise Exception("[OpenSSL] EC_KEY_new_by_curve_name FAIL ...")
            IntPtr key = Native.EC_KEY_new_by_curve_name(714);
    
    //pub_key_x = OpenSSL.BN_bin2bn(pubkey_x, len(pubkey_x), 0)
    //pub_key_y = OpenSSL.BN_bin2bn(pubkey_y, len(pubkey_y), 0)
    
            IntPtr pub_key_x;
            Native.BN_hex2bn(out pub_key_x, Encoding.ASCII.GetBytes(pubkey_x));
    
            IntPtr pub_key_y;
            Native.BN_hex2bn(out pub_key_y, Encoding.ASCII.GetBytes(pubkey_y));
    
    //group = OpenSSL.EC_KEY_get0_group(key)
            IntPtr group = Native.EC_KEY_get0_group(key);
    //pub_key = OpenSSL.EC_POINT_new(group)
            IntPtr pub_key = Native.EC_POINT_new(group);
    
    //if (OpenSSL.EC_POINT_set_affine_coordinates_GFp(group, pub_key, pub_key_x, pub_key_y, 0)) == 0:       !!!!!!!!!
    //  raise Exception("[OpenSSL] EC_POINT_set_affine_coordinates_GFp FAIL ...")
            if(Native.EC_POINT_set_affine_coordinates_GFp(group, pub_key, pub_key_x, pub_key_y, IntPtr.Zero)==0)
                throw new Exception("[OpenSSL] EC_POINT_set_affine_coordinates_GFp FAIL ...");
    
    //if (OpenSSL.EC_KEY_set_public_key(key, pub_key)) == 0:
    //  raise Exception("[OpenSSL] EC_KEY_set_public_key FAIL ...")
            if (Native.EC_KEY_set_public_key(key, pub_key) == 0)
                throw new Exception("[OpenSSL] EC_KEY_set_public_key FAIL ...");
    
    //if (OpenSSL.EC_KEY_check_key(key)) == 0:
    //  raise Exception("[OpenSSL] EC_KEY_check_key FAIL ...")
            if (Native.EC_KEY_check_key(key) == 0)
                throw new Exception("[OpenSSL] EC_KEY_check_key FAIL ...");
    
    //md_ctx = OpenSSL.EVP_MD_CTX_create()
            var md_ctx = Native.EVP_MD_CTX_create();
    //OpenSSL.EVP_MD_CTX_init(md_ctx)
            Native.EVP_MD_CTX_init(md_ctx);
    //OpenSSL.EVP_DigestInit(md_ctx, OpenSSL.EVP_ecdsa())
            Native.EVP_DigestInit_ex(md_ctx, Native.EVP_ecdsa(), IntPtr.Zero);
    //if (OpenSSL.EVP_DigestUpdate(md_ctx, inputb, len(inputb))) == 0:
    //  raise Exception("[OpenSSL] EVP_DigestUpdate FAIL ...")
            if (Native.EVP_DigestUpdate(md_ctx, _hello, (uint)_hello.Length)==0)
                throw new Exception("[OpenSSL] EVP_DigestUpdate FAIL ...");
    //OpenSSL.EVP_DigestFinal(md_ctx, digest, dgst_len)
            Native.EVP_DigestFinal_ex(md_ctx, digest, ref dgst_len);
    
    //ret = OpenSSL.ECDSA_verify(0, digest, dgst_len.contents, sig, len(sig), key)
            //key.Verify(null, _signature);
            int ret = Native.ECDSA_verify(0, digest, (int) dgst_len, _signature, _signature.Length, key);
    
            Console.WriteLine(ret == 1);
    
            Console.ReadKey();
    
            //  todo free memory from openssl resource!!!
        }
    }
    

    }