Reputation: 143
I want to use Diffie hellman for generating a secret key between a c# Server and c++ Client. this code generates a public key for the server:
serverECDH = new ECDiffieHellmanCng(ECCurve.NamedCurves.nistP256);
serverECDH.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
serverECDH.HashAlgorithm = CngAlgorithm.Sha256;
ECDHPublicKey =Convert.ToBase64String(serverECDH.PublicKey.ToByteArray());
Console.WriteLine(serverECDH.KeySize); //256
Console.WriteLine(serverECDH.PublicKey.ToByteArray().Length); //72
Console.WriteLine(ECDHPublicKey);
I wonder why publicKey Byte Array Size is 72 while I expect a 64 byte long array? besides here is my implementation for generating public key in Client:
if (NULL == (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))) HandleErrors(1);
if (1 != EVP_PKEY_paramgen_init(pctx)) HandleErrors(2);
if (1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1)) HandleErrors(3);
if (!EVP_PKEY_paramgen(pctx, ¶ms)) HandleErrors(4);
if (NULL == (kctx = EVP_PKEY_CTX_new(params, NULL))) HandleErrors(5);
if (1 != EVP_PKEY_keygen_init(kctx)) HandleErrors(6);
if (1 != EVP_PKEY_keygen(kctx, &pkey)) HandleErrors(7);
bio = BIO_new(BIO_s_mem());
PEM_write_bio_PUBKEY(bio, pkey);
int publicKeyLen = BIO_pending(bio);
cout << publicKeyLen << endl;
unsigned char* publicKeyChar = (unsigned char*)malloc(publicKeyLen);
BIO_read(bio, publicKeyChar, publicKeyLen);
ECDHPublicKey = string(reinterpret_cast<char const*>(publicKeyChar),publicKeyLen);
cout << ECDHPublicKey << endl;
in this code, the public key length is 128 byte(Characters?) which is odd again because I specify NID_X9_62_prime256v1 curve. where is my mistake? besides are NID_X9_62_prime256v1 and nistP256 matching in c++ and c#?
EDIT: here is a example of cout << ECDHPublicKey << endl output :
-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUFD9ZNby6x2bf/VC16/NHSxIXdit Ips60uLoi0/jKmbmMHRg2xbXVVzV8Uc1DElMlZA817bMFCnVvi1VsM5JYg==
-----END PUBLIC KEY-----
Upvotes: 2
Views: 1677
Reputation: 93958
In the C++ code that public key is not just DER encoded, it is PEM encoded as well. That adds a header and footer around the base 64 encoded DER structure. PEM is used for text-based interface where binary data may get distorted. It is therefore also sometimes called "ASCII armor" (or "ASCII armour" depending on your dialect).
If you want to take a look at the structures of the C++ / OpenSSL code then simply paste base 64 encoded DER structure into this site or use openssl asn1parse
.
The Mickeysoft structure returned by serverECDH.PublicKey.ToByteArray()
seems to consist of "ECK1" + 20 00 00 00
followed by the two statically sized encoded coordinates (which could be big or little endian). ECK1 is probably Elliptic Curve Key format 1 - whatever that is - and 20 00 00 00
is probably the key / coordinate size in octets as 32 * 8 = 256. Note that C# officially uses platform endianess, so the 20 00 00 00
is a 32 bit little endian value. Probably you need an external library to encode their keys so that any other runtime can understand them.
As for your tag-on question: yes, the ANS(I) X9.62 prime256r1 curve is identical to NIST P-256 and secp256r1 as (I think) originally defined by Certicom corp. The curve was first defined and then picked up & standardized by various organizations.
Upvotes: 1