Setting private key permissions with PowerShell

醉酒当歌 提交于 2019-12-13 11:43:18

问题


I have a PowerShell script that installs pfx certificate into the LocalMachine certificate store. The function looks like this:

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
    }
}

When I open the Certificate Manager to verify the installation I can see that it has installed correctly.

The next step in my process is to assign permissions to the certificate to a service account.

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 $_;
    }
}

This function fails. Specifically, this function fails when trying to evaluate the Get-Acl command, with the following error: Get-Acl : Cannot find path 'C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\59f1e969a4f7e5de90224f68bc9be536_1d508f5e-0cbc-4eca-a402-3e55947faa3b'

As it turns out the key file has been installed into my roaming profile C:\Users\MyUserName\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-1259098847-1967870486-1845911597-155499

I'm sure there is something wrong with the Add-Certificate function, but I cannot figure out what it is. How do I force it to install the key file in the C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys directory?


回答1:


The problem is the when the X509Certificate2 was getting imported via the Import() method, the X509KeyStorageFlags were not configured to write the private key to the computer's private key store. I've updated the function to include the appropriate 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:


The script provided worked for me (thanks!) with one change, due to a windows bug (see Why does Set-Acl on the drive root try to set ownership of the "object"?)

Here is the updated script:

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 $_;
 }
}


来源:https://stackoverflow.com/questions/20852807/setting-private-key-permissions-with-powershell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!