如何让 ASP.NET 访问证书存储中证书中的私钥?

Posted

技术标签:

【中文标题】如何让 ASP.NET 访问证书存储中证书中的私钥?【英文标题】:How to give ASP.NET access to a private key in a certificate in the certificate store? 【发布时间】:2011-02-06 06:44:49 【问题描述】:

我有一个 ASP.NET 应用程序,它可以访问证书存储中证书中的私钥。在 Windows Server 2003 上,我能够使用 winhttpcertcfg.exe 授予对 NETWORK SERVICE 帐户的私钥访问权限。如何在 IIS 7.5 网站的 Windows Server 2008 R2 上授予访问证书存储(本地计算机\个人)中证书中的私钥的权限?

我已经尝试为“Everyone”、“IIS AppPool\DefaultAppPool”、“IIS_IUSRS”以及我可以使用证书 MMC(Server 2008 R2)找到的所有其他安全帐户授予完全信任访问权限。但是,下面的代码表明该代码无权访问使用私钥导入的证书的私钥。每次访问私钥属性时,代码都会抛出错误。

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Import Namespace="System.Security.Cryptography.X509Certificates" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "@987654321@">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Repeater ID="repeater1" runat="server">
            <HeaderTemplate>
                <table>
                    <tr>
                        <td>
                            Cert
                        </td>
                        <td>
                            Public Key
                        </td>
                        <td>
                            Private Key
                        </td>
                    </tr>
            </HeaderTemplate>
            <ItemTemplate>
                <tr>
                    <td>
                    <%#((X509Certificate2)Container.DataItem).GetNameInfo(X509NameType.SimpleName, false) %>
                    </td>
                    <td>
                    <%#((X509Certificate2)Container.DataItem).HasPublicKeyAccess() %>
                    </td>
                    <td>
                    <%#((X509Certificate2)Container.DataItem).HasPrivateKeyAccess() %>
                    </td>
                </tr>
            </ItemTemplate>
            <FooterTemplate>
                </table></FooterTemplate>
        </asp:Repeater>
    </div>
    </form>
</body>
</html>

Default.aspx.cs

using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Web.UI;
public partial class _Default : Page 

    public X509Certificate2Collection Certificates;
    protected void Page_Load(object sender, EventArgs e)
    
        // Local Computer\Personal
        var store = new X509Store(StoreLocation.LocalMachine);
        // create and open store for read-only access
        store.Open(OpenFlags.ReadOnly);
        Certificates = store.Certificates;
        repeater1.DataSource = Certificates;
        repeater1.DataBind();
    

public static class Extensions

    public static string HasPublicKeyAccess(this X509Certificate2 cert)
    
        try
        
            AsymmetricAlgorithm algorithm = cert.PublicKey.Key;
        
        catch (Exception ex)
        
            return "No";
        
        return "Yes";
    
    public static string HasPrivateKeyAccess(this X509Certificate2 cert)
    
        try
        
            string algorithm = cert.PrivateKey.KeyExchangeAlgorithm;
        
        catch (Exception ex)
        
            return "No";
        
        return "Yes";
    

【问题讨论】:

【参考方案1】:
    创建/购买证书。确保它有一个私钥。 将证书导入“本地计算机”帐户。最好使用证书 MMC。 确保选中“允许导出私钥”

    基于此,IIS 7.5 应用程序池的标识使用以下之一。

    IIS 7.5 网站在 ApplicationPoolIdentity 下运行。打开 MMC => 添加证书(本地计算机)管理单元 => 证书(本地计算机) => 个人 => 证书 => 右键单击​​感兴趣的证书 => 所有任务 => 管理私钥 => 添加IIS AppPool\AppPoolName 和授予它Full control。将“AppPoolName”替换为您的应用程序池的名称(有时是 IIS_IUSRS) IIS 7.5 网站正在网络服务下运行。使用证书 MMC,将“网络服务”添加到“本地计算机\个人”中证书的完全信任中。 IIS 7.5 网站在“MyIISUser”本地计算机用户帐户下运行。使用证书 MMC,将“MyIISUser”(一个新的本地计算机用户帐户)添加到“本地计算机\个人”中证书的完全信任中。

根据@Phil Hale 评论更新:

请注意,如果您在域中,则默认情况下会在“来自位置框”中选择您的域。确保将其更改为“本地计算机”。将位置更改为“本地计算机”以查看应用程序池标识。

