Since the first question has been answered by yourself in the meantime, I will only address the second question.
ECDiffieHellmanCng#DeriveKeyMaterial()
does not return the raw key agreement, but only a value derived from it. Also, the functions used are irreversible, so the original key agreement cannot be determined.
The details of the key derivation are configured in the ECDiffieHellmanCng#KeyDerivationFunction
and ECDiffieHellmanCng#HashAlgorithm
properties. In the posted code, the SHA256 hash of the key agreement is returned (ECDiffieHellmanKeyDerivationFunction.Hash
, CngAlgorithm.Sha256
).
There is no option that forces the raw key agreement to be returned, see also here and here. Therefore, to determine the raw key agreement, there is no other choice than to use another library or to recalculate the key agreement from scratch.
Regarding the first variant, BouncyCastle is an option. One possible implementation is:
private static byte[] GetKeyAgreementBC(X9ECParameters ecParams, ECPoint publicKey, byte[] privateKey)
{
ECDomainParameters eCDomainParameters = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N);
Org.BouncyCastle.Math.EC.ECCurve curve = eCDomainParameters.Curve;
Org.BouncyCastle.Math.EC.ECPoint pubKey = curve.CreatePoint(new BigInteger(1, publicKey.X), new BigInteger(1, publicKey.Y));
BigInteger privKey = new BigInteger(1, privateKey);
ECPublicKeyParameters ecPubKeyParams = new ECPublicKeyParameters("ECDH", pubKey, SecObjectIdentifiers.SecP256r1);
ECPrivateKeyParameters ecPrivKeyParams = new ECPrivateKeyParameters(privKey, eCDomainParameters);
IBasicAgreement basicAgreement = AgreementUtilities.GetBasicAgreement("ECDH");
basicAgreement.Init(ecPrivKeyParams);
byte[] keyAgreement = basicAgreement.CalculateAgreement(ecPubKeyParams).ToByteArrayUnsigned();
return keyAgreement;
}
Regarding the second variant it has to be kept in mind that the key agreement of one side is the X coordinate of the EC point which is obtained by multiplying the private key of this side with the public key of the other side, see Elliptic-curve Diffie-Hellman (using the arithmetic of elliptic curves). Again, BouncyCastle can be used with respect to the necessary calculations. A possible implementation is:
private static byte[] GetKeyAgreementExplicit(X9ECParameters ecParams, ECPoint publicKey, byte[] privateKey)
{
ECDomainParameters eCDomainParameters = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N);
Org.BouncyCastle.Math.EC.ECCurve curve = eCDomainParameters.Curve;
Org.BouncyCastle.Math.EC.ECPoint pubKey = curve.CreatePoint(new BigInteger(1, publicKey.X), new BigInteger(1, publicKey.Y));
BigInteger privKey = new BigInteger(1, privateKey);
Org.BouncyCastle.Math.EC.ECPoint keyAgreementECPoint = pubKey.Multiply(privKey).Normalize();
byte[] keyAgreement = keyAgreementECPoint.XCoord.ToBigInteger().ToByteArrayUnsigned();
return keyAgreement;
}
As said, both implementations are functionally identical and therefore interchangeable. They can be tested with the following code:
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Security.Cryptography;
...
using (var ecdhAlice = new ECDiffieHellmanCng())
using (var ecdhBob = new ECDiffieHellmanCng())
{
// Generate Alice's private and public key
ecdhAlice.HashAlgorithm = CngAlgorithm.Sha256;
ecdhAlice.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
ecdhAlice.GenerateKey(ECCurve.NamedCurves.nistP256);
byte[] privateKeyAlice = ecdhAlice.ExportParameters(true).D;
ECPoint publicKeyAlice = ecdhAlice.ExportParameters(false).Q;
// Generate Bob's private and public key
ecdhBob.HashAlgorithm = CngAlgorithm.Sha256;
ecdhBob.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
ecdhBob.GenerateKey(ECCurve.NamedCurves.nistP256);
byte[] privateKeyBob = ecdhBob.ExportParameters(true).D;
ECPoint publicKeyBob = ecdhBob.ExportParameters(false).Q;
// Alice's key agreement
byte[] keyAgreementAlice = GetKeyAgreementBC(NistNamedCurves.GetByName("P-256"), publicKeyBob, privateKeyAlice);
byte[] keyAgreementSHA256Alice = SHA256.Create().ComputeHash(keyAgreementAlice);
byte[] keyAgreementCngAlice = ecdhAlice.DeriveKeyMaterial(ecdhBob.PublicKey);
Console.WriteLine("Alice's raw key agreement (BC): " + Hex.ToHexString(keyAgreementAlice));
Console.WriteLine("Alice's hashed key agreement (BC): " + Hex.ToHexString(keyAgreementSHA256Alice));
Console.WriteLine("Alice's key agreement (.NET): " + Hex.ToHexString(keyAgreementCngAlice));
Console.WriteLine();
// Bob's key agreement
byte[] keyAgreementBob = GetKeyAgreementExplicit(NistNamedCurves.GetByName("P-256"), publicKeyAlice, privateKeyBob);
byte[] keyAgreementSHA256Bob = SHA256.Create().ComputeHash(keyAgreementBob);
byte[] keyAgreementCngBob = ecdhBob.DeriveKeyMaterial(ecdhAlice.PublicKey);
Console.WriteLine("Bob's raw key agreement (explicit): " + Hex.ToHexString(keyAgreementBob));
Console.WriteLine("Bob's hashed key agreement (explicit): " + Hex.ToHexString(keyAgreementSHA256Bob));
Console.WriteLine("Bob's key agreement (.NET): " + Hex.ToHexString(keyAgreementCngBob));
Console.WriteLine();
}
For simplicity, the example omitted the steps where the public key is exported (e.g. with ExportSubjectPublicKeyInfo()
) and imported (e.g. with ImportSubjectPublicKeyInfo()
). When the code is executed, e.g. the following output results:
Alice's raw key agreement (BC): d6f337d4c0d8e8bb34848d4f0c1c6834f66f69bbf9f284df5b87c7aee0584fc7
Alice's hashed key agreement (BC): fb95a6b3b95d0882fa6796c28aa5f1a88d14c5b9a3f302b5deae50316cb7a273
Alice's key agreement (.NET): fb95a6b3b95d0882fa6796c28aa5f1a88d14c5b9a3f302b5deae50316cb7a273
Bob's raw key agreement (explicit): d6f337d4c0d8e8bb34848d4f0c1c6834f66f69bbf9f284df5b87c7aee0584fc7
Bob's hashed key agreement (explicit): fb95a6b3b95d0882fa6796c28aa5f1a88d14c5b9a3f302b5deae50316cb7a273
Bob's key agreement (.NET): fb95a6b3b95d0882fa6796c28aa5f1a88d14c5b9a3f302b5deae50316cb7a273
In the code, Alice's raw key agreement keyAgreementAlice
is generated with GetKeyAgreementBC()
and Bob's raw key agreement keyAgreementBob
is generated with GetKeyAgreementExplicit()
. The equivalence of both implementations is shown by the fact that both variants return the same raw key agreement.
Furthermore, the (hashed) key agreement of ECDiffieHellmanCng#DeriveKeyMaterial()
, i.e. keyAgreementCngAlice
and keyAgreementCngBob
, respectively, results when the SHA256 hash, i.e. keyAgreementSHA256Alice
and keyAgreementSHA256Bob
, respectively, is generated from the raw key agreement, for the reasons stated above.