Certificate is already installed on machine. Now I want to give read permission on PrivateKey of Certificate to application user.
6 Answers
Here is the Answer.
Created a powershell script file AddUserToCertificate.ps1
Here is the content for script file.
param(
[string]$userName,
[string]$permission,
[string]$certStoreLocation,
[string]$certThumbprint
);
# check if certificate is already installed
$certificateInstalled = Get-ChildItem cert:$certStoreLocation | Where thumbprint -eq $certThumbprint
# download & install only if certificate is not already installed on machine
if ($certificateInstalled -eq $null)
{
$message="Certificate with thumbprint:"+$certThumbprint+" does not exist at "+$certStoreLocation
Write-Host $message -ForegroundColor Red
exit 1;
}else
{
try
{
$rule = new-object security.accesscontrol.filesystemaccessrule $userName, $permission, allow
$root = "c:\programdata\microsoft\crypto\rsa\machinekeys"
$l = ls Cert:$certStoreLocation
$l = $l |? {$_.thumbprint -like $certThumbprint}
$l |%{
$keyname = $_.privatekey.cspkeycontainerinfo.uniquekeycontainername
$p = [io.path]::combine($root, $keyname)
if ([io.file]::exists($p))
{
$acl = get-acl -path $p
$acl.addaccessrule($rule)
echo $p
set-acl $p $acl
}
}
}
catch
{
Write-Host "Caught an exception:" -ForegroundColor Red
Write-Host "$($_.Exception)" -ForegroundColor Red
exit 1;
}
}
exit $LASTEXITCODE
Now run it as part of deployment. Example to running above script in powershell console window.
C:\>.\AddUserToCertificate.ps1 -userName testuser1 -permission read -certStoreLocation \LocalMachine\My -certThumbprint 1fb7603985a8a11d3e85abee194697e9784a253
this example give read permission to user testuser1 on certificate that in installed in \LocalMachine\My and has thumb print 1fb7603985a8a11d3e85abee194697e9784a253
If you are using ApplicationPoolIdentity then you username will be 'IIS AppPool\AppPoolNameHere'
Note: You will need to use ' ' as there is a space between IIS and AppPool.

- 1,644
- 2
- 16
- 16
-
2thanks a lot, great script. if anyone needs to give the full control permission, it is really - _fullcontrol_ – iBobb Nov 22 '17 at 14:41
-
I have an old Win2008 machine which has PowerShell V2. I had to use `$certificateInstalled = Get-ChildItem cert:$certStoreLocation | Where {$_.thumbprint -eq $certThumbprint}` in line 8 (the first statement) – Tony May 09 '18 at 05:35
-
3This no longer works. $_.privatekey returns null now. – Quark Soup Apr 02 '19 at 00:44
-
Thanks this really saved my week, I combine this with this answer https://stackoverflow.com/questions/7334216/iis7-permissions-overview-applicationpoolidentity and was able to set the application pool to read the certificate. – Juan May 18 '20 at 03:07
The accepted answer did not work for me as the $_.privatekey
returned null. I managed to get access to the private key and assign 'Read' permissions for my Application Pool as follows:
param (
[string]$certStorePath = "Cert:\LocalMachine\My",
[string]$AppPoolName,
[string]$certThumbprint
)
Import-Module WebAdministration
$certificate = Get-ChildItem $certStorePath | Where thumbprint -eq $certThumbprint
if ($certificate -eq $null)
{
$message="Certificate with thumbprint:"+$certThumbprint+" does not exist at "+$certStorePath
Write-Host $message -ForegroundColor Red
exit 1;
}else
{
$rsaCert = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($certificate)
$fileName = $rsaCert.key.UniqueName
$path = "$env:ALLUSERSPROFILE\Microsoft\Crypto\Keys\$fileName"
$permissions = Get-Acl -Path $path
$access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule("IIS AppPool\$AppPoolName", 'Read', 'None', 'None', 'Allow')
$permissions.AddAccessRule($access_rule)
Set-Acl -Path $path -AclObject $permissions
}

- 1,502
- 19
- 17
-
Yes. The accepted answer use to work for me, but it's been a while since I ran it. Had the same problem, the $_.privatekey returned null. Thank you. This seems to have fixed it. – Quark Soup Apr 02 '19 at 00:15
-
This doesn't seem to work right anymore either. The $rsaCert and $fileName variables don't seem to contain the correct information, pulling a UniqueName value that simply doesn't exist in a way that the $path variable can use. – nightsurfer Dec 01 '21 at 14:45
It came to my attention a few weeks ago that something changed (I suspect a Windows update) and broke the ability for some certificates to use the CspKeyContainerInfo.UniqueKeyContainerName
property referenced in Michael Armitage's script. Some sleuthing uncovered that Windows decided to start using CNG instead of Crypto Service Provider to protect the key. The following script fixed my issue and should correctly support CNG vs CSP use case scenarios:
$serviceUser = "DOMAIN\Service User"
$certificate = Get-ChildItem Cert:\LocalMachine\My | Where-Object Thumbprint -eq "certificatethumbprint"
$privateKey = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($certificate)
$containerName = ""
if ($privateKey.GetType().Name -ieq "RSACng")
{
$containerName = $privateKey.Key.UniqueName
}
else
{
$containerName = $privateKey.CspKeyContainerInfo.UniqueKeyContainerName
}
$keyFullPath = $env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\" + $containerName;
if (-Not (Test-Path -Path $keyFullPath -PathType Leaf))
{
throw "Unable to get the private key container to set permissions."
}
# Get the current ACL of the private key
$acl = (Get-Item $keyFullPath).GetAccessControl()
# Add the new ACE to the ACL of the private key
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($serviceUser, "Read", "Allow")
$acl.AddAccessRule($accessRule);
# Write back the new ACL
Set-Acl -Path $keyFullPath -AclObject $acl;
You would, of course, want to adapt/enhance this to meet your specific needs.

