使用ECDiffieHellmanP256导出密钥

Dan*_*ews 15 c# elliptic-curve diffie-hellman

我正在开发一个项目,与Firefox中存在的新Push API集成,并且正在开发为W3C标准.

部分原因是加密数据.服务器将收到Diffie Hellman P256曲线(使用JS生成var key = subscription.getKey('p256dh');)

转换为.NET base64时的一个例子是

BOAiqZO6ucAzDlZKKhF1aLjNpU8 + R2Pfsz4bQzNpV145D + agNxvLqyu5Q2tLalK2w31RpoDHE8Sipo0m2jiX4WA =

但是我遇到了生成派生材料的问题.

var key1 = Convert.FromBase64String("<stringFromAbove>").ToList() // You can criticize my .toList inefficiencies later

// .NET doesn't like the key without these prefixes. See here
// http://stackoverflow.com/questions/24251336/import-a-public-key-from-somewhere-else-to-cngkey
// I know the bytes don't match that post, but that is because the key type is different between their example and mine.
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();

ECDiffieHellmanCng a = new ECDiffieHellmanCng();
a.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
// If I set this as CngAlgorithm.Sha256 it works, but that's not what Firefox gives me.
a.HashAlgorithm = CngAlgorithm.ECDiffieHellmanP256; 
a.KeySize = 256; // It complains if I don't add this since keys are different lengths.

// Now time to actually import the key
CngKey k = CngKey.Import(key1.ToArray(), CngKeyBlobFormat.EccPublicBlob); // Works successfully
byte[] derivedMaterial = a.DeriveKeyMaterial(k); // Exception Here
Run Code Online (Sandbox Code Playgroud)

System.Security.Cryptography.CryptographicException:不支持请求的操作.

我不能正确理解什么(或者更悲伤的是,在Windows/.NET中没有正确实现(或根本没有))

作为替代方案,如果有人可以解释如何将这个Node JS库移植到.NET也可以工作(我认为这有点可及)

更新
我需要继续解决其余问题而不是加密,所以我使用Node.JS Wrapper来允许在.NET端进一步开发.节点代码只生成本地公钥和共享密钥,并将这些值返回给我.我仍然需要在没有Node包装器的情况下使用它.

由于这个测试我可以确认代码的其余部分(这里没有包含)是有效的,所以问题肯定在于上面的代码(如果将HashAlgorithm指定为,我无法生成派生密钥材料 CngAlgorithm.ECDiffieHellmanP256

Ged*_*tis 5

此解决方案仅在Windows 10 64位上得到确认.它被确认无法在Windows 8.1 64位上运行,并且在其他平台上未经测试.

问题是ECDiffieHellmanP256不是哈希算法,而是指定使用哈希键派生函数.您KeyDerivationFunction应该设置为ECDiffieHellmanKeyDerivationFunction.Tls,并且需要为KDF指定种子和标签.

您的固定代码如下所示:

var key1 = Convert.FromBase64String("BOAiqZO6ucAzDlZKKhF1aLjNpU8+R2Pfsz4bQzNpV145D+agNxvLqyu5Q2tLalK2w31RpoDHE8Sipo0m2jiX4WA=").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();

ECDiffieHellmanCng a = new ECDiffieHellmanCng();
a.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Tls;

byte[] label = new byte[32];
string labelStr = "The purpose";
Encoding.ASCII.GetBytes(labelStr, 0, labelStr.Length, label, 0);
a.Label = label;

byte[] seed = new byte[32];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(seed);
a.Seed = seed;

a.HashAlgorithm = CngAlgorithm.ECDiffieHellmanP256;
a.KeySize = 256;

CngKey k = CngKey.Import(key1.ToArray(), CngKeyBlobFormat.EccPublicBlob);
byte[] derivedMaterial = a.DeriveKeyMaterial(k);
Run Code Online (Sandbox Code Playgroud)

请注意,我为该a.Label属性设置了一个无意义的值.

NIST SP 800-108出版物定义了标签为:

Label - 标识派生密钥材料用途的字符串,编码为二进制字符串.

我不确定在特定情况下应该设定的目的是什么.如果有人更好地理解这个字符串应该是什么,请发表评论.

另请注意,如果您要重复调用此函数,则应该保留该函数的持久副本RNGCryptoServiceProvider并使用它.

感谢Simon Mourier评论让我走上正轨.