Как импортировать ключ CNG в хранилище ключей?

Я хотел бы импортировать ключ, который был экспортирован с использованием CngKey.Export(CngKeyBlobFormat.EccPrivateBlob), дать ключу имя и сохранить его в хранилище ключей. Это должно быть так просто, но я не нашел способа сделать это.

Я могу использовать CngKey.Create для создания именованного ключа, и он сохраняется в хранилище ключей, поэтому я могу использовать его позже через CngKey.Open. Если я создаю его с правильными параметрами, я могу использовать CngKey.Export, чтобы экспортировать ключ как EccPrivateBlob и сохранить его в файле. Позже я могу прочитать эти байты обратно из файла и использовать вызов, подобный следующему, для повторного импорта ключа:

CngKey key = CngKey.Import(keyBytes,
       CngKeyBlobFormat.EccPrivateBlob);

Это успешно, но он производит безымянный эфемерный ключ. Как указать имя для ключа, как я могу с помощью Create, чтобы импортированный ключ был сохранен в хранилище ключей?
Я ищу способ заархивировать ключ подписи ECDsa, а затем восстановить его для другого пользователя или ПК. Я не хочу оставлять частный блоб и каждый раз импортировать его - я хочу, чтобы администратор импортировал его только один раз и надежно заблокировал в хранилище ключей.

2 ответа

Как вы правильно сказали, импорт не предоставляет keyName и поэтому создает временный ключ. То, что вам нужно сделать, это вызвать CngKey.Create и предоставить CngProperty в CreationParameters, содержащем приватный большой двоичный объект:

var myKSP = CngProvider.MicrosoftSoftwareKeyStorageProvider;
var blobType = CngKeyBlobFormat.GenericPrivateBlob;
var keyData = Convert.FromBase64String(privateKey);
const bool MachineKey = false;

if (!CngKey.Exists(keyName, myKSP))
{
    var keyParams = new CngKeyCreationParameters
    {
        ExportPolicy = CngExportPolicies.AllowPlaintextExport,
        KeyCreationOptions = (MachineKey) ? CngKeyCreationOptions.MachineKey : CngKeyCreationOptions.None,
        Provider = myKSP
    };
    keyParams.Parameters.Add(new CngProperty(blobType.Format, keyData, CngPropertyOptions.None));

    var key = CngKey.Create(CngAlgorithm.Rsa, keyName, keyParams);
}

Вы можете получить приватный BLOB-объект следующим образом:

var myKSP = CngProvider.MicrosoftSoftwareKeyStorageProvider;
if (CngKey.Exists(keyName, myKSP))
{
    var key = CngKey.Open(keyName);
    var blobType = CngKeyBlobFormat.GenericPrivateBlob;
    var bytes = key.Export(blobType);

    return Convert.ToBase64String(bytes);
}

Наконец-то я смог решить проблему. Основная проблема заключается в том, что ребята из Microsoft написали код таким образом, что при импорте ключ не будет импортирован в хранилище ключей, а вам потребуется заново создать ключ, используя экспортированные байты.

Полный код для экспорта, импорта и тестирования:

Создать Cng Key Parameter и установить его свойства

[System.Security.Cryptography.CngKeyCreationParameters] $cngKeyParameter =  [System.Security.Cryptography.CngKeyCreationParameters]::new()
$cngKeyParameter.KeyUsage = [System.Security.Cryptography.CngKeyUsages]::AllUsages
$cngKeyParameter.ExportPolicy = [System.Security.Cryptography.CngExportPolicies]::AllowPlaintextExport

$cngKeyParameter.Provider = [System.Security.Cryptography.CngProvider]::MicrosoftSoftwareKeyStorageProvider
$cngKeyParameter.UIPolicy = [System.Security.Cryptography.CngUIPolicy]::new([System.Security.Cryptography.CngUIProtectionLevels]::None)
$cngKeyParameter.KeyCreationOptions = [System.Security.Cryptography.CngKeyCreationOptions]::MachineKey

#Create Cng Property for Length, set its value and add it to Cng Key Parameter
[System.Security.Cryptography.CngProperty] $cngProperty = [System.Security.Cryptography.CngProperty]::new($cngPropertyName, [System.BitConverter]::GetBytes(2048), [System.Security.Cryptography.CngPropertyOptions]::None)
$cngKeyParameter.Parameters.Add($cngProperty)

#Create Cng Key for given $keyName using Rsa Algorithm
[System.Security.Cryptography.CngKey] $key = [System.Security.Cryptography.CngKey]::Create([System.Security.Cryptography.CngAlgorithm]::Rsa, "MyRsaKey", $cngKeyParameter)

Write-Output "CNG Key : $globalkeyName - Created"

