Reputation: 165
I would like to import a key that was exported using CngKey.Export(CngKeyBlobFormat.EccPrivateBlob), give the key a name, and have it persisted in the key store. This should be so simple, but I have not found any way to do it.
I can use CngKey.Create to create a named key, and it is persisted to the key store so I can use it later via CngKey.Open. If I create it with the proper options, I can use CngKey.Export to export the key as an EccPrivateBlob and store that in a file. Later, I can read those bytes back from the file and use a call like the following to reimport the key:
CngKey key = CngKey.Import(keyBytes,
CngKeyBlobFormat.EccPrivateBlob);
That is successful, but it produces an unnamed ephemeral key. How do I supply a name for the key like I can with Create, so the imported key will be stored in the key store?
I am looking for a way to archive an ECDsa signing key and later restore it to a different user or PC. I do not want to leave the private blob laying around and import it each time -- I want an administrator to import it just once and have it securely locked up in the key store.
Upvotes: 5
Views: 4658
Reputation: 1432
As you rightly said, the import does not supply a keyName and therefor it creates an ephemeral key. What you need to do is call CngKey.Create and supply a CngProperty in the CreationParameters containing the private blob:
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);
}
You can get the private blob like this:
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);
}
Upvotes: 5
Reputation: 608
So finally I was able to fix the issue. The main issue is that Microsoft guys have written the code in such a way that an Import will not import the key to the key store rather you need to recreate the key again using the exported bytes.
The full code to export, import and how to test is as following:
[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
Here there are some important points:
To test:
$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"
Upvotes: 1