4

我正在尝试找到一种从 powershell 脚本授予私钥权限的方法。证书存储在 CNG 中。欢迎所有想法。

4

3 回答 3

7

上面的答案在技术上是正确的,但是当我在寻找同样的东西时它并没有帮助我,因为它没有提到您需要使用从 codeplex https://clrsecurity.codeplex.com/上的 CLRSecurity 项目加载的程序集。

这是我如何实现相同目标的摘录,包括加载您需要使用 Security.Cryptography.dll 的 CLR 安全程序集。首先需要几个函数声明。我将这些包含在模块中,但是您可以根据需要使用它们。

Function Load-Assembly()
{
    [CmdletBinding(PositionalBinding=$false)]   
    param(
        [Parameter(Mandatory)][string][ValidateScript({Test-Path $_})] $DirectoryPath,
        [Parameter(Mandatory)][string][ValidateNotNullOrEmpty()] $Name
    )

    $assemblyFileNameFullPath = Join-Path -Path $DirectoryPath -ChildPath $Name

    If (Test-Path -Path  $assemblyFileNameFullPath -PathType Leaf)
    {
        Write-Verbose "Loading .NET assembly from path ""$assemblyFileNameFullPath"""

        #Load the assembly using the bytes as this gets around security restrictions that stop certain assemblies from loading from external sources
        $assemblyBytes = [System.IO.File]::ReadAllBytes($assemblyFileNameFullPath)
        $assemblyLoaded = [System.Reflection.Assembly]::Load($assemblyBytes);

        if ($assemblyLoaded -ne $null)
        {
            return $assemblyLoaded
        }
        else
        {
            Throw "Cannot load .NET assembly ""$Name"" from directory ""$DirectoryPath"""
        }
    }
    else
    {
        Write-Error "Cannot find required .NET assembly at path ""$assemblyFileNameFullPath"""
    }
}

Function Get-PrivateKeyContainerPath()
{
    [CmdletBinding(PositionalBinding=$false)]
    Param(
        [Parameter(Mandatory=$True)][string][ValidateNotNullOrEmpty()] $Name,
        [Parameter(Mandatory=$True)][boolean] $IsCNG
    )

    If ($IsCNG)
    {
        $searchDirectories = @("Microsoft\Crypto\Keys","Microsoft\Crypto\SystemKeys")
    }
    else
    {
        $searchDirectories = @("Microsoft\Crypto\RSA\MachineKeys","Microsoft\Crypto\RSA\S-1-5-18","Microsoft\Crypto\RSA\S-1-5-19","Crypto\DSS\S-1-5-20")
    }

    foreach ($searchDirectory in $searchDirectories)
    {
        $machineKeyDirectory = Join-Path -Path $([Environment]::GetFolderPath("CommonApplicationData")) -ChildPath $searchDirectory
        $privateKeyFile = Get-ChildItem -Path $machineKeyDirectory -Filter $Name -Recurse
        if ($privateKeyFile -ne $null)
        {
           return $privateKeyFile.FullName
        }
    }

    Throw "Cannot find private key file path for key container ""$Name"""
}


#Extracted code of how to obtain the private key file path (taken from a function)
#Requires an x509Certificate2 object in variable $Certificate and string variable $CertificateStore that contains the name of the certificate store

#Need to use the Security.Cryptography assembly
    $assembly = Load-Assembly -DirectoryPath $PSScriptRoot -Name Security.Cryptography.dll

    #Uses the extension methods in Security.Cryptography assembly from (https://clrsecurity.codeplex.com/)
    If ([Security.Cryptography.X509Certificates.X509CertificateExtensionMethods]::HasCngKey($Certificate))
    {
        Write-Verbose "Private key CSP is CNG"
        $privateKey = [Security.Cryptography.X509Certificates.X509Certificate2ExtensionMethods]::GetCngPrivateKey($Certificate)
        $keyContainerName = $privateKey.UniqueName
        $privateKeyPath = Get-PrivateKeyContainerPath -Name $keyContainerName -IsCNG $true
    }
    elseif ($Certificate.PrivateKey -ne $null)
    {
        Write-Verbose "Private key CSP is legacy"
        $privateKey = $Certificate.PrivateKey        
        $keyContainerName = $Certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName      
        $privateKeyPath = Get-PrivateKeyContainerPath -Name $keyContainerName -IsCNG $false
    }
    else
    {
        Throw "Certificate ""$($Certificate.GetNameInfo("SimpleName",$false))"" in store ""$CertificateStore"" does not have a private key, or that key is inaccessible, therefore permission cannot be granted"
    }

抱歉,如果这看起来像上面的重复,正如我所说的那样,它确实使用了相同的技术,但希望其他人会发现这更有用,因为它解释了如何使用 CLR 安全项目中的方法,包括如何加载程序集。

于 2014-03-03T12:32:29.150 回答
1

用于获取私钥文件名的 Cmdlet 代码。

[Cmdlet("Get", "PrivateKeyName")]
public class GetKeyNameCmdlet : Cmdlet
{
    [Parameter(Position = 0, Mandatory = false)]
    public X509Certificate2 Cert;

    protected override void ProcessRecord()
    {
        WriteObject(GetUniqueKeyName(Cert));
    }

    private static string GetUniqueKeyName(X509Certificate2 cert)
    {
        if (cert == null)
            throw new ArgumentNullException("cert");

        var cngPrivateKey = cert.GetCngPrivateKey();

        if (cngPrivateKey != null)
            return cngPrivateKey.UniqueName;

        var rsaPrivateKey = cert.PrivateKey as RSACryptoServiceProvider;
        if (rsaPrivateKey != null)
            return rsaPrivateKey.CspKeyContainerInfo.UniqueKeyContainerName;

         throw new Exception("cert");
    }
}

使用 cmdlet。CngCrypt.dll - 带有 cmdlet 代码的 dll。

  Import-Module .\CngCrypt.dll
  $local:certificateRootPath = join-path $env:ALLUSERSPROFILE      '\Microsoft\Crypto\RSA\MachineKeys\'
  $WorkingCert = Get-ChildItem CERT:\LocalMachine\My |where {$_.Subject -match 'Test'}| sort 
  Get-PrivateKeyName ($WorkingCert) 
于 2014-02-24T16:18:24.527 回答
0

如果您已经在机器/服务器上安装了证书,并且只是在寻找如何使用 powershell 向特定用户授予权限。

以下是如何使用 powershell 授予用户对证书私钥的权限的答案 ?

于 2016-10-14T15:38:26.720 回答