Come concedere ad ASP.NET l'accesso a una chiave privata in un certificato nell'archivio certificati?


111

Ho un'applicazione ASP.NET che accede alla chiave privata in un certificato nell'archivio certificati. Su Windows Server 2003 sono stato in grado di utilizzare winhttpcertcfg.exe per fornire l'accesso con chiave privata all'account SERVIZIO DI RETE. Come si concedono le autorizzazioni per accedere a una chiave privata in un certificato nell'archivio certificati (Computer locale \ Personale) su Windows Server 2008 R2 in un sito Web IIS 7.5?

Ho provato a dare accesso completo attendibile a "Everyone", "IIS AppPool \ DefaultAppPool", "IIS_IUSRS" e ogni altro account di sicurezza che ho trovato utilizzando Certificates MMC (Server 2008 R2). Tuttavia, il codice riportato di seguito dimostra che il codice non ha accesso alla chiave privata di un certificato importato con la chiave privata. Il codice invece genera un errore ogni volta che si accede alla proprietà della chiave privata.

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

Risposte:


195
  1. Crea / acquista certificato. Assicurati che abbia una chiave privata.
  2. Importa il certificato nell'account "Computer locale". Meglio usare i certificati MMC. Assicurati di selezionare "Consenti l'esportazione della chiave privata"
  3. In base a ciò, l'identità del pool di applicazioni di IIS 7.5 utilizza uno dei seguenti.

    • Il sito Web IIS 7.5 è in esecuzione con ApplicationPoolIdentity. Apri MMC => Aggiungi certificati (computer locale) snap-in => Certificati (computer locale) => Personale => Certificati => Fai clic con il pulsante destro del mouse sul certificato di interesse => Tutte le attività => Gestisci chiave privata => Aggiungi IIS AppPool\AppPoolNamee concedilo Full control. Sostituisci " AppPoolName " con il nome del tuo pool di applicazioni (a volte IIS_IUSRS)
    • Il sito Web IIS 7.5 è in esecuzione con SERVIZIO DI RETE. Utilizzando i certificati MMC, aggiunto "SERVIZIO DI RETE" a Attendibilità totale sul certificato in "Computer locale \ Personale".
    • Il sito Web IIS 7.5 è in esecuzione con l'account utente del computer locale "MyIISUser". Utilizzando i certificati MMC, aggiunto "MyIISUser" (un nuovo account utente del computer locale) a Attendibilità totale sul certificato in "Computer locale \ Personale".

Aggiornamento basato sul commento di @Phil Hale:

Attenzione, se sei su un dominio, il tuo dominio sarà selezionato per impostazione predefinita nella casella "dalla posizione". Assicurati di cambiarlo in "Computer locale". Modificare la posizione in "Computer locale" per visualizzare le identità del pool di app.


3
Come configurare ("XXX" su Attendibilità totale sul certificato in "Computer locale \ Personale") in Windows Server 2008 R2? esegui / mmc / file / aggiungi snap-in / certificati e ??? Grazie
Cobaia

7
Dopo aver aperto i certificati MMC in Computer locale \ Personale, fare clic su "certificato" per visualizzare i certificati. (nota: quanto segue presuppone che il certificato sia già importato, altrimenti importa prima il certificato) Fare clic con il pulsante destro del mouse sul certificato a cui si desidera concedere il controllo completo. Nel menu contestuale, fare clic su "Tutte le attività", quindi nel sottomenu fare clic su "Gestisci chiavi private". Da lì puoi aggiungere qualsiasi utente a cui desideri avere accesso in "lettura" alla chiave privata per il certificato.
Tamigi

5
Assicurati che il computer locale sia selezionato nella casella "dalla posizione". Questo mi ha lasciato perplesso per un po '. Il dominio è stato selezionato per impostazione predefinita, quindi non ha trovato l'utente con l'identità del pool di app finché non ho cambiato la posizione sul computer locale
Phil Hale,

