我最近亲自经历了对证书私钥的自动访问。我也发现很多地方告诉我修改硬盘上关键数据的 ACL,但这并不令人满意,因为当我使用 PowerShell 检查私钥的权限时,我添加的用户没有列出。大量的网络搜索、几篇文章以及相当多的尝试和错误使我得出了这一点。
我首先定义用户对象以及我想要授予他们的访问权限:
# Create NTAccount object to represent the account
$AccountName = 'Domain\UserName'
$User = New-Object System.Security.Principal.NTAccount($AccountName)
# Define AccessRule to be added to the private key, could use 'GenericRead' if all you need is read access
$AccessRule = New-Object System.Security.AccessControl.CryptoKeyAccessRule($User, 'FullControl', 'Allow')
然后,我以读/写方式打开本地计算机证书存储,并找到我正在查找的证书:
# Define the thumbprint of the certificate we are interested in
$Thumb = '63CFDDE9A748345CD77C106DAA09B805B33951BF'
# Open Certificate store as read/write
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine")
$store.Open("ReadWrite")
# Look up the certificate's reference object in the store
$RWcert = $store.Certificates | where {$_.Thumbprint -eq $Thumb}
然后,我创建一个新的 CSP(加密服务提供商)参数集,基于现有证书,将新的访问规则添加到参数集中。
# Create new CSP parameter object based on existing certificate provider and key name
$csp = New-Object System.Security.Cryptography.CspParameters($RWcert.PrivateKey.CspKeyContainerInfo.ProviderType, $RWcert.PrivateKey.CspKeyContainerInfo.ProviderName, $RWcert.PrivateKey.CspKeyContainerInfo.KeyContainerName)
# Set flags and key security based on existing cert
$csp.Flags = "UseExistingKey","UseMachineKeyStore"
$csp.CryptoKeySecurity = $RWcert.PrivateKey.CspKeyContainerInfo.CryptoKeySecurity
$csp.KeyNumber = $RWcert.PrivateKey.CspKeyContainerInfo.KeyNumber
# Add access rule to CSP object
$csp.CryptoKeySecurity.AddAccessRule($AccessRule)
然后,我们使用这些参数实例化一个新的 CSP,它将根据我们定义的标志以及我们提供的密钥信息将新的访问规则应用于现有证书。
# Create new CryptoServiceProvider object which updates Key with CSP information created/modified above
$rsa2 = New-Object System.Security.Cryptography.RSACryptoServiceProvider($csp)
然后我们只要关闭证书存储就可以了。
# Close certificate store
$store.Close()
Edit:环顾四周后,我意识到我有几个相同的证书。我认为这是因为使用非 RSA 密码来加密私钥。我使用了一些信息这个答案 https://stackoverflow.com/questions/17185429/how-to-grant-permission-to-private-key-from-powershell/22146915#22146915这解释了如何与第三方 CNG 加密提供商合作。我不喜欢必须下载程序集来执行该答案中的操作,但我使用了一些代码来获取密钥的路径(是的,驱动器上有一个密钥),并添加了 ACL到该文件,该文件确实用于将权利委托给私钥。所以这就是我所做的......
首先,我们验证证书是否具有基于 CNG 的密钥:
[Security.Cryptography.X509Certificates.X509CertificateExtensionMethods]::HasCngKey($Certificate)
如果返回True
然后我们就可以继续前进了。我的也这样,我猜你的也会这样。然后我们通过读取 PrivateKey 数据(在$Certificate
),并得到UniqueName
查找它,然后在 Crypto 文件夹中搜索该文件。
$privateKey = [Security.Cryptography.X509Certificates.X509Certificate2ExtensionMethods]::GetCngPrivateKey($Certificate)
$keyContainerName = $privateKey.UniqueName
$keyMaterialFile = gci $env:ALLUSERSPROFILE\Microsoft\Crypto\*Keys\$keyContainerName
然后,我获取该文件的当前 ACL,创建一个新的 AccessRule 来为所需的用户提供对该文件的访问权限,将该规则添加到我刚刚获取的 ACL,并将更新的 ACL 应用回该文件。
$ACL = Get-Acl $keyMaterialFile
$AccountName = 'Domain\User'
$User = New-Object System.Security.Principal.NTAccount($AccountName)
$AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($User,'FullControl','None','None','Allow')
$ACL.AddAccessRule($AccessRule)
Set-Acl -Path $keyMaterialFile -AclObject $ACL
之后,我能够查看 certlm.msc 并验证用户是否有权使用私钥。
依赖更新:看起来 Microsoft 现在发布了 Security.Cryptography.dll,因此您不必从 GitHub 上编译它。如果安装了 AzureRM 模块,您可以在许多组件模块中找到该 DLL(我从 AzureRM.SiteRecovery 中获取它)。