【讨论】:

如何在 Windows Server 2008 R2 中配置(“XXX”对“本地计算机\个人”中的证书完全信任)?运行/mmc/文件/添加管理单元/证书和???谢谢 当您将 MMC 证书打开到本地计算机\个人时,单击“证书”以查看证书。 (注意:以下假设已经导入证书,如果没有,则先导入证书)右键单击要授予完全控制权的证书。在上下文菜单中,单击“所有任务”,然后在子菜单中单击“管理私钥”。从那里,您可以添加您希望对证书的私钥具有“读取”访问权限的任何用户。 确保在“从位置”框中选择了本地计算机。这让我难过了一阵子。默认情况下选择了域,因此在我将位置更改为本地计算机之前它没有找到应用程序池身份用户 在 AWS 的 Windows 2012 R2 EC2 VM(基于 IIS 8)上,您需要授予 IIS_IUSRS 对证书私钥的访问权限 知道如何通过 powershell 做到这一点吗?【参考方案2】:

关于通过 MMC、证书、选择证书、右键单击、所有任务、“管理私钥”授予权限的注意事项

Manage Private Keys 仅在 Personal 的菜单列表中...因此,如果您将证书放在 Trusted People 等中,那么您就不走运了。

我们找到了一种对我们有用的解决方法。将证书拖放到 Personal,执行 Manage Private Keys 以授予权限。请记住设置为使用对象类型内置函数并使用本地计算机而不是域。我们向 DefaultAppPool 用户授予了权限,并将其保留。

完成后,将证书拖放回原来的位置。快。

【讨论】:

是的,这很好用。我在以下帖子的答案中提到了它,但是即使接受的答案更长并且需要下载 WCF 文件,也接受了另一个答案。 ***.com/questions/10580326/… win2003服务器有什么解决办法吗?它没有像 Windows 7 那样的“管理私钥”选项 @sonjz - 查看this technet,它提到使用命令行winhttpcertcfg 如果您需要私钥用于除个人之外的任何证书,您很可能做错了什么...所有其他位置都适用于您信任的其他/外部实体。你不应该拥有他们的私钥。他们的公钥(证书)应该足够了。我什至敢说,如果你有他们的私钥,你就不应该相信他们。【参考方案3】:

如果您尝试从 IIS 中的 .pfx 文件加载证书,解决方案可能就像为 Application Pool 启用此选项一样简单。

右键单击应用程序池并选择Advanced Settings

然后启用Load User Profile

【讨论】:

为什么会有不同? 这一定是windows的连接方式。它可能会暂时将配置文件加载到用户配置文件中,因此它需要此选项。我同意从 IIS 有权访问的文件加载时这是必要的,这很奇怪。 这在我为 PDF 设置数字签名时帮助了我。 天哪!我已经花费了 10 多个小时来调试使用客户端证书加载 p12 文件(= 基本上与 PFX 相同)以使用 Web 服务进行身份验证并启用此功能的问题,最终解决了问题。谢谢! 谢谢!问题就这样解决了。【参考方案4】:

我想出了如何在有人询问的 Powershell 中执行此操作:

$keyname=(((gci cert:\LocalMachine\my | ? $_.thumbprint -like $thumbprint).PrivateKey).CspKeyContainerInfo).UniqueKeyContainerName
$keypath = $env:ProgramData + “\Microsoft\Crypto\RSA\MachineKeys\”
$fullpath=$keypath+$keyname

$Acl = Get-Acl $fullpath
$Ar = New-Object System.Security.AccessControl.FileSystemAccessRule("IIS AppPool\$iisAppPoolName", "Read", "Allow")
$Acl.SetAccessRule($Ar)
Set-Acl $fullpath $Acl

【讨论】:

【参考方案5】:

对我来说,无非是重新导入证书并勾选“允许导出私钥”。

我想这是必要的,但它确实让我感到紧张,因为它是访问此证书的第三方应用程序。

【讨论】:

谢谢我这样做了 X509Certificate2 cert = new X509Certificate2(certBytes, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);【参考方案6】:

补充答案this is a guide找到证书的私钥并添加权限。

这是获取证书私钥指南中的FindPrivateKey.exe 的指南。

【讨论】:

【参考方案7】:

虽然我参加过以上,但经过多次尝试,我走到了这一步。 1-如果您想从商店访问证书,您可以这样做作为示例 2-生成证书并通过路径使用它更加容易和清洁