Экспортировать ключ в файл

 [System.IO.File]::WriteAllBytes("c:\\user\myusername\\keyexport", $key.Export([System.Security.Cryptography.CngKeyBlobFormat]::GenericPrivateBlob));

Импортировать

    $importedKeyBlob = [System.IO.File]::readAllBytes("c:\\user\myusername\\keyexport");
   # [System.Security.Cryptography.CngKey] $importedkey = [System.Security.Cryptography.CngKey]::Import($importedKeyBlob, [System.Security.Cryptography.CngKeyBlobFormat]::GenericPrivateBlob, [System.Security.Cryptography.CngProvider]::MicrosoftSoftwareKeyStorageProvider)

       #Create Cng Key Parameter and set its properties
[System.Security.Cryptography.CngKeyCreationParameters] $cngKeyParameter =  [System.Security.Cryptography.CngKeyCreationParameters]::new()
$cngKeyParameter.KeyUsage = [System.Security.Cryptography.CngKeyUsages]::AllUsages
$cngKeyParameter.ExportPolicy = [System.Security.Cryptography.CngExportPolicies]::AllowPlaintextExport

$cngKeyParameter.Provider = [System.Security.Cryptography.CngProvider]::MicrosoftSoftwareKeyStorageProvider
$cngKeyParameter.UIPolicy = [System.Security.Cryptography.CngUIPolicy]::new([System.Security.Cryptography.CngUIProtectionLevels]::None)
$cngKeyParameter.KeyCreationOptions = [System.Security.Cryptography.CngKeyCreationOptions]::MachineKey

#Create Cng Property for Length, set its value and add it to Cng Key Parameter
[System.Security.Cryptography.CngProperty] $cngProperty = [System.Security.Cryptography.CngProperty]::new($cngPropertyName, [System.BitConverter]::GetBytes(2048), [System.Security.Cryptography.CngPropertyOptions]::None)
$cngKeyParameter.Parameters.Add($cngProperty)

#Create Cng Property for blob, set its value and add it to Cng Key Parameter
[System.Security.Cryptography.CngProperty] $keyBlobProperty = [System.Security.Cryptography.CngProperty]::new([System.Security.Cryptography.CngKeyBlobFormat]::GenericPrivateBlob,$importedKeyBlob , [System.Security.Cryptography.CngPropertyOptions]::None)
$cngKeyParameter.Parameters.Add($keyBlobProperty)

$cngKeyParameter

#Create Cng Key for given $keyName using Rsa Algorithm
[System.Security.Cryptography.CngKey] $key = [System.Security.Cryptography.CngKey]::Create([System.Security.Cryptography.CngAlgorithm]::Rsa, "MyRsaKey", $cngKeyParameter)

$key 

Здесь есть несколько важных моментов:

  1. [System.Security.Cryptography.CngKeyBlobFormat]:: Pkcs8PrivateBlob все еще не работает. Я видел, как парни из MS обсуждают GitHub, что у них есть проблемы в этом формате сообщений.
  2. CngKeyBlobformat должен быть одинаковым для экспорта и импорта, иначе код не будет работать с неизвестной ошибкой
  3. Вы должны запустить ISE powershell с правами администратора

Тестировать:

  • Создать ключ 'MyRsaKey'
  • Проверьте, есть ли ключ в хранилище ключей с помощью PowerShell тестирования, приведенного ниже потока
  • Экспортировать ключ
  • Удалите ключ, используя следующий скрипт powershell
  • еще раз проверьте ключ, если он есть в хранилище ключей
  • Импортируйте и протестируйте снова

Проверьте, есть ли ключ в хранилище ключей

$isKeyExists = [System.Security.Cryptography.CngKey]::Exists("MyRsaKey", [System.Security.Cryptography.CngProvider]::MicrosoftSoftwareKeyStorageProvider, [System.Security.Cryptography.CngKeyCreationOptions]::MachineKey)
Write-Output "CNG Key Exists :: $isKeyExists"

Удалите ключ из хранилища ключей и проверьте, удаляется ли ключ успешно

[System.Security.Cryptography.CngKey] $key = [System.Security.Cryptography.CngKey]::Open($globalkeyName, [System.Security.Cryptography.CngProvider]::MicrosoftSoftwareKeyStorageProvider, [System.Security.Cryptography.CngKeyCreationOptions]::MachineKey)
    $key.Delete()

    $isKeyExists = [System.Security.Cryptography.CngKey]::Exists($globalkeyName, [System.Security.Cryptography.CngProvider]::MicrosoftSoftwareKeyStorageProvider, [System.Security.Cryptography.CngKeyCreationOptions]::MachineKey)
Write-Output "CNG Key Exists :: $isKeyExists"
Другие вопросы по тегам