我使用以下PowerShell功能将PFX导入我的Windows 2008 R2服务器的证书存储区
function Import-PfxCertificate ([String]$certPath,[String]$certificateStoreLocation = "CurrentUser",[String]$certificateStoreName = "My",$pfxPassword = $null)
{
$pfx = new-object System.Security.Cryptography.X509Certificates.X509Certificate2
$pfx.Import($certPath, $pfxPassword, "Exportable,PersistKeySet")
$store = new-object System.Security.Cryptography.X509Certificates.X509Store($certificateStoreName,$certificateStoreLocation)
$store.open("MaxAllowed")
$store.add($pfx)
$store.close()
return $pfx
}
该函数的调用者看起来像$importedPfxCert = Import-PfxCertificate $pfxFile "LocalMachine" "My" $password
我将它安装到本地机器的My store。然后我将读取权限授予了我的IIS应用程序池。
我有一个需要使用它的WCF服务
<behaviors>
<serviceBehaviors>
<behavior>
<serviceCredentials>
<serviceCertificate findValue="MyCertName" x509FindType="FindBySubjectName" />
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="MyValidator" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
当我使用客户端来调用服务时,我从WCF It is likely that certificate 'CN=MyCertName' may not have a private key that is capable of key exchange or the process may not have access rights for the private key.
如果我从MMC中删除它,并手动将同一个PFX文件从证书MMC导入同一个商店并授予相同权限,我的客户端可以毫无问题地调用该服务。
所以它引导我思考,出于某种原因,如果我使用PowerShell,私钥会以某种方式被搞砸。
有趣的是,无论采用哪种方式,我都会去MMC并双击我安装的证书,我可以看到You have a private key that corresponds to the certificate.
所以即使在PowerShell中也可以加载私钥。权限设置相同。
任何线索或经验?
答案 0 :(得分:4)
有同样的问题。下一个脚本工作:
function InstallCert ($certPath, [System.Security.Cryptography.X509Certificates.StoreName] $storeName)
{
[Reflection.Assembly]::Load("System.Security, Version=2.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a")
$flags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath, "", $flags)
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store($storeName, [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite);
$store.Add($cert);
$store.Close();
}
答案 1 :(得分:1)
我更新了谢尔盖对以下内容的回答。请注意,using namespace ...
语法仅对PS 5.0及更高版本有效。如果您需要更早版本,则必须根据需要添加完整的命名空间System.Security.Cryptography.X509Certificates
。
using namespace System.Security
[CmdletBinding()]
param (
[parameter(mandatory=$true)] [string] $CertificateFile,
[parameter(mandatory=$true)] [securestring] $PrivateKeyPassword,
[parameter(mandatory=$true)] [string] $AllowedUsername
)
# Setup certificate
$Flags = [Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet `
-bor [Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet `
-bor [Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$Certificate = New-Object Cryptography.X509Certificates.X509Certificate2($CertificateFile, $PrivateKeyPassword, $Flags)
# Install certificate into machine store
$Store = New-Object Cryptography.X509Certificates.X509Store(
[Cryptography.X509Certificates.StoreName]::My,
[Cryptography.X509Certificates.StoreLocation]::LocalMachine)
$Store.Open([Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$Store.Add($Certificate)
$Store.Close()
# Allow read permission of private key by user
$PKFile = Get-ChildItem "$env:ProgramData\Microsoft\Crypto\RSA\MachineKeys\$($Certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName)"
$PKAcl = $PKFile.GetAccessControl("Access")
$ReadAccessRule = New-Object AccessControl.FileSystemAccessRule(
$AllowedUsername,
[AccessControl.FileSystemRights]::Read,
[AccessControl.AccessControlType]::Allow
)
$PKAcl.AddAccessRule($ReadAccessRule)
Set-Acl $PKFile.FullName $PKAcl
将此脚本保存到InstallCertificate.ps1
,然后以管理员身份运行:
PS C:\Users\me> .\InstallCertificate.ps1
cmdlet InstallCertificate.ps1 at command pipeline position 1
Supply values for the following parameters:
CertificateFile: c:\my\path\mycert.pfx
PrivateKeyPassword: *********************
AllowedUsername: me
PS C:\Users\me> ls Cert:\LocalMachine\My
<Observe that your cert is now listed here. Get the thumbprint>
PS C:\Users\me> (ls Cert:\LocalMachine\My | ? { $_.Thumbprint -eq $Thumbprint }).PrivateKey
重新启动后,最后一行应显示私钥仍然安装,即使是非管理员。
已编辑以https://stackoverflow.com/a/37402173/7864889中所述添加ACL步骤。
答案 2 :(得分:0)
通过MMC导入证书时,我在其中一台开发服务器上遇到了类似的问题。我的问题是Administrators组对MachineKeys文件夹没有任何权限。
C:\ Users \ All Users \ Microsoft \ Crypto \ RSA \ MachineKeys
我在MachineKeys文件夹中添加了对管理员的完全控制权,并且在导入证书时能够成功创建私钥。
确保您运行Powershell的用户有权写入MachineKeys文件夹。
答案 3 :(得分:0)
谢尔盖·阿扎克维奇(Sergey Azarkevich)在下面引用的以下代码是我的诀窍:
$flags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet
答案 4 :(得分:0)
因为内置的 Import-PfxCertificate
没有正确导入 CAPI 证书,所以我登陆了这个 SO 线程。不幸的是,powerdude
的回答 cmdlet 对我不起作用,因为它在设置权限时抛出了 The property 'CspKeyContainerInfo' cannot be found on this object. Verify that the property exists.
。
将它与这个美妙的 gist from milesgratz 结合后,它起作用了。
这是看起来像 Import-PfxCertificate
替代品的最终修改版本。
# Import-CapiPfxCertificate.ps1
# for CNG certificates use built in Import-PfxCertificate
using namespace System.Security
[CmdletBinding()]
param (
[parameter(mandatory=$true)] [string] $FilePath,
[parameter(mandatory=$true)] [securestring] $Password,
[parameter(mandatory=$true)] [string] $CertStoreLocation,
[parameter(mandatory=$false)] [string] $AllowedUsername
)
if (-not ($CertStoreLocation -match '^Cert:\\([A-Z]+)\\([A-Z]+)$')) {
Write-Host "Incorrect CertStoreLocation. See usage in the Import-PfxCertificate documentation" -ForegroundColor Red
exit 1;
}
$StoreName = $Matches.2
$StoreLocation = $Matches.1
# Setup certificate
$Flags = [Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet `
-bor [Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet `
-bor [Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$Certificate = New-Object Cryptography.X509Certificates.X509Certificate2($FilePath, $Password, $Flags)
# Install certificate into the specified store
$Store = New-Object Cryptography.X509Certificates.X509Store(
$StoreName,
$StoreLocation)
$Store.Open([Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$Store.Add($Certificate)
$Store.Close()
if (-not ([string]::IsNullOrEmpty($AllowedUsername))) {
# Allow read permission of private key by user
$PKUniqueName = ([System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($Certificate)).key.UniqueName
$PKFile = Get-Item "$env:ProgramData\Microsoft\Crypto\RSA\MachineKeys\$PKUniqueName"
$PKAcl = Get-Acl $PKFile
$PKAcl.AddAccessRule((New-Object AccessControl.FileSystemAccessRule($AllowedUsername, "Read", "Allow")))
Set-Acl $PKFile.FullName $PKAcl
}