Вывод ключа с ECDiffieHellmanP256

Я работаю над проектом для интеграции с новым Push API, который существует в Firefox и разрабатывается как стандарт W3C.

Частично это шифрование данных. Сервер получит кривую P256 Diffie Hellman (сгенерированную в 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/info/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 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

System.Security.Cryptography.CryptographicException: запрошенная операция не поддерживается.

Что я не понимаю правильно (или на более грустной стороне, что не реализовано правильно (или вообще) в windows/.NET)?

В качестве альтернативы, если кто-нибудь может объяснить, как переносить эту Node JS-библиотеку в .NET, которая тоже будет работать (я думаю, что немного достичь)

Обновление
Мне нужно было продолжить работу над остальной частью проблемы и не задерживаться в шифровании, поэтому я использовал Wrapper Node.JS, чтобы разрешить дальнейшую разработку на стороне .NET. Код node просто генерирует локальный открытый ключ и секретный секрет и возвращает эти значения мне. Мне все равно нужно, чтобы эта работа работала без оболочки node.

Из-за этого теста я могу подтвердить, что остальная часть кода (не включенная здесь) работает, поэтому проблема определенно лежит в коде выше (и моей неспособности генерировать производный ключевой материал, если HashAlgorithm указан как CngAlgorithm.ECDiffieHellmanP256

Ответы

Ответ 1

Это решение подтверждено только на 64-разрядной версии Windows 10.. Это подтверждено, что он не работает на 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);

Обратите внимание, что я установил значение бессмыслицы в свойство a.Label.

публикация NIST SP 800-108 определяет метку как:

Ярлык - строка, которая идентифицирует цель для производного ключевого материала, который закодирован как двоичная строка.

Я не уверен, что цель должна быть установлена ​​в вашем конкретном контексте. Если кто-то лучше понимает, что это за строка, пожалуйста, оставьте комментарий.

Также обратите внимание, что если вы собираетесь повторно называть эту функцию, вероятно, вы должны сохранить постоянную копию RNGCryptoServiceProvider и использовать ее.

Благодаря комментарию Саймона Моуриера, который заставил меня на правильном пути.