自问自答中已经回答了第一个问题,我将只回答第二个问题。
ECDiffieHellmanCng#DeriveKeyMaterial()
并不返回原始密钥协商,而是仅返回从其派生的值。另外,所使用的函数是不可逆的,因此无法确定原始密钥协商。
密钥派生的详细信息在
ECDiffieHellmanCng#KeyDerivationFunction
和
ECDiffieHellmanCng#HashAlgorithm
属性中配置。在发布的代码中,返回了密钥协商的SHA256哈希值(
ECDiffieHellmanKeyDerivationFunction.Hash
,
CngAlgorithm.Sha256
)。
没有选项可以强制返回原始的密钥协商,请参见
这里 和
这里。因此,确定原始密钥协商的唯一选择是使用另一个库或从头开始重新计算密钥协商。
关于第一种变体,BouncyCastle是一种选择。一种可能的实现方式如下:
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;
}
关于第二种变体,需要记住一方的密钥协商是通过将此方的私钥与另一方的公钥相乘所得到的EC点的X坐标来实现的,参见椭圆曲线Diffie-Hellman(使用椭圆曲线算术)。同样,可以使用BouncyCastle进行必要的计算。一个可能的实现如下:
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;
}
如前所述,这两种实现在功能上完全相同,因此可以互换使用。它们可使用以下代码进行测试:
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())
{
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;
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;
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();
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();
}
为简单起见,示例省略了导出公钥(例如,使用ExportSubjectPublicKeyInfo()
)和导入公钥(例如,使用ImportSubjectPublicKeyInfo()
)时的步骤。当代码执行时,例如以下输出结果:
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
在代码中,Alice的原始密钥协定`keyAgreementAlice`通过`GetKeyAgreementBC()`生成,Bob的原始密钥协定`keyAgreementBob`则通过`GetKeyAgreementExplicit()`生成。两种实现方式之间的等价性表明,这两个变量都返回相同的原始密钥协定。
此外,使用`ECDiffieHellmanCng#DeriveKeyMaterial()`导出的(哈希后的)密钥协定,即分别对应于`keyAgreementCngAlice`和`keyAgreementCngBob`的SHA256哈希值,即`keyAgreementSHA256Alice`和`keyAgreementSHA256Bob`,是从原始密钥协定生成的,原因如上所述。