Search code examples
c#.netcryptographybouncycastle

ECDH public and private keys generation with .Net C#


I am porting nodejs code to .Net and I am stuck at this part where I need to generate public and private keys.

Javascript code:

const dh = crypto.createECDH('prime256v1');
let privk = dh.getPrivateKey();
let pubk = dh.getPublicKey();

I tried the same with .Net C# with

var ecdh = new ECDiffieHellmanCng(CngKey.Create(CngAlgorithm.ECDiffieHellmanP256, null, new CngKeyCreationParameters { ExportPolicy = CngExportPolicies.AllowPlaintextExport }));

var privateKey = ecdh.Key.Export(CngKeyBlobFormat.EccPrivateBlob);
var publickey = ecdh.Key.Export(CngKeyBlobFormat.EccPublicBlob);

However when I tried to exchange those keys generated with C# with the Google FCM server, I got invalid argument error. When I copy the generated byte[] array from nodejs to .Net C# code as constants it works. It is obvious that generated keys are not meeting the requirement of the server. Since I am working with undocumented interface I can't tell why the keys are not accepted. I can see that the keys generated with nodejs are in 32 bytes in length for the private key and 65 bytes in length for the public key. The keys generated from C# are 140 and 96 bytes in length. How to generate keys in C# to match the key properties in nodejs?


Solution

  • I was able to solve my problem using Bouncy Castle

                ECKeyPairGenerator gen = new ECKeyPairGenerator("ECDH");
                SecureRandom secureRandom = new SecureRandom();
                X9ECParameters ecp = NistNamedCurves.GetByName("P-256");
                ECDomainParameters ecSpec = new ECDomainParameters(ecp.Curve, ecp.G, ecp.N, ecp.H, ecp.GetSeed());
                ECKeyGenerationParameters ecgp = new ECKeyGenerationParameters(ecSpec, secureRandom);
                gen.Init(ecgp);
                AsymmetricCipherKeyPair eckp = gen.GenerateKeyPair();
    
                ECPublicKeyParameters ecPub = (ECPublicKeyParameters)eckp.Public;
                ECPrivateKeyParameters ecPri = (ECPrivateKeyParameters)eckp.Private;
    
                byte[] publicKeyBytes = ecPub.Q.GetEncoded();