C# 来自ByteArray的ECDiffieHellman公钥(使用ECDiffieHellman NamedCurves)

C# 来自ByteArray的ECDiffieHellman公钥(使用ECDiffieHellman NamedCurves),c#,cryptography,diffie-hellman,ecdh,public-key-exchange,C#,Cryptography,Diffie Hellman,Ecdh,Public Key Exchange,我正在开发通信节点->c#server。 我需要保护它们之间的连接,所以我选择ECDiffieHellman作为密钥交换机制(nodejs支持它)。我有点问题。。。只是因为我缺乏知识,所以我已经上了课,现在我可以生成密钥并将其导出为base64,nodejs接受c#key没有问题,但另一方面c#。。。甚至连自己的钥匙都不拿。。。 错误System.Security.Cryptography.CryptographyException:“参数不正确。” 是的,我知道我做错了什么 using (EC

我正在开发通信节点->c#server。 我需要保护它们之间的连接,所以我选择ECDiffieHellman作为密钥交换机制(nodejs支持它)。我有点问题。。。只是因为我缺乏知识,所以我已经上了课,现在我可以生成密钥并将其导出为base64,nodejs接受c#key没有问题,但另一方面c#。。。甚至连自己的钥匙都不拿。。。
错误System.Security.Cryptography.CryptographyException:“参数不正确。”
是的,我知道我做错了什么

using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP256r1))
{

    var alicePublicKey = Convert.ToBase64String(alice.PublicKey.ToByteArray());
    //NODEJS brainpoolP256r1 publickey 
    var key1 = Convert.FromBase64String("BB92GQLod55fXEhgNxwQcPQFFvph7eIjnSzdNz2PhzUAOcaPEiLBPQR6AL5pqVLFram8OtPapoBGYZn2vaGl+/U=").ToList();
    //test
    var key2 = Convert.FromBase64String(alicePublicKey);
    var keyType = new byte[] { 0x45, 0x43, 0x4B, 0x50 };
    var keyLength = new byte[] { 0x20, 0x00, 0x00, 0x00 };
    key1.RemoveAt(0);
    key1 = keyType.Concat(keyLength).Concat(key1).ToList();
    byte[] bobKeyBytes = key1.ToArray();
    ECDiffieHellmanPublicKey k = ECDiffieHellmanCngPublicKey.FromByteArray(bobKeyBytes, new CngKeyBlobFormat("ECCPUBLICBLOB")); //error  System.Security.Cryptography.CryptographicException: 'The parameter is incorrect.'
    ECDiffieHellmanPublicKey kk = ECDiffieHellmanCngPublicKey.FromByteArray(key2, new CngKeyBlobFormat("ECCPUBLICBLOB")); // error System.Security.Cryptography.CryptographicException: 'The parameter is incorrect.'
    byte[] aliceKey = alice.DeriveKeyMaterial(k);
    byte[] encryptedMessage = null;
    byte[] iv = null;
    // Send(aliceKey, "Secret message", out encryptedMessage, out iv);
}
你可以在那里找到故事的其余部分

您断言进入
key1
的base64内容是针对brainpoolP256r1的

解码这个值,我们看到它是一个65字节的有效负载,从
04
开始,它看起来像是一个256位素数曲线的未压缩点编码。到目前为止还不错

您甚至已经正确地使用了BCRYPT\u ECDH\u PUBLIC\u GENERIC\u MAGIC,但是如果不指定告诉它是哪条曲线的导入属性,就无法导入“GENERIC named key blob”

从这一点加载密钥的简单方法是

byte[] keyX = new byte[key1.Length / 2];
byte[] keyY = new byte[keyX.Length];
Buffer.BlockCopy(key1, 1, keyX, 0, keyX.Length);
Buffer.BlockCopy(key1, 1 + keyX.Length, keyY, 0, keyY.Length);

ECParameters parameters = new ECParameters
{
    Curve = ECCurve.NamedCurves.brainpoolP256r1,
    Q =
    {
        X = keyX,
        Y = keyY,
    },
};

byte[] derivedKey;

using (ECDiffieHellman bob = ECDiffieHellman.Create(parameters))
using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
{
    derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
}

我已经将
DeriveKeyMaterial
方法扩展为默认的ECDiffieHellmancing方法,因为其他类型的ECDH不支持该方法(由于其行为的低特异性)。

您断言进入
key1
的base64内容是针对brainpoolP256r1的

解码这个值,我们看到它是一个65字节的有效负载,从
04
开始,它看起来像是一个256位素数曲线的未压缩点编码。到目前为止还不错

您甚至已经正确地使用了BCRYPT\u ECDH\u PUBLIC\u GENERIC\u MAGIC,但是如果不指定告诉它是哪条曲线的导入属性,就无法导入“GENERIC named key blob”

从这一点加载密钥的简单方法是

byte[] keyX = new byte[key1.Length / 2];
byte[] keyY = new byte[keyX.Length];
Buffer.BlockCopy(key1, 1, keyX, 0, keyX.Length);
Buffer.BlockCopy(key1, 1 + keyX.Length, keyY, 0, keyY.Length);

ECParameters parameters = new ECParameters
{
    Curve = ECCurve.NamedCurves.brainpoolP256r1,
    Q =
    {
        X = keyX,
        Y = keyY,
    },
};

byte[] derivedKey;

using (ECDiffieHellman bob = ECDiffieHellman.Create(parameters))
using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
{
    derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
}

我已经将
DeriveKeyMaterial
方法扩展为默认的ECDiffieHellmancing方法,因为其他类型的ECDH不支持该方法(由于其行为的低特异性)。

很好,这很有帮助!但我还有一个问题。。。来自nodejs的“computeSecret”和“DeriveKey”是否应具有相同的输出?因为我得到了
//allice C#“wJ7O4Hm2Jxs1FcLx6KaMmENvqdTQJPZ/YNSs1+MQDOQ=“
//boob nodejs”iIoH9aJoWf3666QQ6X+kj4iUKrk9j+hbRuXbhgs7YzM=“
@Janso123看起来cryptojs正在返回原始共享机密,但.NET API只允许返回派生值。如果您计算cryptojs答案的SHA256(在base64之前),您将得到C#答案。很好,这非常有用!但我还有一个问题。。。来自nodejs的“computeSecret”和“DeriveKey”是否应具有相同的输出?因为我得到了
//allice C#“wJ7O4Hm2Jxs1FcLx6KaMmENvqdTQJPZ/YNSs1+MQDOQ=“
//boob nodejs”iIoH9aJoWf3666QQ6X+kj4iUKrk9j+hbRuXbhgs7YzM=“
@Janso123看起来cryptojs正在返回原始共享机密,但.NET API只允许返回派生值。如果计算cryptojs答案的SHA256(在base64之前),则得到C#答案。