如何设置 .NET 中 X.509 证书私钥文件的读取权限

发布于 2024-07-11 01:36:44 字数 351 浏览 8 评论 0原文

以下是将 pfx 添加到证书存储的代码。

X509Store store = new X509Store( StoreName.My, StoreLocation.LocalMachine );
store.Open( OpenFlags.ReadWrite );
X509Certificate2 cert = new X509Certificate2( "test.pfx", "password" );
store.Add( cert );
store.Close();

但是,我找不到设置 NetworkService 访问私钥的权限的方法。

任何人都可以透露一些信息吗? 提前致谢。

Here is the code to add a pfx to the Cert store.

X509Store store = new X509Store( StoreName.My, StoreLocation.LocalMachine );
store.Open( OpenFlags.ReadWrite );
X509Certificate2 cert = new X509Certificate2( "test.pfx", "password" );
store.Add( cert );
store.Close();

However, I couldn't find a way to set permission for NetworkService to access the private key.

Can anyone shed some light? Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

岁月蹉跎了容颜 2024-07-18 01:36:44

这个答案迟到了,但我想将其发布给在这里搜索的其他人:

我找到了一篇 MSDN 博客文章,其中提供了使用 CryptoKeySecurity 的解决方案 此处,这是 C# 解决方案的示例:

var rsa = certificate.PrivateKey as RSACryptoServiceProvider;
if (rsa != null)
{
    // Modifying the CryptoKeySecurity of a new CspParameters and then instantiating
    // a new RSACryptoServiceProvider seems to be the trick to persist the access rule.
    // cf. http://blogs.msdn.com/b/cagatay/archive/2009/02/08/removing-acls-from-csp-key-containers.aspx
    var cspParams = new CspParameters(rsa.CspKeyContainerInfo.ProviderType, rsa.CspKeyContainerInfo.ProviderName, rsa.CspKeyContainerInfo.KeyContainerName)
    {
        Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore,
        CryptoKeySecurity = rsa.CspKeyContainerInfo.CryptoKeySecurity
    };

    cspParams.CryptoKeySecurity.AddAccessRule(new CryptoKeyAccessRule(sid, CryptoKeyRights.GenericRead, AccessControlType.Allow));

    using (var rsa2 = new RSACryptoServiceProvider(cspParams))
    {
        // Only created to persist the rule change in the CryptoKeySecurity
    }
}

我使用 SecurityIdentifier 来识别帐户但 NTAccount 也同样有效。

This answer is late but I wanted to post it for anybody else that comes searching in here:

I found an MSDN blog article that gave a solution using CryptoKeySecurity here, and here is an example of a solution in C#:

var rsa = certificate.PrivateKey as RSACryptoServiceProvider;
if (rsa != null)
{
    // Modifying the CryptoKeySecurity of a new CspParameters and then instantiating
    // a new RSACryptoServiceProvider seems to be the trick to persist the access rule.
    // cf. http://blogs.msdn.com/b/cagatay/archive/2009/02/08/removing-acls-from-csp-key-containers.aspx
    var cspParams = new CspParameters(rsa.CspKeyContainerInfo.ProviderType, rsa.CspKeyContainerInfo.ProviderName, rsa.CspKeyContainerInfo.KeyContainerName)
    {
        Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore,
        CryptoKeySecurity = rsa.CspKeyContainerInfo.CryptoKeySecurity
    };

    cspParams.CryptoKeySecurity.AddAccessRule(new CryptoKeyAccessRule(sid, CryptoKeyRights.GenericRead, AccessControlType.Allow));

    using (var rsa2 = new RSACryptoServiceProvider(cspParams))
    {
        // Only created to persist the rule change in the CryptoKeySecurity
    }
}

I'm using a SecurityIdentifier to identify the account but an NTAccount would work just as well.

痕至 2024-07-18 01:36:44

如果这可以帮助其他人,我在 Powershell 中写了 Jim Flood 的答案