3
Sulle VM di AWS Windows 2012 R2 EC2 (basate su IIS 8) devi concedere l' IIS_IUSRSaccesso alla chiave privata del certificato
DeepSpace101

4
Qualche idea su come farlo tramite PowerShell?
sonjz

43

Nota sulla concessione di autorizzazioni tramite MMC, certificati, Seleziona certificato, clic con il pulsante destro del mouse, tutte le attività, "Gestisci chiavi private"

Gestisci chiavi private è solo nell'elenco di menu per Personale ... Quindi se hai messo il tuo certificato in Persone fidate, ecc., Sei sfortunato.

Abbiamo trovato un modo per aggirare questo problema che ha funzionato per noi. Trascina e rilascia il certificato su Personale, esegui la procedura Gestisci chiavi private per concedere le autorizzazioni. Ricordarsi di impostare l'utilizzo di built-in di tipo oggetto e utilizzare la macchina locale non il dominio. Abbiamo concesso i diritti all'utente DefaultAppPool e lasciato così.

Una volta terminato, trascina e rilascia il certificato dove lo avevi originariamente. Presto.


sì, funziona bene. L'ho menzionato in una risposta nel post seguente, tuttavia un'altra risposta è stata accettata anche se la risposta accettata è molto più lunga e richiede il download di un file WCF. stackoverflow.com/questions/10580326/…
thames

2
qualsiasi soluzione per l'assistente win2003? non ha la gestione delle chiavi private come opzione come Windows 7
sonjz

1
@sonjz - controlla questo technet , menziona l'utilizzo della riga di comandowinhttpcertcfg
mlhDev

Se hai bisogno di chiavi private per certificati per qualsiasi cosa tranne che personali, molto probabilmente stai facendo qualcosa di sbagliato ... Tutte le altre posizioni sono per altre entità esterne di cui ti fidi. Non dovresti avere le loro chiavi private. Le loro chiavi pubbliche (certificati) dovrebbero essere sufficienti. Oserei anche dire che se hai le loro chiavi private, non dovresti fidarti di loro.
Martin

15

Se stai tentando di caricare un certificato da un file .pfx in IIS, la soluzione potrebbe essere semplice come abilitare questa opzione per Application Pool.

Fare clic con il tasto destro sul pool di app e selezionare Advanced Settings.

Quindi abilita Load User Profile


inserisci qui la descrizione dell'immagine


1
Perché questo fa la differenza?
MichaelD

3
Deve essere solo il modo in cui Windows è cablato. Che forse sta caricando temporaneamente il profilo in un profilo utente, quindi necessita di questa opzione. Sono d'accordo che è strano che questo sia necessario quando si carica da un file a cui IIS ha accesso.
Simon_Weaver

Questo mi ha aiutato quando stavo impostando le firme digitali per i PDF.
Fred Wilson,

7

Ho capito come farlo in Powershell che qualcuno ha chiesto:

$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

Per me, non è stato altro che reimportare il certificato con "Consenti esportazione della chiave privata" selezionata.

Immagino sia necessario, ma mi rende nervoso poiché è un'app di terze parti che accede a questo certificato.


grazie l'ho fatto in questo modo X509Certificate2 cert = new X509Certificate2 (certBytes, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
Juan Lozoya


0

Sebbene abbia assistito a quanto sopra, sono arrivato a questo punto dopo molti tentativi. 1- Se vuoi accedere al certificato dal negozio, puoi farlo come esempio 2- È molto più semplice e pulito produrre il certificato e usarlo tramite un percorso

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

    }
}

OPPURE 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

Nel pannello Certificati, fai clic con il pulsante destro del mouse su un certificato -> Tutte le attività -> Gestisci chiave privata -> Aggiungi utente IIS_IUSRS con controllo completo

Nel mio caso, non avevo bisogno di installare il mio certificato con l'opzione "Consenti esportazione della chiave privata" selezionata, come detto in altre risposte.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.