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

126

我有一个ASP.NET应用程序需要访问证书存储中的证书中的私钥。在Windows Server 2003上,我可以使用winhttpcertcfg.exe给NETWORK SERVICE帐户授予私钥访问权限。如何在Windows Server 2008 R2上为IIS 7.5网站中的证书存储(Local Computer\Personal)中的证书提供访问私钥的权限?

我已尝试使用证书MMC(Server 2008 R2)向“Everyone”,“IIS AppPool\DefaultAppPool”,“IIS_IUSRS”和我能找到的每个安全账户授予完全信任访问权限。然而,下面的代码表明该代码无法访问导入私钥的证书的私钥。每当访问私钥属性时,代码都会抛出错误。

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" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<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"; } }
7个回答

225
  1. 创建/购买证书。确保它有一个私钥。
  2. 将证书导入到“本地计算机”帐户中。最好使用证书MMC。确保勾选“允许导出私钥”
  3. 根据此,IIS 7.5应用程序池的身份验证使用以下之一。

    • IIS 7.5网站正在运行ApplicationPoolIdentity下。打开MMC =>添加证书(本地计算机)快照=>证书(本地计算机)=>个人=>证书=>右键点击有关证书=>所有任务=>管理私钥=>添加并授予其完全控制。将"AppPoolName"替换为您的应用程序池名称(有时为IIS_IUSRS)。
    • IIS 7.5网站正在运行NETWORK SERVICE 下。使用证书MMC,在“本地计算机\ 个人”证书中将 "NETWORK SERVICE" 添加到完全信任。
    • IIS 7.5网站正在运行“MyIISUser”本地计算机用户帐户下。使用证书MMC,在“本地计算机\ 个人”证书中将 "MyIISUser"(新的本地计算机用户帐户)添加到完全信任。

基于@Phil Hale的评论进行更新:

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


3
如何在Windows Server 2008 R2上将证书中的“XXX”配置为完全信任("Local Computer\Personal")?运行/mmc/file/add snap-in/certificates,然后进行相应操作即可。谢谢。 - Cobaia
7
当您打开了 MMC 证书(位于“本地计算机\个人”)时,单击“证书”以查看证书。 (注:以下假设证书已导入,如果没有,则先导入证书)。右键单击要授予完全控制权限的证书。在上下文菜单中,单击“所有任务”,然后在子菜单中单击“管理私钥”。从那里,您可以添加任何您想要具有“读取”访问权限的用户,以访问证书的私钥。 - thames
5
确保本地计算机被选中在“来自位置”的框中。这让我困惑了一段时间。默认情况下选择了域,因此直到我将位置更改为本地计算机才找到应用程序池身份验证用户。 - Phil Hale
1
由于您正在为证书/个人添加完全信任,这意味着您必须将相同的证书导入到个人存储中,而不仅仅是证书/受信任的根...我必须从“StoreName.Root”访问证书,但是您无法设置这些证书的权限。据我所知,您只能在个人证书上设置权限... - Robert Koritnik
3
在AWS的Windows 2012 R2 EC2虚拟机(基于IIS 8)上,您需要授予"IIS_IUSRS"访问证书私钥的权限。 - DeepSpace101
显示剩余4条评论

48

关于通过MMC、证书、选择证书、右键、全部任务、“管理私钥”来授予权限的说明

“管理私钥”只在“个人”菜单列表中出现……所以如果您将您的证书放在了“受信任的人”等中,您就没有办法了。

我们找到了一种解决方法,适用于我们的情况。将证书拖放到“个人”中,进行“管理私钥”的操作以授予权限。记得设置使用内置对象类型,并使用本地机器而不是域名。我们向DefaultAppPool用户授予了权限,然后就完成了。

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


是的,这个方法很有效。我在下面的帖子中提到了它,但是另一个答案被接受了,尽管被接受的答案更长,需要下载一个WCF文件。https://dev59.com/LmPVa4cB1Zd3GeqP9M5S#10594123 - thames
2
有没有针对Win2003服务器的解决方案?它没有像Windows 7那样的“管理私钥”选项。 - sonjz
1
@sonjz - 请查看此技术文档,它提到使用命令行工具winhttpcertcfg - mlhDev
如果你需要证书的私钥,除了个人使用之外,那么你很可能在做一些错误的事情...所有其他位置都是为其他/外部实体而设计的,这些实体是值得信任的。你不应该拥有他们的私钥。他们的公钥(证书)应该足够了。我甚至敢说,如果你拥有他们的私钥,你就不应该信任他们。 - Martin

35
如果您正在尝试在IIS中从.pfx文件加载证书,则解决方案可能就是启用此选项:应用程序池
右键单击应用程序池,然后选择高级设置
然后启用加载用户配置文件


输入图像描述


3
为什么这样做有所不同? - MichaelD
4
这可能是Windows的设置问题,它可能会暂时将配置文件加载到用户配置文件中,因此需要这个选项。虽然加载IIS有访问权限的文件时需要这样做似乎不太合理,但我同意这种情况很奇怪。 - Simon_Weaver

10
我发现有人询问如何在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

7

对我来说,只需要勾选“允许导出私钥”重新导入证书即可。

我猜这是必要的,但作为一个第三方应用程序访问该证书,这确实让我感到有些紧张。


谢谢,我是这样做的 X509Certificate2 cert = new X509Certificate2(certBytes, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet); - Juan Lozoya

1
尽管我已经参加了上述活动,但经过多次尝试后才达到了这个阶段。 1- 如果您想从商店访问证书,您可以通过以下示例完成。 2- 通过路径生成证书并使用它要简单得多且更清晰。 Asp.net Core 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;
            }
        }

    }
}

OR 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")
        );

0
在证书面板中,右键单击某个证书 -> 所有任务 -> 管理私钥 -> 添加具有完全控制权限的IIS_IUSRS用户。
在我的情况下,我不需要安装我的证书并勾选“允许导出私钥”选项,就像其他答案中所说的那样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接