Comment donner à ASP.NET l'accès à une clé privée dans un certificat dans le magasin de certificats?

111

J'ai une application ASP.NET qui accède à la clé privée dans un certificat dans le magasin de certificats. Sur Windows Server 2003, j'ai pu utiliser winhttpcertcfg.exe pour donner accès par clé privée au compte SERVICE RÉSEAU. Comment accorder des autorisations pour accéder à une clé privée dans un certificat dans le magasin de certificats (ordinateur local \ personnel) sur un Windows Server 2008 R2 dans un site Web IIS 7.5?

J'ai essayé de donner un accès Full Trust à "Tout le monde", "IIS AppPool \ DefaultAppPool", "IIS_IUSRS" et à tous les autres comptes de sécurité que j'ai pu trouver en utilisant les Certificats MMC (Server 2008 R2). Cependant, le code ci-dessous montre que le code n'a pas accès à la clé privée d'un certificat qui a été importé avec la clé privée. Le code génère à la place une erreur à chaque fois que la propriété de clé privée est accédée.

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

Tamise
la source

Réponses:

195
  1. Créer / acheter un certificat. Assurez-vous qu'il a une clé privée.
  2. Importez le certificat dans le compte "Ordinateur local". Il est préférable d'utiliser les certificats MMC. Assurez-vous de cocher "Autoriser l'exportation de la clé privée"
  3. Sur cette base, l'identité du pool d'applications IIS 7.5 utilise l'un des éléments suivants.

    • Le site Web IIS 7.5 fonctionne sous ApplicationPoolIdentity. Ouvrez MMC => Ajouter des certificats (ordinateur local) snap-in => Certificats (ordinateur local) => Personnel => Certificats => Faites un clic droit sur le certificat d'intérêt => Toutes les tâches => Gérer la clé privée => Ajoutez IIS AppPool\AppPoolNameet accordez-le Full control. Remplacez " AppPoolName " par le nom de votre pool d'applications (parfois IIS_IUSRS)
    • Le site Web IIS 7.5 fonctionne sous SERVICE RÉSEAU. À l'aide de Certificats MMC, ajout de «SERVICE RÉSEAU» à Confiance totale sur le certificat dans «Ordinateur local \ Personnel».
    • Le site Web IIS 7.5 fonctionne sous le compte d'utilisateur de l'ordinateur local "MyIISUser". À l'aide de Certificats MMC, ajouté «MyIISUser» (un nouveau compte d'utilisateur d'ordinateur local) à la confiance totale sur le certificat dans «Ordinateur local \ Personnel».

Mise à jour basée sur le commentaire de @Phil Hale:

Attention, si vous êtes sur un domaine, votre domaine sera sélectionné par défaut dans la case "à partir de l'emplacement". Assurez-vous de changer cela en "Ordinateur local". Changez l'emplacement en «Ordinateur local» pour afficher les identités du pool d'applications.