function Set-PrivateKeyPermissions {
param(
[Parameter(Mandatory=$true)][string]$thumbprint,
[Parameter(Mandatory=$false)][string]$account = "NT AUTHORITY\NETWORK SERVICE"
)
#Open Certificate store and locate certificate based on provided thumbprint
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine")
$store.Open("ReadWrite")
$cert = $store.Certificates | where {$_.Thumbprint -eq $thumbprint}

#Create new CSP object based on existing certificate provider and key name
$csp = New-Object System.Security.Cryptography.CspParameters($cert.PrivateKey.CspKeyContainerInfo.ProviderType, $cert.PrivateKey.CspKeyContainerInfo.ProviderName, $cert.PrivateKey.CspKeyContainerInfo.KeyContainerName)

# Set flags and key security based on existing cert
$csp.Flags = "UseExistingKey","UseMachineKeyStore"
$csp.CryptoKeySecurity = $cert.PrivateKey.CspKeyContainerInfo.CryptoKeySecurity
$csp.KeyNumber = $cert.PrivateKey.CspKeyContainerInfo.KeyNumber

# Create new access rule - could use parameters for permissions, but I only needed GenericRead
$access = New-Object System.Security.AccessControl.CryptoKeyAccessRule($account,"GenericRead","Allow")
# Add access rule to CSP object
$csp.CryptoKeySecurity.AddAccessRule($access)

#Create new CryptoServiceProvider object which updates Key with CSP information created/modified above
$rsa2 = New-Object System.Security.Cryptography.RSACryptoServiceProvider($csp)

#Close certificate store
$store.Close()

}

请注意,帐户参数也可以采用“DOMAIN\USER”的形式(不仅仅是内置名称) - 我在我的环境中测试了它,它会自动将其转换为适当的 SID

In case this helps anyone else out, I wrote Jim Flood's answer in Powershell

function Set-PrivateKeyPermissions {
param(
[Parameter(Mandatory=$true)][string]$thumbprint,
[Parameter(Mandatory=$false)][string]$account = "NT AUTHORITY\NETWORK SERVICE"
)
#Open Certificate store and locate certificate based on provided thumbprint
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine")
$store.Open("ReadWrite")
$cert = $store.Certificates | where {$_.Thumbprint -eq $thumbprint}

#Create new CSP object based on existing certificate provider and key name
$csp = New-Object System.Security.Cryptography.CspParameters($cert.PrivateKey.CspKeyContainerInfo.ProviderType, $cert.PrivateKey.CspKeyContainerInfo.ProviderName, $cert.PrivateKey.CspKeyContainerInfo.KeyContainerName)

# Set flags and key security based on existing cert
$csp.Flags = "UseExistingKey","UseMachineKeyStore"
$csp.CryptoKeySecurity = $cert.PrivateKey.CspKeyContainerInfo.CryptoKeySecurity
$csp.KeyNumber = $cert.PrivateKey.CspKeyContainerInfo.KeyNumber

# Create new access rule - could use parameters for permissions, but I only needed GenericRead
$access = New-Object System.Security.AccessControl.CryptoKeyAccessRule($account,"GenericRead","Allow")
# Add access rule to CSP object
$csp.CryptoKeySecurity.AddAccessRule($access)

#Create new CryptoServiceProvider object which updates Key with CSP information created/modified above
$rsa2 = New-Object System.Security.Cryptography.RSACryptoServiceProvider($csp)

#Close certificate store
$store.Close()

}

Note that the account parameter can be in the form of "DOMAIN\USER" as well (not just built in names) - I tested this in my environment and it automatically converted it to the appropriate SID

沧笙踏歌 2024-07-18 01:36:44

要以编程方式执行此操作,您必须执行三件事:

  1. 获取私钥文件夹的路径。

  2. 获取该文件夹中私钥的文件名。

  3. 添加对该文件的权限。

请参阅这篇文章了解一些示例代码所有三个(具体查看“AddAccessToCertificate”方法)。

To do it programmatically, you have to do three things:

  1. Get the path of the private key folder.

  2. Get the file name of the private key within that folder.

  3. Add the permission to that file.

See this post for some example code that does all three (specifically look at the "AddAccessToCertificate" method).

半山落雨半山空 2024-07-18 01:36:44

