Установка разрешений закрытого ключа с помощью PowerShell

У меня есть сценарий PowerShell, который устанавливает сертификат pfx в хранилище сертификатов LocalMachine. Функция выглядит так:

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: Не удается найти путь 'C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\59f1e969a4f7e5de90224f68bc9be536_1d508f5e-0cbc40aeee9e3e99 '

Оказывается, файл ключа был установлен в мой перемещаемый профиль C:\Users\MyUserName\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-1259098847-1967870486-1845911597-155499

Я уверен, что что-то не так с функцией Add-Certificate, но я не могу понять, что это такое. Как заставить его установить файл ключа в каталог C: \ ProgramData \ Microsoft \ Crypto \ RSA \ MachineKeys?

2 ответа

Решение

Проблема в том, когда 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 $_;
    }
}

Предоставленный сценарий работал для меня (спасибо!) С одним изменением из-за ошибки Windows (см. Почему Set-Acl в корне диска пытается установить право собственности на "объект"?)

Вот обновленный скрипт:

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 $_;
 }
}
Другие вопросы по тегам