Tamise
la source
3
Comment configurer ("XXX" en Confiance totale sur le certificat dans "Ordinateur local \ Personnel") dans Windows Server 2008 R2? exécuter / mmc / file / ajouter un composant logiciel enfichable / des certificats et ??? Merci
Cobaia
7
Lorsque les certificats MMC sont ouverts sur Ordinateur local \ Personnel, cliquez sur le «certificat» pour afficher les certificats. (Remarque: ce qui suit suppose que le certificat est déjà importé, sinon importez d'abord le certificat) Cliquez avec le bouton droit sur le certificat sur lequel vous souhaitez accorder le contrôle total. Dans le menu contextuel, cliquez sur «Toutes les tâches», puis dans le sous-menu cliquez sur «Gérer les clés privées». À partir de là, vous pouvez ajouter les utilisateurs que vous souhaitez avoir un accès en lecture à la clé privée du certificat.
thames
5
Assurez-vous que l'ordinateur local est sélectionné dans la case «à partir de l'emplacement». Cela m'a déconcerté pendant un moment. Le domaine a été sélectionné par défaut, il n'a donc pas trouvé l'utilisateur d'identité du pool d'applications jusqu'à ce que je change d'emplacement sur l'ordinateur local
Phil Hale
3
Sur les machines virtuelles Windows 2012 R2 EC2 d'AWS (basées sur IIS 8), vous devez donner IIS_IUSRSaccès à la clé privée du certificat
DeepSpace101
4
Une idée comment faire cela via PowerShell?
sonjz le
43

Remarque sur l'octroi d'autorisations via MMC, certificats, Select Cert, clic droit, toutes les tâches, "Gérer les clés privées"

Gérer les clés privées est uniquement dans la liste du menu pour Personnel ... Donc, si vous avez mis votre certificat dans Trusted People, etc., vous n'avez pas de chance.

Nous avons trouvé un moyen de contourner cela qui a fonctionné pour nous. Faites glisser et déposez le certificat sur Personnel, faites la chose Gérer les clés privées pour accorder des autorisations. N'oubliez pas de configurer pour utiliser des éléments intégrés de type objet et d'utiliser la machine locale et non le domaine. Nous avons accordé des droits à l'utilisateur DefaultAppPool et nous en sommes restés là.

Une fois que vous avez terminé, faites glisser et déposez le certificat là où vous l'aviez à l'origine. Presto.

Garrett Goebel
la source
oui cela fonctionne bien. Je l'ai mentionné dans une réponse sur le post suivant, mais une autre réponse a été acceptée même si la réponse acceptée est beaucoup plus longue et nécessite le téléchargement d'un fichier WCF. stackoverflow.com/questions/10580326/…
thames
2
une solution pour le serveur win2003? il n'a pas l'option Gérer les clés privées comme Windows 7
sonjz
1
@sonjz - consultez ce technet , il mentionne en utilisant la ligne de commandewinhttpcertcfg
mlhDev
Si vous avez besoin de clés privées pour des certificats pour autre chose que pour des raisons personnelles, vous faites probablement quelque chose de mal ... Tous les autres emplacements sont pour d'autres entités externes en qui vous avez confiance. Vous ne devriez pas avoir leurs clés privées. Leurs clés publiques (certificats) devraient suffire. J'oserais même dire que si vous avez leurs clés privées, vous ne devriez pas leur faire confiance.
Martin
15

Si vous essayez de charger un certificat à partir d'un fichier .pfx dans IIS, la solution peut être aussi simple que d'activer cette option pour le Application Pool .

Faites un clic droit sur le pool d'applications et sélectionnez Advanced Settings .

Puis activez Load User Profile


entrez la description de l'image ici

Simon_Weaver
la source
1
Pourquoi cela fait-il une différence?
MichaelD
3
Cela doit juste être la façon dont les fenêtres sont câblées. Qu'il charge peut-être temporairement le profil dans un profil utilisateur, il a donc besoin de cette option. Je conviens qu'il est étrange que cela soit nécessaire lors du chargement à partir d'un fichier auquel IIS a accès.
Simon_Weaver
Cela m'a aidé lorsque je configurais des signatures numériques pour les PDF.
Fred Wilson
7

J'ai compris comment faire cela dans Powershell que quelqu'un a demandé:

$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
Ian Robertson
la source
6

Pour moi, ce n'était rien de plus que de réimporter le certificat avec "Autoriser l'exportation de la clé privée" cochée.

Je suppose que c'est nécessaire, mais cela me rend nerveux car c'est une application tierce qui accède à ce certificat.

Nathan Hartley
la source
merci je l'ai fait de cette façon X509Certificate2 cert = new X509Certificate2 (certBytes, mot de passe, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
Juan Lozoya
1

Complétant les réponses, ceci est un guide pour trouver la clé privée du certificat et ajouter les autorisations.

Ceci est le guide pour obtenir FindPrivateKey.exe trouvé dans le guide pour trouver la clé privée du certificat.

Juan Lozoya
la source
0

Bien que j'aie assisté à ce qui précède, j'en suis arrivé à ce point après de nombreuses tentatives. 1- Si vous souhaitez accéder au certificat depuis le magasin, vous pouvez le faire à titre d'exemple 2- Il est beaucoup plus facile et plus propre de produire le certificat et de l'utiliser via un chemin

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

    }
}

OU 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")
        );
Hamit YILDIRIM
la source
0

Dans le panneau des certificats, cliquez avec le bouton droit sur un certificat -> Toutes les tâches -> Gérer la clé privée -> Ajouter un utilisateur IIS_IUSRS avec un contrôle total

Dans mon cas, je n'ai pas besoin d'installer mon certificat avec l'option "Autoriser l'exportation de la clé privée" cochée, comme indiqué dans d'autres réponses.

Fernando Meneses Gomes
la source