- 61
- 1
- 1
-
2Nicely done, keep in mind fewer and fewer certs are being stored in the RSA\MachineKeys folder, so you may be better off doing something like `$keyFullPath = Get-ChildItem -Path $env:AllUsersProfile\Microsoft\Crypto -Recurse -Filter $containerName | Select -Expand FullName` – TheMadTechnician Feb 10 '22 at 23:27
-
Ahah, good to know, thanks! I was curious which folders they are transitioning to and came across this document: https://learn.microsoft.com/en-us/windows/win32/seccng/key-storage-and-retrieval I'll update my answer once I have a chance to test your proposed modification! – anxkha Feb 12 '22 at 18:41
-
@anxkha The suggestion of @TheMadTechnician does return the exact same result as your current `$keyFullPath = $env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\" + $containerName;` line above. However, on my Win2k19 Server machine using PS 7.2.4, there's no `.GetAccessControl()` method available: `InvalidOperation: Method invocation failed because [System.IO.FileInfo] does not contain a method named 'GetAccessControl'.` - any help? – Yoda Jun 01 '22 at 15:46
-
1If I remember correctly, PowerShell Core does not have the ability to modify ACLs and one of the symptoms of this is GetAccessControl not existing. Someone else may have had better luck/found a way, but I have not found a way to convince PowerShell Core to know how to modify ACLs and so only the built-in PowerShell 5.4 works for this. Sorry :( – anxkha Jun 03 '22 at 02:11
Adding on Michael Armitage script, This will work for both the cases where PrivateKey value is present and when it is blank
function setCertificatePermission {
param($accountName, $certificate)
if([string]::IsNullOrEmpty($certificate.PrivateKey))
{
$rsaCert = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($certificate)
$fileName = $rsaCert.key.UniqueName
$path = "$env:ALLUSERSPROFILE\Microsoft\Crypto\Keys\$fileName"
$permissions = Get-Acl -Path $path
$access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($accountName, 'FullControl', 'None', 'None', 'Allow')
$permissions.AddAccessRule($access_rule)
Set-Acl -Path $path -AclObject $permissions
} else{
$user = New-Object System.Security.Principal.NTAccount($accountName)
$accessRule = New-Object System.Security.AccessControl.CryptoKeyAccessRule($user, 'FullControl', 'Allow')
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine")
$store.Open("ReadWrite")
$rwCert = $store.Certificates | where {$_.Thumbprint -eq $certificate.Thumbprint}
$csp = New-Object System.Security.Cryptography.CspParameters($rwCert.PrivateKey.CspKeyContainerInfo.ProviderType, $rwCert.PrivateKey.CspKeyContainerInfo.ProviderName, $rwCert.PrivateKey.CspKeyContainerInfo.KeyContainerName)
$csp.Flags = "UseExistingKey","UseMachineKeyStore"
$csp.CryptoKeySecurity = $rwCert.PrivateKey.CspKeyContainerInfo.CryptoKeySecurity
$csp.KeyNumber = $rwCert.PrivateKey.CspKeyContainerInfo.KeyNumber
$csp.CryptoKeySecurity.AddAccessRule($AccessRule)
$rsa2 = New-Object System.Security.Cryptography.RSACryptoServiceProvider($csp)
$store.close()
}
}

- 321
- 2
- 8
As an alternate to above script. You can use PowerShell module. I have not tried it myself but module looks good. http://get-carbon.org/index.html
Here is command to set permissions http://get-carbon.org/Grant-Permission.html

- 1,644
- 2
- 16
- 16
You can use WinHttpCertCfg.exe, a Certificate Configuration Tool Link: https://learn.microsoft.com/en-us/windows/desktop/winhttp/winhttpcertcfg-exe--a-certificate-configuration-tool
Some code example:
Set privatekeyAcces to Svc-LocalAgent$@mydomain.local
*.\WinHttpCertCfg.exe -g -c LOCAL_MACHINE\MY -s *.d365.mydomain.com -a "Svc-LocalAgent$@mydomain.com"*

- 20,585
- 22
- 95
- 108

- 29
- 1
-
Take the time to learn the Powershell version of this from Michael Armitage. – Quark Soup Apr 02 '19 at 00:44
-
1@DonaldAirey why? This is much simpler, if you have the tool and it supports your scenario. – Ohad Schneider Apr 16 '19 at 14:07
-
21. Because Powershell is a general purpose language. 2. Powershell will be around long after WinHttpCertCfg.exe is retired. 3. You solution involves setting the path to include the directory where WinHttpCertCfg.exe lives and, possibly, downloading it. I don't know where this tool is on my machine and I don't have time to go looking for it. – Quark Soup Apr 16 '19 at 15:26
-
1setting permission on certificate where `$_.privatekey` is null, this will throw error of **access denied** – Ankit Patel Jun 23 '20 at 14:48