Получить ключ с помощью 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