I'm trying to find a way to grant permissions for private key from powershell script. Certificate is stored in CNG. All ideas are welcome.
-
Did you ever resolve this? I can see how to set it on the filesystem, but not within the certificate management itself... – LDJ Feb 21 '14 at 14:38
3 Answers
The answer above is technically correct however it did not help me when I was looking for the same thing because it fails to mention that you need to use assemblies loaded from the CLRSecurity project on codeplex https://clrsecurity.codeplex.com/.
Here is an extract of how I achieved the same thing including loading the CLR Security assembly that you need to use Security.Cryptography.dll. There are a couple of function declarations that are needed first. I have these included in modules however you can use them as you wish.
Function Load-Assembly()
{
[CmdletBinding(PositionalBinding=$false)]
param(
[Parameter(Mandatory)][string][ValidateScript({Test-Path $_})] $DirectoryPath,
[Parameter(Mandatory)][string][ValidateNotNullOrEmpty()] $Name
)
$assemblyFileNameFullPath = Join-Path -Path $DirectoryPath -ChildPath $Name
If (Test-Path -Path $assemblyFileNameFullPath -PathType Leaf)
{
Write-Verbose "Loading .NET assembly from path ""$assemblyFileNameFullPath"""
#Load the assembly using the bytes as this gets around security restrictions that stop certain assemblies from loading from external sources
$assemblyBytes = [System.IO.File]::ReadAllBytes($assemblyFileNameFullPath)
$assemblyLoaded = [System.Reflection.Assembly]::Load($assemblyBytes);
if ($assemblyLoaded -ne $null)
{
return $assemblyLoaded
}
else
{
Throw "Cannot load .NET assembly ""$Name"" from directory ""$DirectoryPath"""
}
}
else
{
Write-Error "Cannot find required .NET assembly at path ""$assemblyFileNameFullPath"""
}
}
Function Get-PrivateKeyContainerPath()
{
[CmdletBinding(PositionalBinding=$false)]
Param(
[Parameter(Mandatory=$True)][string][ValidateNotNullOrEmpty()] $Name,
[Parameter(Mandatory=$True)][boolean] $IsCNG
)
If ($IsCNG)
{
$searchDirectories = @("Microsoft\Crypto\Keys","Microsoft\Crypto\SystemKeys")
}
else
{
$searchDirectories = @("Microsoft\Crypto\RSA\MachineKeys","Microsoft\Crypto\RSA\S-1-5-18","Microsoft\Crypto\RSA\S-1-5-19","Crypto\DSS\S-1-5-20")
}
foreach ($searchDirectory in $searchDirectories)
{
$machineKeyDirectory = Join-Path -Path $([Environment]::GetFolderPath("CommonApplicationData")) -ChildPath $searchDirectory
$privateKeyFile = Get-ChildItem -Path $machineKeyDirectory -Filter $Name -Recurse
if ($privateKeyFile -ne $null)
{
return $privateKeyFile.FullName
}
}
Throw "Cannot find private key file path for key container ""$Name"""
}
#Extracted code of how to obtain the private key file path (taken from a function)
#Requires an x509Certificate2 object in variable $Certificate and string variable $CertificateStore that contains the name of the certificate store
#Need to use the Security.Cryptography assembly
$assembly = Load-Assembly -DirectoryPath $PSScriptRoot -Name Security.Cryptography.dll
#Uses the extension methods in Security.Cryptography assembly from (https://clrsecurity.codeplex.com/)
If ([Security.Cryptography.X509Certificates.X509CertificateExtensionMethods]::HasCngKey($Certificate))
{
Write-Verbose "Private key CSP is CNG"
$privateKey = [Security.Cryptography.X509Certificates.X509Certificate2ExtensionMethods]::GetCngPrivateKey($Certificate)
$keyContainerName = $privateKey.UniqueName
$privateKeyPath = Get-PrivateKeyContainerPath -Name $keyContainerName -IsCNG $true
}
elseif ($Certificate.PrivateKey -ne $null)
{
Write-Verbose "Private key CSP is legacy"
$privateKey = $Certificate.PrivateKey
$keyContainerName = $Certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
$privateKeyPath = Get-PrivateKeyContainerPath -Name $keyContainerName -IsCNG $false
}
else
{
Throw "Certificate ""$($Certificate.GetNameInfo("SimpleName",$false))"" in store ""$CertificateStore"" does not have a private key, or that key is inaccessible, therefore permission cannot be granted"
}
Sorry if this seems like a repeat from above, as I said it does use the same technique but hopefully others may find this more useful since it explains how to use the methods in the CLR Security project including how to load the assembly.

- 1,718
- 1
- 17
- 21
-
This is an awsome post. For those trying to find the referenced DLL, check the following: https://github.com/MicrosoftArchive/clrsecurity. CodePlex was shutdown last year. – Adam Apr 19 '18 at 13:41
-
This answer is puzzling! What are `HasCngKey` and `GetCngPrivateKey`? No idea whatsoever. – Jari Turkia Apr 08 '19 at 17:48
-
@JariTurkia Those are static methods defined in the Security.Cryptography.dll which you need to import from the referenced project in GitHub. The basic functionality in .NET does not handle CNG keys so id you need to work with those keys you need to use this code. – CarlR Aug 20 '19 at 13:48
-
Ok, got it. I was researching handling X.509 private keys on PowerShell Core which can run on Windows or Linux. Huge differences between platforms make that very tricky. – Jari Turkia Aug 21 '19 at 06:15
Cmdlet code for getting private key filename.
[Cmdlet("Get", "PrivateKeyName")]
public class GetKeyNameCmdlet : Cmdlet
{
[Parameter(Position = 0, Mandatory = false)]
public X509Certificate2 Cert;
protected override void ProcessRecord()
{
WriteObject(GetUniqueKeyName(Cert));
}
private static string GetUniqueKeyName(X509Certificate2 cert)
{
if (cert == null)
throw new ArgumentNullException("cert");
var cngPrivateKey = cert.GetCngPrivateKey();
if (cngPrivateKey != null)
return cngPrivateKey.UniqueName;
var rsaPrivateKey = cert.PrivateKey as RSACryptoServiceProvider;
if (rsaPrivateKey != null)
return rsaPrivateKey.CspKeyContainerInfo.UniqueKeyContainerName;
throw new Exception("cert");
}
}
using cmdlet. CngCrypt.dll - dll with cmdlet code.
Import-Module .\CngCrypt.dll
$local:certificateRootPath = join-path $env:ALLUSERSPROFILE '\Microsoft\Crypto\RSA\MachineKeys\'
$WorkingCert = Get-ChildItem CERT:\LocalMachine\My |where {$_.Subject -match 'Test'}| sort
Get-PrivateKeyName ($WorkingCert)

- 553
- 1
- 5
- 19
If you have certificate already installed on machine/server and just looking for how to give permission to specific user using powershell.
Here is the answer How to Grant permission to user on Certificate private key using powershell?

- 1
- 1

- 1,644
- 2
- 16
- 16