Импорт сертификата на основе ECC из хранилища сертификатов Windows в CngKey

Как я могу получить открытые/закрытые ключи из X509Certificate2 на основе ECC в CngKey для использования с ECDsaCng и ECDiffieHellmanCng?

В настоящее время я использую пары ключей RSA 2048 для подписи/шифрования. Я делаю это, извлекая сертификаты из X509Store, где они надежно хранятся с закрытыми ключами, помеченными как неэкспортируемые. Я хотел бы преобразовать текущую реализацию для использования ECDSA и ECDH, чтобы я мог использовать ключи меньшего размера для эквивалентной безопасности.

Я успешно сгенерировал сертификаты ECC с помощью openssl:

  1. openssl ecparam -out private.pem -name prime256v1 -genkey
  2. openssl req -new -key private.pem -x509 -nodes -days 365 -out public.cer
  3. openssl pkcs12 -export -in public.cer -inkey private.pem -out export.pfx

Я успешно установил сгенерированные выше сертификаты в хранилище сертификатов. Я могу получить их по отпечатку пальца, но поставщики криптографии для закрытых и открытых ключей выдают исключения «Алгоритм не поддерживается». Вместо этого я понимаю, что должен использовать ECDsaCng и ECDiffieHellmanCng для подписи/шифрования. Но это дело в CngKey годах.

Bouncy Castle не подходит, потому что он требует, чтобы закрытые ключи можно было экспортировать.

CLR Security вернет мне пару CngKey через GetCngPrivateKey, но ее нельзя использовать с ECDsa, поскольку ключ, возвращаемый CLRSecurity, является ключом ECDH. Кроме того, CLR Security не дает мне возможности получить только открытый ключ от X509Certificate2 для проверки подписи (где у меня даже нет или не нужен закрытый ключ подписавшего).

Есть идеи? Я в отчаянии... Мы будем много признательны за любую помощь.


person Dan Turner    schedule 31.03.2014    source источник


Ответы (1)


Вам нужно создать CngKey из открытого ключа сертификата (certificate.PublicKey.EncodedKeyValue.RawData):

CngKey содержит 8 дополнительных байтов, первые 4 байта (так называемые магические) используются для имени используемой кривой (для ECDsa: ECS1, ECS3 или ECS5, для ECDH: ECK1, ECK3, ECK5), последние 4 являются длина ключа вкл. набивка (32, 48 или 66).

Первый байт открытого ключа из сертификата удаляется (так как для открытого ключа ECDSA он всегда равен 0x04).

Так, например, для ECDSA с использованием кривой P-256 и алгоритма хеширования SHA-256 вы получите открытый ключ длиной 65 байт. Отбросить первый байт, оставив 64 байта, затем префикс с 4 байтами для кривой и 4 байта для длины ключа, т.е.:

var keyData = certificate.PublicKey.EncodedKeyValue.RawData.Skip(1).ToArray();
var keySize = BitConverter.GetBytes(keyData.Length/2);
var magic = Encoding.ASCII.GetBytes("ECS1").Concat()

var eccPublicBlobValue = magic.Concat(keySize).Concat(keyData).ToArray();

Теперь у вас есть открытый ключ (72 байта) для создания CngKey из:

var cngKey = CngKey.Import(eccPublicBlobValue, CngKeyBlobFormat.EccPublicBlob);

var ecdsaCng = new ECDsaCng(cngKey);

И вы можете проверить подпись:

return ecdsaCng.VerifyData(encodedBytes, signature);
person Nicolas G.    schedule 15.06.2014
comment
Но разве проблема не в том, что certificate.PublicKey выдает, если ключ не основан на RSA/DSA? - person EricLaw; 12.09.2014
comment
@EricLaw, нет, не так. Приведенный выше код работает. - person Christian Davén; 05.02.2021
comment
Теперь я могу импортировать ключ (спасибо!), но verifyData говорит, что он недействителен. Какой CngAlgorithm вы указываете? - person McMurphy; 16.03.2021