Wie kann ASP.NET Zugriff auf einen privaten Schlüssel in einem Zertifikat im Zertifikatspeicher erhalten?


111

Ich habe eine ASP.NET-Anwendung, die auf den privaten Schlüssel in einem Zertifikat im Zertifikatspeicher zugreift. Unter Windows Server 2003 konnte ich winhttpcertcfg.exe verwenden, um privaten Schlüsseln Zugriff auf das NETWORK SERVICE-Konto zu gewähren. Wie erteile ich Berechtigungen für den Zugriff auf einen privaten Schlüssel in einem Zertifikat im Zertifikatspeicher (Lokaler Computer \ Personal) auf einem Windows Server 2008 R2 auf einer IIS 7.5-Website?

Ich habe versucht, "Jeder", "IIS AppPool \ DefaultAppPool", "IIS_IUSRS" und jedem anderen Sicherheitskonto, das ich mithilfe der Zertifikats-MMC (Server 2008 R2) finden konnte, vollständigen Vertrauenszugriff zu gewähren. Der folgende Code zeigt jedoch, dass der Code keinen Zugriff auf den privaten Schlüssel eines Zertifikats hat, das mit dem privaten Schlüssel importiert wurde. Der Code löst stattdessen jedes Mal einen Fehler aus, wenn auf die Eigenschaft des privaten Schlüssels zugegriffen wird.

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";
    }
}

Antworten:


195
  1. Zertifikat erstellen / kaufen. Stellen Sie sicher, dass es einen privaten Schlüssel hat.
  2. Importieren Sie das Zertifikat in das Konto "Lokaler Computer". Am besten verwenden Sie Zertifikate MMC. Stellen Sie sicher, dass "Exportieren des privaten Schlüssels zulassen" aktiviert ist.
  3. Basierend darauf verwendet die Identität des IIS 7.5-Anwendungspools eine der folgenden Optionen.

    • Die IIS 7.5-Website wird unter ApplicationPoolIdentity ausgeführt. Öffnen Sie das Snap-In MMC => Zertifikate hinzufügen (lokaler Computer) => Zertifikate (lokaler Computer) => Persönlich => Zertifikate => Klicken Sie mit der rechten Maustaste auf das gewünschte Zertifikat => Alle Aufgaben => Privaten Schlüssel verwalten => Hinzufügen IIS AppPool\AppPoolNameund gewähren Full control. Ersetzen Sie " AppPoolName " durch den Namen Ihres Anwendungspools (manchmal IIS_IUSRS)
    • Die IIS 7.5-Website wird unter NETWORK SERVICE ausgeführt. Bei Verwendung der MMC-Zertifikate wurde "NETWORK SERVICE" zu "Full Trust on Certificate" in "Local Computer \ Personal" hinzugefügt.
    • Die IIS 7.5-Website wird unter dem Benutzerkonto des lokalen Computers "MyIISUser" ausgeführt. Bei Verwendung der MMC "Zertifikate" wurde "MyIISUser" (ein neues Benutzerkonto für den lokalen Computer) zu "Vollvertrauen für das Zertifikat" unter "Lokaler Computer \ Personal" hinzugefügt.

Update basierend auf @ Phil Hale Kommentar:

Beachten Sie, dass Ihre Domain standardmäßig im Feld "Vom Standort" ausgewählt wird, wenn Sie sich in einer Domain befinden. Stellen Sie sicher, dass Sie dies in "Lokaler Computer" ändern. Ändern Sie den Speicherort in "Lokaler Computer", um die Identitäten des App-Pools anzuzeigen.


3
Wie konfiguriere ich ("XXX", um das Zertifikat in "Local Computer \ Personal" vollständig zu vertrauen) in Windows Server 2008 R2? Führen Sie / mmc / file / add snap-in / certificates und ??? Vielen Dank
Cobaia

7
Wenn Sie MMC-Zertifikate unter Lokaler Computer \ Personal geöffnet haben, klicken Sie auf "Zertifikat", um die Zertifikate anzuzeigen. (Hinweis: Im Folgenden wird davon ausgegangen, dass das Zertifikat bereits importiert wurde. Wenn nicht, importieren Sie das Zertifikat zuerst.) Klicken Sie mit der rechten Maustaste auf das Zertifikat, für das Sie die vollständige Kontrolle gewähren möchten. Klicken Sie im Kontextmenü auf "Alle Aufgaben" und dann im Untermenü auf "Private Schlüssel verwalten". Von dort aus können Sie beliebige Benutzer hinzufügen, die Lesezugriff auf den privaten Schlüssel für das Zertifikat haben möchten.
Themse

5
Stellen Sie sicher, dass der lokale Computer im Feld "Vom Standort" ausgewählt ist. Das hat mich eine Weile verblüfft. Die Domain wurde standardmäßig ausgewählt, sodass der Benutzer der App-Pool-Identität erst gefunden wurde, als ich den Speicherort auf den lokalen Computer geändert habe
Phil Hale,