Asp.net 核心 2.2 OR1:

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Operators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace Tursys.Pool.Storage.Api.Utility

    class CertificateManager
    
        public static X509Certificate2 GetCertificate(string caller)
        
            AsymmetricKeyParameter caPrivateKey = null;
            X509Certificate2 clientCert;
            X509Certificate2 serverCert;

            clientCert = GetCertificateIfExist("CN=127.0.0.1", StoreName.My, StoreLocation.LocalMachine);
            serverCert = GetCertificateIfExist("CN=MyROOTCA", StoreName.Root, StoreLocation.LocalMachine);
            if (clientCert == null || serverCert == null)
            
                var caCert = GenerateCACertificate("CN=MyROOTCA", ref caPrivateKey);
                addCertToStore(caCert, StoreName.Root, StoreLocation.LocalMachine);

                clientCert = GenerateSelfSignedCertificate("CN=127.0.0.1", "CN=MyROOTCA", caPrivateKey);
                var p12 = clientCert.Export(X509ContentType.Pfx);

                addCertToStore(new X509Certificate2(p12, (string)null, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet), StoreName.My, StoreLocation.LocalMachine);
            

            if (caller == "client")
                return clientCert;

            return serverCert;
        

        public static X509Certificate2 GenerateSelfSignedCertificate(string subjectName, string issuerName, AsymmetricKeyParameter issuerPrivKey)
        
            const int keyStrength = 2048;

            // Generating Random Numbers
            CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
            SecureRandom random = new SecureRandom(randomGenerator);

            // The Certificate Generator
            X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();

            // Serial Number
            BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
            certificateGenerator.SetSerialNumber(serialNumber);

            // Signature Algorithm
            //const string signatureAlgorithm = "SHA256WithRSA";
            //certificateGenerator.SetSignatureAlgorithm(signatureAlgorithm);

            // Issuer and Subject Name
            X509Name subjectDN = new X509Name(subjectName);
            X509Name issuerDN = new X509Name(issuerName);
            certificateGenerator.SetIssuerDN(issuerDN);
            certificateGenerator.SetSubjectDN(subjectDN);

            // Valid For
            DateTime notBefore = DateTime.UtcNow.Date;
            DateTime notAfter = notBefore.AddYears(2);

            certificateGenerator.SetNotBefore(notBefore);
            certificateGenerator.SetNotAfter(notAfter);

            // Subject Public Key
            AsymmetricCipherKeyPair subjectKeyPair;
            var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
            var keyPairGenerator = new RsaKeyPairGenerator();
            keyPairGenerator.Init(keyGenerationParameters);
            subjectKeyPair = keyPairGenerator.GenerateKeyPair();

            certificateGenerator.SetPublicKey(subjectKeyPair.Public);

            // Generating the Certificate
            AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair;

            ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", issuerKeyPair.Private, random);
            // selfsign certificate
            Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(signatureFactory);


            // correcponding private key
            PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);


            // merge into X509Certificate2
            X509Certificate2 x509 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certificate.GetEncoded());

            Asn1Sequence seq = (Asn1Sequence)Asn1Object.FromByteArray(info.PrivateKeyAlgorithm.GetDerEncoded());
            if (seq.Count != 9)
            
                //throw new PemException("malformed sequence in RSA private key");
            

            RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(info.ParsePrivateKey());
            RsaPrivateCrtKeyParameters rsaparams = new RsaPrivateCrtKeyParameters(
                rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2, rsa.Coefficient);

            try
            
                var rsap = DotNetUtilities.ToRSA(rsaparams);
                x509 = x509.CopyWithPrivateKey(rsap);

                //x509.PrivateKey = ToDotNetKey(rsaparams);
            
            catch(Exception ex)
            
                ;
            
            //x509.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
            return x509;

        

        public static AsymmetricAlgorithm ToDotNetKey(RsaPrivateCrtKeyParameters privateKey)
        
            var cspParams = new CspParameters
            
                KeyContainerName = Guid.NewGuid().ToString(),
                KeyNumber = (int)KeyNumber.Exchange,
                Flags = CspProviderFlags.UseMachineKeyStore
            ;

            var rsaProvider = new RSACryptoServiceProvider(cspParams);
            var parameters = new RSAParameters
            
                Modulus = privateKey.Modulus.ToByteArrayUnsigned(),
                P = privateKey.P.ToByteArrayUnsigned(),
                Q = privateKey.Q.ToByteArrayUnsigned(),
                DP = privateKey.DP.ToByteArrayUnsigned(),
                DQ = privateKey.DQ.ToByteArrayUnsigned(),
                InverseQ = privateKey.QInv.ToByteArrayUnsigned(),
                D = privateKey.Exponent.ToByteArrayUnsigned(),
                Exponent = privateKey.PublicExponent.ToByteArrayUnsigned()
            ;

            rsaProvider.ImportParameters(parameters);
            return rsaProvider;
        

        public static X509Certificate2 GenerateCACertificate(string subjectName, ref AsymmetricKeyParameter CaPrivateKey)
        
            const int keyStrength = 2048;

            // Generating Random Numbers
            CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
            SecureRandom random = new SecureRandom(randomGenerator);

            // The Certificate Generator
            X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();

            // Serial Number
            BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
            certificateGenerator.SetSerialNumber(serialNumber);

            // Signature Algorithm
            //const string signatureAlgorithm = "SHA256WithRSA";
            //certificateGenerator.SetSignatureAlgorithm(signatureAlgorithm);

            // Issuer and Subject Name
            X509Name subjectDN = new X509Name(subjectName);
            X509Name issuerDN = subjectDN;
            certificateGenerator.SetIssuerDN(issuerDN);
            certificateGenerator.SetSubjectDN(subjectDN);

            // Valid For
            DateTime notBefore = DateTime.UtcNow.Date;
            DateTime notAfter = notBefore.AddYears(2);

            certificateGenerator.SetNotBefore(notBefore);
            certificateGenerator.SetNotAfter(notAfter);

            // Subject Public Key
            AsymmetricCipherKeyPair subjectKeyPair;
            KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
            RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator();
            keyPairGenerator.Init(keyGenerationParameters);
            subjectKeyPair = keyPairGenerator.GenerateKeyPair();

            certificateGenerator.SetPublicKey(subjectKeyPair.Public);

            // Generating the Certificate
            AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair;

            // selfsign certificate
            //Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(issuerKeyPair.Private, random);

            ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", issuerKeyPair.Private, random);
            // selfsign certificate
            Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(signatureFactory);


            X509Certificate2 x509 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certificate.GetEncoded());

            CaPrivateKey = issuerKeyPair.Private;

            return x509;
            //return issuerKeyPair.Private;

        

        public static bool addCertToStore(System.Security.Cryptography.X509Certificates.X509Certificate2 cert, System.Security.Cryptography.X509Certificates.StoreName st, System.Security.Cryptography.X509Certificates.StoreLocation sl)
        
            bool bRet = false;

            try
            
                X509Store store = new X509Store(st, sl);
                store.Open(OpenFlags.ReadWrite);
                store.Add(cert);

                store.Close();
            
            catch
            

            

            return bRet;
        

        protected internal static X509Certificate2 GetCertificateIfExist(string subjectName, StoreName store, StoreLocation location)
        
            using (var certStore = new X509Store(store, location))
            
                certStore.Open(OpenFlags.ReadOnly);
                var certCollection = certStore.Certificates.Find(
                                           X509FindType.FindBySubjectDistinguishedName, subjectName, false);
                X509Certificate2 certificate = null;
                if (certCollection.Count > 0)
                
                    certificate = certCollection[0];
                
                return certificate;
            
        

    

或 2:

    services.AddDataProtection()
//.PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
.ProtectKeysWithCertificate(
        new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "clientCert.pfx"), "Password")
        )
.UnprotectKeysWithAnyCertificate(
        new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "clientCert.pfx"), "Password")
        );

【讨论】:

【参考方案8】:

在证书面板中,右键单击一些证书 -> 所有任务 -> 管理私钥 -> 添加具有完全控制权的 IIS_IUSRS 用户

就我而言,我不需要安装我的证书并选中“允许导出私钥”选项,就像其他答案中所说的那样。

【讨论】:

以上是关于如何让 ASP.NET 访问证书存储中证书中的私钥?的主要内容,如果未能解决你的问题,请参考以下文章

WCF 中的自签名证书问题 - 必须具有私钥

从 iOS 中的私钥派生的证书(Objective-C)

证书与私钥匹配

如何获取“开发人员ID应用程序”或“开发人员ID安装程序”证书的私钥?

如何编程读取PKCS#12格式的证书与私钥

如何将私钥添加到分发证书?