Получить ключ с помощью ECDiffieHellmanP256

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

Частично это шифрование данных. Сервер получит кривую Диффи-Хеллмана 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://stackru.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

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

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

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

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

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

1 ответ

Решение

Это решение подтверждено только на Windows 10 64-битной. Подтверждено, что он не работает в 64-битной Windows 8.1 и не тестировался на других платформах.

Проблема в том, что 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 и использовать это.

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

KeyDerivationFunction - это пост-обработка: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecdiffiehellmancng?view=netframework-4.7

так что, возможно, это вычисление главного секрета https://tools.ietf.org/html/rfc5246

ECDiffieHellmanCng.Label = Encoding.ASCII.GetBytes("master secret");
ECDiffieHellmanCng.Seed = clientRandom.Concat(serverRandom).ToArray();
//there is also a function like this:
//ECDiffieHellmanCng.DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey, byte[] prfLabel, byte[] prfSeed)

так Label а также Seed должен использоваться в PRF, что, я думаю, довольно странно, почему ECDiffieHellman все равно делает PRF

извините, я должен был добавить комментарий к принятому ответу, но you must have 50 reputation to comment

Другие вопросы по тегам