I'm trying to generate a shared secret between a web server running PHP and a C# desktop application. I'm aware of the BouncyCastle library, but I'd prefer not having to use it since it's pretty huge.
I'm using phpecc and ECDiffieHellmanCng and trying to generate a shared secret between the two parties but I'm having issues with exporting/importing in C#.
It seems phpecc requires der/pem format in order to import a key, and ECDiffieHellmanCng doesn't seem to have any easy way to export in a compatible format.
Would I need to write my own pem/der encoder and decoder in order to do this or is there some alternative easier way?
Currently I'm doing the following in C#:
using (var ecdh = new ECDiffieHellmanCng())
{
ecdh.HashAlgorithm = CngAlgorithm.ECDiffieHellmanP384;
ecdh.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
var encoded = EncodePem(ecdh.PublicKey.ToByteArray());
//... do something with encoded
}
private static string EncodePem(byte[] data)
{
var pemDat = new StringBuilder();
var chunk = new char[64];
pemDat.AppendLine("-----BEGIN PUBLIC KEY-----");
var encodedData = Convert.ToBase64String(data);
for (var i = 0; i < encodedData.Length; i += chunk.Length)
{
var index = 0;
while (index != chunk.Length && i + index < encodedData.Length)
{
chunk[index] = encodedData[i + index];
index++;
}
pemDat.AppendLine(new string(chunk));
}
pemDat.AppendLine("-----END PUBLIC KEY-----");
return pemDat.ToString();
}
Obviously the above is only doing the pem encoding, so on the php side it returns an error when it's trying to parse it:
Type: Runtime
Exception Message: Invalid data.
File: /.../vendor/mdanter/ecc/src/Serializer/PublicKey/Der/Parser.php
Line: 49
.NET Core 1.0 and .NET Framework 4.7 have the ECParameters struct to import/export keys. The
ToByteArray()
method you called is producing a CNG EccPublicBlob which has very little to do with the SEC-1 ECParameters format.I'm going to assume that you wanted to use secp384r1/NIST P-384, even though you specified that as a hash algorithm. If you want some other curve, you'll need to do some translations.
The (.NET) ECParameters struct will only help you get started. Turning that into a file requires translating it into a PEM-encoded DER-encoded ASN.1-based structure. (But if you're sticking with NIST P-256/384/521, you can do it with the byte[] you currently have)
In SEC 1 v2.0 we get the following structures:
Distilling this down to the relevant parts, you need to write
The AlgorithmIdentifier contains data that's fixed given you don't change the curve:
and we can now count how many bytes were in the payload: 16 (0x10), so we fill in the length:
The public key encoding that everyone understands is "uncompressed point", which is
Turns out, that has a fixed size for a given curve, too, so unlike most things that are DER encoded, you can do this in one pass :). For secp384r1 the x and y coordinate are each 384 bit values, or (384 + 7)/8 == 48 bytes, so the ECPoint is 48 + 48 + 1 == 97 (0x61) bytes. Then it needs to be wrapped in a BIT STRING, which adds one payload byte and the length and tag. So, we get:
Running the output I got from that into OpenSSL: