【问题标题】:Setting private key permissions with PowerShell使用 PowerShell 设置私钥权限
【发布时间】:2013-12-31 06:20:15
【问题描述】:

我有一个将 pfx 证书安装到 LocalMachine 证书存储区的 PowerShell 脚本。函数如下所示:

function Add-Certificate {
param
(
    [Parameter(Position=1, Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [string]$pfxPath,

    [Parameter(Position=2, Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [string]$pfxPassword
)

    Write-Host "Installing certificate" -ForegroundColor Yellow

    try 
    {
        $pfxcert = new-object system.security.cryptography.x509certificates.x509certificate2
        $pfxcert.Import($pfxPath, $pfxPassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]"PersistKeySet")

        $store = new-object system.security.cryptography.X509Certificates.X509Store -argumentlist "MY", LocalMachine
        $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]"ReadWrite");
        $store.Add($pfxcert);
        $store.Close();

        return $pfxcert
    }
    catch 
    {
        throw
    }
}

当我打开证书管理器以验证安装时,我可以看到它已正确安装。

我的流程的下一步是将证书的权限分配给服务帐户。

function Set-CertificatePermission
{
    param
    (
        [Parameter(Position=1, Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$pfxThumbPrint,

        [Parameter(Position=2, Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$serviceAccount
    )

    $cert = Get-ChildItem -Path cert:\LocalMachine\My | Where-Object -FilterScript { $PSItem.ThumbPrint -eq $pfxThumbPrint; };

    # Specify the user, the permissions and the permission type
    $permission = "$($serviceAccount)","Read,FullControl","Allow"
    $accessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $permission;

    # Location of the machine related keys
    $keyPath = $env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\";
    $keyName = $cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName;
    $keyFullPath = $keyPath + $keyName;

    try
    {
        # Get the current acl of the private key
        # This is the line that fails!
        $acl = Get-Acl -Path $keyFullPath;

        # Add the new ace to the acl of the private key
        $acl.AddAccessRule($accessRule);

        # Write back the new acl
        Set-Acl -Path $keyFullPath -AclObject $acl;
    }
    catch
    {
        throw $_;
    }
}

此功能失败。具体来说,此函数在尝试评估 Get-Acl 命令时失败,并出现以下错误:Get-Acl : Cannot find path 'C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\59f1e969a4f7e5de90224f68bc9be536_1d508f5e-0cbc-4eca- a402-3e55947faa3b'

事实证明,密钥文件已安装到我的漫游配置文件中C:\Users\MyUserName\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-1259098847-1967870486-1845911597 -155499

我确定 Add-Certificate 功能有问题,但我无法弄清楚它是什么。如何强制它在 C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys 目录下安装密钥文件?

【问题讨论】:

  • 仅供参考,我知道这并不能解决主题,但try .. catch 语句只会捕获 PowerShell 中的终止错误。大多数 cmdlet 会生成非终止错误,因此要将它们更改为终止错误,请使用 Get-Acl 上的 -ErrorAction 参数并将值设置为 Stop。
  • 谢谢@TrevorSullivan,我会立即做出改变。
  • 不客气。顺便说一句,您不使用Import-PfxCertificate cmdlet 是否有原因?
  • @TrevorSullivan 据我所知,这仅在 Windows 2012 中可用。
  • 您运行的是哪个版本的 Windows 和 PowerShell?

标签: powershell permissions pfx


【解决方案1】:

问题是当X509Certificate2 通过Import() 方法导入时,X509KeyStorageFlags 未配置为将私钥写入计算机的私钥存储。我已经更新了函数以包含适当的X509KeyStorageFlags。

function Add-Certificate {
    [CmdletBinding()]
    param
    (
        [Parameter(Position=1, Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Position=2, Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Password
    )

    Write-Verbose -Message ('Installing certificate from path: {0}' -f $Path);

    try 
    {
        # Create the certificate
        $pfxcert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ErrorAction Stop;
        $KeyStorageFlags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable -bxor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet -bxor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet;
        Write-Verbose ('Key storage flags is: {0}' -f $KeyStorageFlags);
        $pfxcert.Import($Path, $Password, $KeyStorageFlags);

        # Create the X509 store and import the certificate
        $store = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList My, LocalMachine -ErrorAction Stop;
        $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite);
        $store.Add($pfxcert);
        $store.Close();

        Write-Output -InputObject $pfxcert;
    }
    catch 
    {
        throw $_;
    }
}

【讨论】:

  • 我在看同样的东西。我遇到的问题是这设置了文件系统的权限,而不是私钥本身的权限(例如,在证书管理器中右键单击证书->所有任务->管理私钥)。知道如何修改吗?
【解决方案2】:

由于 Windows 错误,提供的脚本对我有用(谢谢!)有一个更改(请参阅Why does Set-Acl on the drive root try to set ownership of the "object"?)

这是更新后的脚本:

function Set-CertificatePermission
{
 param
 (
    [Parameter(Position=1, Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [string]$pfxThumbPrint,

    [Parameter(Position=2, Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [string]$serviceAccount
 )

 $cert = Get-ChildItem -Path cert:\LocalMachine\My | Where-Object -FilterScript { $PSItem.ThumbPrint -eq $pfxThumbPrint; };

 # Specify the user, the permissions and the permission type
 $permission = "$($serviceAccount)","Read,FullControl","Allow"
 $accessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $permission;

 # Location of the machine related keys
 $keyPath = $env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\";
 $keyName = $cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName;
 $keyFullPath = $keyPath + $keyName;

 try
 {
    # Get the current acl of the private key
    $acl = (Get-Item $keyFullPath).GetAccessControl

    # Add the new ace to the acl of the private key
    $acl.AddAccessRule($accessRule);

    # Write back the new acl
    Set-Acl -Path $keyFullPath -AclObject $acl;
 }
 catch
 {
    throw $_;
 }
}

【讨论】:

  • 只有一个补充:try-catch 中的 GetAccessControl 不是 cmdlet,需要放在括号中。否则,看起来很酷:)
  • 对于任何出现并尝试使用此答案的人,try-catch 中带有 $acl 的行应如下所示: $acl = (Get-Item $keyFullPath).GetAccessControl('Access ')
  • 我注意到这是在授予“Read,FullControl”。什么时候需要在其中包含“FullControl”,什么时候“Read”就足够了?
猜你喜欢
  • 1970-01-01
  • 2013-06-15
  • 1970-01-01
  • 1970-01-01
  • 2017-02-24
  • 1970-01-01
  • 1970-01-01
  • 2015-03-11
  • 2023-03-28
相关资源
最近更新 更多