您可以使用作为 < a href="http://www.microsoft.com/downloads/details.aspx?familyid=9d467a69-57ff-4ae7-96ee-b18c4790cffd&displaylang=en" rel="noreferrer">Windows Server 2003 资源工具包工具。

示例:

winhttpcertcfg -g -c LOCAL_MACHINE\My -s test -a NetworkService

或者,您可以使用 WCF 附带的查找私钥工具 SDK,查找证书私钥文件在磁盘上的位置。 然后您可以简单地使用 ACL 来设置文件的正确权限。

示例:

FindPrivateKey My LocalMachine -n "CN=test"

You can use the WinHttpCertCfg.exe tool that ships as part of the Windows Server 2003 Resource Kit Tools.

Example:

winhttpcertcfg -g -c LOCAL_MACHINE\My -s test -a NetworkService

Alternatively, you could use the Find Private Key tool that ships with the WCF SDK, to find the location on disk of the certificate's private key file. Then you can simply use ACL to set the right privileges on the file.

Example:

FindPrivateKey My LocalMachine -n "CN=test"
凉城凉梦凉人心 2024-07-18 01:36:44

根据@russ的回答,

这个版本可以处理密钥存储提供程序和密钥存储提供程序。 传统加密服务提供商。

function Set-PrivateKeyPermissions {
    param(
        [Parameter(Mandatory=$true)]
        [string]$thumbprint,
        [Parameter(Mandatory=$true)]
        [string]$account
    )

    #Open Certificate store and locate certificate based on provided thumbprint
    $store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine")
    $store.Open("ReadWrite")
    $cert = $store.Certificates | where {$_.Thumbprint -eq $thumbprint}

    if ($cert.PrivateKey -Eq $null) {
        # Probably using Key Storage Provider rather than crypto service provider
        $rsaCert = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
        if ($rsaCert -Eq $null) {
            throw "Private key on certificate $($cert.Subject) not available"
        }

        $fileName = $rsaCert.key.UniqueName
        $path = "$env:ALLUSERSPROFILE\Microsoft\Crypto\Keys\$fileName"
        $permissions = Get-Acl -Path $path

        $access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($account, "FullControl", "Allow")
        $permissions.AddAccessRule($access_rule)
        Set-Acl -Path $path -AclObject $permissions
    } else {
        #Create new CSP object based on existing certificate provider and key name
        $csp = New-Object System.Security.Cryptography.CspParameters($cert.PrivateKey.CspKeyContainerInfo.ProviderType, $cert.PrivateKey.CspKeyContainerInfo.ProviderName, $cert.PrivateKey.CspKeyContainerInfo.KeyContainerName)

        # Set flags and key security based on existing cert
        $csp.Flags = "UseExistingKey","UseMachineKeyStore"
        $csp.CryptoKeySecurity = $cert.PrivateKey.CspKeyContainerInfo.CryptoKeySecurity
        $csp.KeyNumber = $cert.PrivateKey.CspKeyContainerInfo.KeyNumber

        # Create new access rule - could use parameters for permissions, but I only needed GenericRead
        $access = New-Object System.Security.AccessControl.CryptoKeyAccessRule($account,"GenericRead","Allow")
        # Add access rule to CSP object
        $csp.CryptoKeySecurity.AddAccessRule($access)

        #Create new CryptoServiceProvider object which updates Key with CSP information created/modified above
        $rsa2 = New-Object System.Security.Cryptography.RSACryptoServiceProvider($csp)
    }

    #Close certificate store
    $store.Close()
}

Based on @russ's answer,

This version copes with both Key Storage Provider & the Legacy Crypto Service Provider.