3
Auf Windows 2012 R2 EC2- IIS_IUSRS
VMs von

4
Irgendeine Idee, wie man das über Powershell macht?
Sonjz

43

Hinweis zum Erteilen von Berechtigungen über MMC, Zertifikate, Zertifikat auswählen, Rechtsklick, Alle Aufgaben, "Private Schlüssel verwalten"

Private Keys verwalten steht nur in der Menüliste für Personal ... Wenn Sie also Ihr Zertifikat in Trusted People usw. abgelegt haben, haben Sie kein Glück.

Wir haben einen Weg gefunden, der für uns funktioniert hat. Ziehen Sie das Zertifikat per Drag & Drop auf Persönlich und führen Sie die Option Private Schlüssel verwalten aus, um Berechtigungen zu erteilen. Denken Sie daran, die Verwendung von integrierten Objekttypen festzulegen und die Domäne nicht auf dem lokalen Computer zu verwenden. Wir haben dem DefaultAppPool-Benutzer Rechte gewährt und dabei belassen.

Wenn Sie fertig sind, ziehen Sie das Zertifikat per Drag & Drop zurück, wo immer Sie es ursprünglich hatten. Presto.


Ja, das funktioniert gut. Ich habe es in einer Antwort im folgenden Beitrag erwähnt, jedoch wurde eine andere Antwort akzeptiert, obwohl die akzeptierte Antwort viel länger ist und das Herunterladen einer WCF-Datei erfordert. stackoverflow.com/questions/10580326/…
Themse

2
Irgendeine Lösung für den Win2003 Server? Es hat nicht die Option Private Schlüssel verwalten als Option wie Windows 7
sonjz

1
@sonjz - überprüfen Sie diese Technologie , es erwähnt über die Befehlszeilewinhttpcertcfg
mlhDev

Wenn Sie private Schlüssel für Zertifikate für etwas anderes als persönliches benötigen, machen Sie höchstwahrscheinlich etwas falsch ... Alle anderen Standorte sind für andere / externe Entitäten, denen Sie vertrauen. Sie sollten nicht ihre privaten Schlüssel haben. Ihre öffentlichen Schlüssel (Zertifikate) sollten ausreichen. Ich würde sogar sagen, wenn Sie ihre privaten Schlüssel haben, sollten Sie ihnen nicht vertrauen.
Martin

15

Wenn Sie versuchen, ein Zertifikat aus einer PFX-Datei in IIS zu laden, ist die Lösung möglicherweise so einfach wie das Aktivieren dieser Option für die Application Pool.

Klicken Sie mit der rechten Maustaste auf den App-Pool und wählen Sie Advanced Settings.

Dann aktivieren Load User Profile


Geben Sie hier die Bildbeschreibung ein


1
Warum macht das einen Unterschied?
MichaelD

3
Es muss nur so sein, wie Fenster verkabelt sind. Möglicherweise wird das Profil vorübergehend in ein Benutzerprofil geladen, sodass diese Option benötigt wird. Ich bin damit einverstanden, dass dies seltsam ist, wenn dies aus einer Datei geladen wird, auf die IIS Zugriff hat.
Simon_Weaver

Dies hat mir beim Einrichten digitaler Signaturen für PDFs geholfen.
Fred Wilson

7

Ich habe herausgefunden, wie das in Powershell geht, nach dem jemand gefragt hat:

$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

6

Für mich war es nichts anderes als das erneute Importieren des Zertifikats mit aktiviertem Kontrollkästchen "Exportieren des privaten Schlüssels zulassen".

Ich denke, es ist notwendig, aber es macht mich nervös, da es sich um eine Drittanbieter-App handelt, die auf dieses Zertifikat zugreift.


Danke, ich habe es so gemacht. X509Certificate2 cert = new X509Certificate2 (certBytes, Passwort, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
Juan Lozoya


0

Obwohl ich an dem oben genannten teilgenommen habe, bin ich nach vielen Versuchen an diesen Punkt gekommen. 1- Wenn Sie vom Geschäft aus auf das Zertifikat zugreifen möchten, können Sie dies als Beispiel tun. 2- Es ist viel einfacher und sauberer, das Zertifikat zu erstellen und über einen Pfad zu verwenden

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;
            }
        }

    }
}

ODER 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

Klicken Sie im Zertifikatfenster mit der rechten Maustaste auf ein Zertifikat -> Alle Aufgaben -> Privaten Schlüssel verwalten -> IIS_IUSRS-Benutzer mit voller Kontrolle hinzufügen

In meinem Fall musste ich mein Zertifikat nicht installieren, wenn die Option "Exportieren des privaten Schlüssels zulassen" aktiviert war, wie in anderen Antworten angegeben.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.