ECDH nodejs 和 C# 密钥交换
ECDH nodejs and C# key exchange
我迷失了自己,我需要帮助才能朝着正确的方向前进:)
我有 nodejs 服务器必须与用 C# 编写的服务器交换一些关键数据,所以在这种情况下,我希望我的数据以某种方式加密。我在考虑 AES,为了安全地交换密钥,我想使用 ECDH,但我不知道如何让它正常工作......如果我想的是正确的方式,我可以让我的 C# "allice" 像这 :
ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP192t1);
var aliceKey = alice.PublicKey;
var ketyToExport = Convert.ToBase64String(aliceKey.ToByteArray());
//send ketyToExport to nodejs
//KEY FROM nodejs
var key1 = Convert.FromBase64String("BIzSJ1dmTXpSS8rqkVFISZ+vumhqXYMWfWoU5vB9GHhOtxxDInu/GZ92VJpqhrE3vw==").ToList();
var keyType = new byte[] { 0x45, 0x43, 0x4B, 0x31 };
var keyLength = new byte[] { 0x20, 0x00, 0x00, 0x00 };
key1.RemoveAt(0);
key1 = keyType.Concat(keyLength).Concat(key1).ToList();
byte[] bobKeyBytes = key1.ToArray();
//and I have a problem with that line bellow, I do not know how to make it work
var aliceSecret = alice.DeriveKeyMaterial(/*ECDiffieHellmanPublicKey bobKeyBytes*/);
而nodejs "Bob" 端是这样的:
const crypto = require("crypto");
const bob = crypto.createECDH('brainpoolP192t1')
const bobKey = bob.generateKeys('base64');
var bobLength = Buffer.from(bobKey, 'base64').length;
//send bobkey to c#
//recive alicekey
var tmp = "RUNLUBgAAAAR9C7kO2o+vxNT/UBvvEuJHNdI8NfU4fUxUT431ER1q3kJbeUVHepoG5SWUM2NHj8="
var aliceKeyBuffer = Buffer.from(tmp, 'base64');
var aliceKey = Buffer.alloc(bobLength)
aliceKeyBuffer.copy(aliceKey, 1, 8);
aliceKey[0] = 4;
bob.computeSecret(aliceKey);
//create aes
//get mesage and iv ...
好的,我已经对所有这些进行了一些调整,但现在我不知道如何处理这条线如何让它发挥作用...
var aliceSecret = alice.DeriveKeyMaterial(/*ECDiffieHellmanPublicKey bobKeyBytes*/);
#大编辑
我在 得到了帮助
现在我有另一个问题 -_-
我的节点 js 代码没有从上面改变,但 c# 看起来像:
using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP256r1))
{
var alicePublicKey = Convert.ToBase64String(alice.PublicKey.ToByteArray());
//NODEJS brainpoolP256r1 publickey
var key1 = Convert.FromBase64String("BASsbkule53ARqlMBA8hYysyyoRi3xGxGnSzIJ2fS5FlLniQD/zYiiGUVydmO/BBkQwVTUo5f4OMCxVNtQ/LuMQ=");
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);
}
var aliceKey = Convert.ToBase64String(derivedKey);
byte[] encryptedMessage = null;
byte[] iv = null;
// Send(aliceKey, "Secret message", out encryptedMessage, out iv);
}
它正在工作,但它给了我不同的密钥......
bob.computeSecret(aliceKey)
我得到了 iIoH9aJoWf3666QQ6X+kj4iUKrk9j+hbRuXbhgs7YzM=
从 alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
我得到了 wJ7O4Hm2Jxs1FcLx6KaMmENvqdTQJPZ/YNSs1+MQDOQ=
如果我的想法正确的话,他们应该是平等的。我是不是想错了?
#编辑完成!!
所以在 js 文件末尾添加这段代码给了我我需要的东西。
const hash = crypto.createHash('sha256');
var tt = bob.computeSecret(aliceKey);
hash.update(tt);
console.log(hash.digest('base64'));
##解决方案##
C#
class Program
{
static void Main(string[] args)
{
using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP256r1))
{
var alicePublicKey = Convert.ToBase64String(alice.PublicKey.ToByteArray());
//send alicePublicKey
var nodejsKey = ""; //NODEJS brainpoolP256r1 publickey base64
byte[] nodejsKeyBytes= Convert.FromBase64String(nodejsKey);
var aliceKey = Convert.ToBase64String(getDeriveKey(nodejsKeyBytes,alice));
byte[] encryptedMessage = null;
byte[] iv = null;
// Send(aliceKey, "Secret message", out encryptedMessage, out iv);
}
}
static byte[] getDeriveKey(byte[] key1, ECDiffieHellman alice)
{
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)
{
return derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
}
}
}
NODEJS
const crypto = require("crypto");
const bob = crypto.createECDH('brainpoolP256r1')
bob.generateKeys();
const bobKey = bob.getPublicKey('base64');
var bobLength = Buffer.from(bobKey, 'base64').length;
//send bobkey to c#
//recive alicekey
var alicePublicKey = "RUNLUCAAAAB/xP7JhSIhYIYAijyC2zHu7obB5CwfK/ynQPxcRAIhBI6OLRRcHyPo61AhfSZN3qA2vGDfWO2mrdWWvqqhVaDf";
var aliceKeyBuffer = Buffer.from(alicePublicKey, 'base64');
var aliceKey = Buffer.alloc(bobLength)
aliceKeyBuffer.copy(aliceKey, 1, 8);
aliceKey[0] = 4;
const hash = crypto.createHash('sha256');
var tt = bob.computeSecret(aliceKey);
var bobSecretKey = hash.update(tt).digest('base64');
非常感谢 @bartonjs and @Maarten Bodewes
以上回答对我帮助很大。我正在使用 secp521r1
/nistP521
在 NodeJS 和 C# 中生成密钥。在我的例子中,调用 alice.PublicKey.ToByteArray()
会导致异常:
Unhandled exception. System.PlatformNotSupportedException: Operation is not supported on this platform.
at System.Security.Cryptography.ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms.ECDiffieHellmanSecurityTransformsPublicKey.ToByteArray()
根据 dotnet 运行时团队记录的问题 here:
ECDiffieHellmanPublicKey.ToByteArray() doesn't have a (standards-)defined export format. The version used by ECDiffieHellmanPublicKeyCng is Windows-specific, and only works for NIST P-256 (secp256r1), NIST P-384 (secp384r1), and NIST P-521 (secp521r1).
To create a copy of a key you should use the ExportParameters() method to obtain the rich ECParameters object; which can then be sent through ECDiffieHellman.Create(ECParameters) and the PublicKey property of that object be read to get back a second instance of ECDiffieHellmanPublicKey.
假设我将我的 X 和 Y 从 Alice 发送给 Bob(我使用 hex 而不是 base64),在 NodeJS 端获得相同共享秘密的相应过程如下所示:
// Alice's public key X, Y coordinates from DotNet Core
const aliceHexX = '00248F624728B17196B22005742C13D80D3DFF75BCA74AF865195E5A29F41C013653B0931BC544245402EDD7922F38692F38DCCF780CF1A9E27D3CFB5E09E53883C0';
const aliceHexY = '0043517F3B2EF33ED70EFA2BC4163E9B99558A7C2ECB7C659A12EA4024633CFA8B9FC997F0A42D30759B4280FDADC13A67A3E7BB0227047C907FAAE92E7716E8A10D';
const alicePublicKeyXBytes = Buffer.from(aliceHexX, 'hex');
const alicePublicKeyYBytes = Buffer.from(aliceHexY, 'hex');
const alicePublicKey = Buffer.concat([Buffer.from([0x04]), alicePublicKeyXBytes, alicePublicKeyYBytes])
const bobSecret = bob.computeSecret(alicePublicKey);
const hash = crypto.createHash('sha256');
hash.update(bobSecret);
const sharedSecret = hash.digest('hex');
console.log(`Shared Secret: ${sharedSecret.toString('hex')}`);
我迷失了自己,我需要帮助才能朝着正确的方向前进:) 我有 nodejs 服务器必须与用 C# 编写的服务器交换一些关键数据,所以在这种情况下,我希望我的数据以某种方式加密。我在考虑 AES,为了安全地交换密钥,我想使用 ECDH,但我不知道如何让它正常工作......如果我想的是正确的方式,我可以让我的 C# "allice" 像这 :
ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP192t1);
var aliceKey = alice.PublicKey;
var ketyToExport = Convert.ToBase64String(aliceKey.ToByteArray());
//send ketyToExport to nodejs
//KEY FROM nodejs
var key1 = Convert.FromBase64String("BIzSJ1dmTXpSS8rqkVFISZ+vumhqXYMWfWoU5vB9GHhOtxxDInu/GZ92VJpqhrE3vw==").ToList();
var keyType = new byte[] { 0x45, 0x43, 0x4B, 0x31 };
var keyLength = new byte[] { 0x20, 0x00, 0x00, 0x00 };
key1.RemoveAt(0);
key1 = keyType.Concat(keyLength).Concat(key1).ToList();
byte[] bobKeyBytes = key1.ToArray();
//and I have a problem with that line bellow, I do not know how to make it work
var aliceSecret = alice.DeriveKeyMaterial(/*ECDiffieHellmanPublicKey bobKeyBytes*/);
而nodejs "Bob" 端是这样的:
const crypto = require("crypto");
const bob = crypto.createECDH('brainpoolP192t1')
const bobKey = bob.generateKeys('base64');
var bobLength = Buffer.from(bobKey, 'base64').length;
//send bobkey to c#
//recive alicekey
var tmp = "RUNLUBgAAAAR9C7kO2o+vxNT/UBvvEuJHNdI8NfU4fUxUT431ER1q3kJbeUVHepoG5SWUM2NHj8="
var aliceKeyBuffer = Buffer.from(tmp, 'base64');
var aliceKey = Buffer.alloc(bobLength)
aliceKeyBuffer.copy(aliceKey, 1, 8);
aliceKey[0] = 4;
bob.computeSecret(aliceKey);
//create aes
//get mesage and iv ...
好的,我已经对所有这些进行了一些调整,但现在我不知道如何处理这条线如何让它发挥作用...
var aliceSecret = alice.DeriveKeyMaterial(/*ECDiffieHellmanPublicKey bobKeyBytes*/);
#大编辑
我在
using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP256r1))
{
var alicePublicKey = Convert.ToBase64String(alice.PublicKey.ToByteArray());
//NODEJS brainpoolP256r1 publickey
var key1 = Convert.FromBase64String("BASsbkule53ARqlMBA8hYysyyoRi3xGxGnSzIJ2fS5FlLniQD/zYiiGUVydmO/BBkQwVTUo5f4OMCxVNtQ/LuMQ=");
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);
}
var aliceKey = Convert.ToBase64String(derivedKey);
byte[] encryptedMessage = null;
byte[] iv = null;
// Send(aliceKey, "Secret message", out encryptedMessage, out iv);
}
它正在工作,但它给了我不同的密钥......
bob.computeSecret(aliceKey)
我得到了 iIoH9aJoWf3666QQ6X+kj4iUKrk9j+hbRuXbhgs7YzM=
从 alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
我得到了 wJ7O4Hm2Jxs1FcLx6KaMmENvqdTQJPZ/YNSs1+MQDOQ=
如果我的想法正确的话,他们应该是平等的。我是不是想错了?
#编辑完成!!
所以在 js 文件末尾添加这段代码给了我我需要的东西。
const hash = crypto.createHash('sha256');
var tt = bob.computeSecret(aliceKey);
hash.update(tt);
console.log(hash.digest('base64'));
##解决方案##
C#
class Program
{
static void Main(string[] args)
{
using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP256r1))
{
var alicePublicKey = Convert.ToBase64String(alice.PublicKey.ToByteArray());
//send alicePublicKey
var nodejsKey = ""; //NODEJS brainpoolP256r1 publickey base64
byte[] nodejsKeyBytes= Convert.FromBase64String(nodejsKey);
var aliceKey = Convert.ToBase64String(getDeriveKey(nodejsKeyBytes,alice));
byte[] encryptedMessage = null;
byte[] iv = null;
// Send(aliceKey, "Secret message", out encryptedMessage, out iv);
}
}
static byte[] getDeriveKey(byte[] key1, ECDiffieHellman alice)
{
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)
{
return derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
}
}
}
NODEJS
const crypto = require("crypto");
const bob = crypto.createECDH('brainpoolP256r1')
bob.generateKeys();
const bobKey = bob.getPublicKey('base64');
var bobLength = Buffer.from(bobKey, 'base64').length;
//send bobkey to c#
//recive alicekey
var alicePublicKey = "RUNLUCAAAAB/xP7JhSIhYIYAijyC2zHu7obB5CwfK/ynQPxcRAIhBI6OLRRcHyPo61AhfSZN3qA2vGDfWO2mrdWWvqqhVaDf";
var aliceKeyBuffer = Buffer.from(alicePublicKey, 'base64');
var aliceKey = Buffer.alloc(bobLength)
aliceKeyBuffer.copy(aliceKey, 1, 8);
aliceKey[0] = 4;
const hash = crypto.createHash('sha256');
var tt = bob.computeSecret(aliceKey);
var bobSecretKey = hash.update(tt).digest('base64');
非常感谢 @bartonjs and @Maarten Bodewes
以上回答对我帮助很大。我正在使用 secp521r1
/nistP521
在 NodeJS 和 C# 中生成密钥。在我的例子中,调用 alice.PublicKey.ToByteArray()
会导致异常:
Unhandled exception. System.PlatformNotSupportedException: Operation is not supported on this platform.
at System.Security.Cryptography.ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms.ECDiffieHellmanSecurityTransformsPublicKey.ToByteArray()
根据 dotnet 运行时团队记录的问题 here:
ECDiffieHellmanPublicKey.ToByteArray() doesn't have a (standards-)defined export format. The version used by ECDiffieHellmanPublicKeyCng is Windows-specific, and only works for NIST P-256 (secp256r1), NIST P-384 (secp384r1), and NIST P-521 (secp521r1).
To create a copy of a key you should use the ExportParameters() method to obtain the rich ECParameters object; which can then be sent through ECDiffieHellman.Create(ECParameters) and the PublicKey property of that object be read to get back a second instance of ECDiffieHellmanPublicKey.
假设我将我的 X 和 Y 从 Alice 发送给 Bob(我使用 hex 而不是 base64),在 NodeJS 端获得相同共享秘密的相应过程如下所示:
// Alice's public key X, Y coordinates from DotNet Core
const aliceHexX = '00248F624728B17196B22005742C13D80D3DFF75BCA74AF865195E5A29F41C013653B0931BC544245402EDD7922F38692F38DCCF780CF1A9E27D3CFB5E09E53883C0';
const aliceHexY = '0043517F3B2EF33ED70EFA2BC4163E9B99558A7C2ECB7C659A12EA4024633CFA8B9FC997F0A42D30759B4280FDADC13A67A3E7BB0227047C907FAAE92E7716E8A10D';
const alicePublicKeyXBytes = Buffer.from(aliceHexX, 'hex');
const alicePublicKeyYBytes = Buffer.from(aliceHexY, 'hex');
const alicePublicKey = Buffer.concat([Buffer.from([0x04]), alicePublicKeyXBytes, alicePublicKeyYBytes])
const bobSecret = bob.computeSecret(alicePublicKey);
const hash = crypto.createHash('sha256');
hash.update(bobSecret);
const sharedSecret = hash.digest('hex');
console.log(`Shared Secret: ${sharedSecret.toString('hex')}`);