function Set-PrivateKeyPermissions {
    param(
        [Parameter(Mandatory=$true)]
        [string]$thumbprint,
        [Parameter(Mandatory=$true)]
        [string]$account
    )

    #Open Certificate store and locate certificate based on provided thumbprint
    $store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine")
    $store.Open("ReadWrite")
    $cert = $store.Certificates | where {$_.Thumbprint -eq $thumbprint}

    if ($cert.PrivateKey -Eq $null) {
        # Probably using Key Storage Provider rather than crypto service provider
        $rsaCert = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
        if ($rsaCert -Eq $null) {
            throw "Private key on certificate $($cert.Subject) not available"
        }

        $fileName = $rsaCert.key.UniqueName
        $path = "$env:ALLUSERSPROFILE\Microsoft\Crypto\Keys\$fileName"
        $permissions = Get-Acl -Path $path

        $access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($account, "FullControl", "Allow")
        $permissions.AddAccessRule($access_rule)
        Set-Acl -Path $path -AclObject $permissions
    } else {
        #Create new CSP object based on existing certificate provider and key name
        $csp = New-Object System.Security.Cryptography.CspParameters($cert.PrivateKey.CspKeyContainerInfo.ProviderType, $cert.PrivateKey.CspKeyContainerInfo.ProviderName, $cert.PrivateKey.CspKeyContainerInfo.KeyContainerName)

        # Set flags and key security based on existing cert
        $csp.Flags = "UseExistingKey","UseMachineKeyStore"
        $csp.CryptoKeySecurity = $cert.PrivateKey.CspKeyContainerInfo.CryptoKeySecurity
        $csp.KeyNumber = $cert.PrivateKey.CspKeyContainerInfo.KeyNumber

        # Create new access rule - could use parameters for permissions, but I only needed GenericRead
        $access = New-Object System.Security.AccessControl.CryptoKeyAccessRule($account,"GenericRead","Allow")
        # Add access rule to CSP object
        $csp.CryptoKeySecurity.AddAccessRule($access)

        #Create new CryptoServiceProvider object which updates Key with CSP information created/modified above
        $rsa2 = New-Object System.Security.Cryptography.RSACryptoServiceProvider($csp)
    }

    #Close certificate store
    $store.Close()
}
旧话新听 2024-07-18 01:36:44

如果有人感兴趣的话,这是我为 Windows Server 2008 找到的解决方案: http:// technet.microsoft.com/en-us/library/ee662329.aspx

基本上,我必须向需要使用 MMC 工具访问证书的服务授予权限。 奇迹般有效。

This is the solution I found for windows server 2008 if anyone was interested: http://technet.microsoft.com/en-us/library/ee662329.aspx

Basically, I had to grant permissions to the service that needs to access the certificate using the MMC tool. Works like a charm.

呆° 2024-07-18 01:36:44

如果CertificatePrivateKeyRSACng类型,您可以走这条路线:

对于LocalMachine:

 var rsaPrivateKey = certificate.GetRSAPrivateKey();
 var privateKey = rsaPrivateKey as RSACng;
 var keyUniqueName = privateKey.Key.UniqueName;
 var folderPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
 var keyPath = Path.Combine(folderPath, "Microsoft", "Crypto", "RSA", "MachineKeys", keyUniqueName);
 var fileInfo = new FileInfo(keyPath);
 var accessControl = fileInfo.GetAccessControl();
 accessControl.AddAccessRule(new FileSystemAccessRule(new NTAccount("<account>"), FileSystemRights.FullControl, AccessControlType.Allow));
 fileInfo.SetAccessControl(accessControl);

如果它是用户证书,请使用Environment .SpecialFolder.ApplicationData 并在文件夹 roaming\microsoft\crypto\rsa 中查找 sid

If the PrivateKey of Certificate is of type RSACng you can go this route:

For LocalMachine:

 var rsaPrivateKey = certificate.GetRSAPrivateKey();
 var privateKey = rsaPrivateKey as RSACng;
 var keyUniqueName = privateKey.Key.UniqueName;
 var folderPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
 var keyPath = Path.Combine(folderPath, "Microsoft", "Crypto", "RSA", "MachineKeys", keyUniqueName);
 var fileInfo = new FileInfo(keyPath);
 var accessControl = fileInfo.GetAccessControl();
 accessControl.AddAccessRule(new FileSystemAccessRule(new NTAccount("<account>"), FileSystemRights.FullControl, AccessControlType.Allow));
 fileInfo.SetAccessControl(accessControl);

If it is a User Certificate use Environment.SpecialFolder.ApplicationData and find the sid in the folder roaming\microsoft\crypto\rsa instead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文