Jak nadać ASP.NET dostęp do klucza prywatnego w certyfikacie w magazynie certyfikatów?

111

Mam aplikację ASP.NET, która uzyskuje dostęp do klucza prywatnego w certyfikacie w magazynie certyfikatów. W systemie Windows Server 2003 mogłem użyć winhttpcertcfg.exe do udzielenia dostępu klucza prywatnego do konta NETWORK SERVICE. Jak nadać uprawnienia dostępu do klucza prywatnego w certyfikacie w magazynie certyfikatów (komputer lokalny \ osobisty) w systemie Windows Server 2008 R2 w witrynie sieci Web usług IIS 7.5?

Próbowałem nadać pełne zaufanie dostępom „Wszyscy”, „IIS AppPool \ DefaultAppPool”, „IIS_IUSRS” i wszystkim innym kontom zabezpieczeń, które udało mi się znaleźć przy użyciu programu Certificates MMC (Server 2008 R2). Jednak poniższy kod pokazuje, że kod nie ma dostępu do klucza prywatnego certyfikatu, który został zaimportowany z kluczem prywatnym. Zamiast tego kod generuje błąd za każdym razem, gdy uzyskuje się dostęp do właściwości klucza prywatnego.

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

Tamiza
źródło

Odpowiedzi:

195
  1. Utwórz / Kup certyfikat. Upewnij się, że ma klucz prywatny.
  2. Zaimportuj certyfikat na konto „Komputer lokalny”. Najlepiej używać certyfikatów MMC. Upewnij się, że zaznaczono opcję „Zezwalaj na eksport klucza prywatnego”
  3. Na podstawie których tożsamość puli aplikacji usług IIS 7.5 używa jednego z następujących.

    • Witryna internetowa usług IIS 7.5 działa w ramach ApplicationPoolIdentity. Otwórz MMC => Dodaj certyfikaty (komputer lokalny) przystawka => Certyfikaty (komputer lokalny) => Osobiste => Certyfikaty => Kliknij prawym przyciskiem myszy interesujący certyfikat => Wszystkie zadania => Zarządzaj kluczem prywatnym => Dodaj IIS AppPool\AppPoolNamei nadaj Full control. Zastąp „ AppPoolName ” nazwą puli aplikacji (czasami IIS_IUSRS)
    • Witryna internetowa usług IIS 7.5 działa w ramach usługi NETWORK SERVICE. Używając certyfikatów MMC, dodano „USŁUGA SIECIOWA” do pełnego zaufania na certyfikacie w „Komputer lokalny \ Osobiste”.
    • Witryna internetowa usług IIS 7.5 działa na koncie użytkownika komputera lokalnego „MyIISUser”. Używając certyfikatów MMC, dodano „MyIISUser” (nowe konto użytkownika komputera lokalnego) do pełnego zaufania na certyfikacie w „Komputer lokalny \ Osobiste”.

Aktualizacja oparta na komentarzu @Phil Hale:

Uwaga, jeśli jesteś w domenie, Twoja domena zostanie wybrana domyślnie w polu „z lokalizacji”. Pamiętaj, aby zmienić to na „Komputer lokalny”. Zmień lokalizację na „Komputer lokalny”, aby wyświetlić tożsamości puli aplikacji.

Tamiza
źródło
3
Jak skonfigurować („XXX” na pełne zaufanie do certyfikatu w „Komputer lokalny \ Osobisty”) w systemie Windows Server 2008 R2? uruchom / mmc / plik / dodaj przystawkę / certyfikaty i ??? Dzięki
Cobaia
7
Gdy masz certyfikaty MMC otwarte w folderze Komputer lokalny \ Osobisty, kliknij „certyfikat”, aby wyświetlić certyfikaty. (uwaga: poniżej założono, że certyfikat został już zaimportowany, jeśli nie, to najpierw zaimportuj certyfikat) Kliknij prawym przyciskiem myszy certyfikat, nad którym chcesz udzielić pełnej kontroli. W menu kontekstowym kliknij „Wszystkie zadania”, a następnie w podmenu kliknij „Zarządzaj kluczami prywatnymi”. Stamtąd możesz dodać dowolnych użytkowników, dla których chcesz mieć dostęp do klucza prywatnego dla certyfikatu.
Tamiza
5
Upewnij się, że komputer lokalny jest zaznaczony w polu „z lokalizacji”. Zaskoczyło mnie to na chwilę. Domena została wybrana domyślnie, więc nie znalazła użytkownika tożsamości puli aplikacji, dopóki nie zmieniłem lokalizacji na komputer lokalny
Phil Hale
3
Na AWS systemu Windows 2012 R2 EC2 VM (IIS 8 based) trzeba dać IIS_IUSRSdostęp do klucza prywatnego certyfikatu
DeepSpace101
4
Masz pomysł, jak to zrobić za pomocą programu PowerShell?
sonjz
43

Uwaga dotycząca nadawania uprawnień przez MMC, certyfikaty, wybierz certyfikat, kliknij prawym przyciskiem myszy, wszystkie zadania, „Zarządzaj kluczami prywatnymi”

Zarządzaj kluczami prywatnymi znajduje się na liście menu tylko dla osobistych ... Więc jeśli umieściłeś swój certyfikat w Zaufanych ludziach, itp., Nie masz szczęścia.

Znaleźliśmy sposób na obejście tego, który działał dla nas. Przeciągnij i upuść certyfikat na Osobisty, wykonaj polecenie Zarządzaj kluczami prywatnymi, aby przyznać uprawnienia. Pamiętaj, aby ustawić używanie wbudowanych typów obiektów i używać komputera lokalnego, a nie domeny. Przyznaliśmy prawa użytkownikowi DefaultAppPool i tak to zostawiliśmy.

Gdy skończysz, przeciągnij i upuść certyfikat z powrotem tam, gdzie był pierwotnie. Presto.

Garrett Goebel
źródło
tak, to działa dobrze. Wspomniałem o tym w odpowiedzi w poniższym poście, jednak inna odpowiedź została zaakceptowana, mimo że zaakceptowana odpowiedź jest znacznie dłuższa i wymaga pobrania pliku WCF. stackoverflow.com/questions/10580326/…
thames
2
jakieś rozwiązanie dla serwera win2003? nie ma opcji Zarządzaj
kluczami
1
@sonjz - sprawdź ten technet , wspomina o użyciu wiersza poleceńwinhttpcertcfg
mlhDev
Jeśli potrzebujesz kluczy prywatnych do certyfikatów do czegoś innego niż osobiste, najprawdopodobniej robisz coś nie tak ... Wszystkie inne lokalizacje są przeznaczone dla innych / zewnętrznych podmiotów, którym ufasz. Nie powinieneś mieć ich kluczy prywatnych. Ich klucze publiczne (certyfikaty) powinny wystarczyć. Odważyłbym się nawet powiedzieć, że jeśli masz ich klucze prywatne, nie powinieneś im ufać.
Martin
15

Jeśli próbujesz załadować certyfikat z pliku .pfx w usługach IIS, rozwiązanie może być tak proste, jak włączenie tej opcji dla Application Pool.

Kliknij prawym przyciskiem myszy pulę aplikacji i wybierz Advanced Settings.

Następnie włącz Load User Profile


wprowadź opis obrazu tutaj

Simon_Weaver
źródło
1
Dlaczego to ma znaczenie?
MichaelD
3
To musi być sposób, w jaki okablowane są okna. To, że być może jest to tymczasowe ładowanie profilu do profilu użytkownika, więc potrzebuje tej opcji. Zgadzam się, że to dziwne, że jest to konieczne podczas ładowania z pliku, do którego mają dostęp IIS.
Simon_Weaver
Pomogło mi to podczas konfigurowania podpisów cyfrowych dla plików PDF.
Fred Wilson
7

Dowiedziałem się, jak to zrobić w Powershell, o które ktoś pytał:

$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
źródło
6

Dla mnie to nic innego jak ponowne zaimportowanie certyfikatu z zaznaczeniem „Zezwól na eksport klucza prywatnego”.

Myślę, że jest to konieczne, ale denerwuje mnie, ponieważ jest to aplikacja innej firmy uzyskująca dostęp do tego certyfikatu.

Nathan Hartley
źródło
dziękuję Zrobiłem to w ten sposób X509Certificate2 cert = new X509Certificate2 (certBytes, hasło, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
Juan Lozoya
1

Uzupełnieniem odpowiedzi jest przewodnik po znalezieniu klucza prywatnego certyfikatu i dodaniu uprawnień.

To jest przewodnik dotyczący pobierania FindPrivateKey.exe znajdujący się w przewodniku dotyczącym znajdowania klucza prywatnego certyfikatu.

Juan Lozoya
źródło
0

Chociaż brałem udział w powyższym, doszedłem do tego punktu po wielu próbach. 1- Jeśli chcesz uzyskać dostęp do certyfikatu ze sklepu, możesz to zrobić jako przykład 2- Znacznie łatwiej i czystiej jest wytworzyć certyfikat i użyć go na ścieżce

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

    }
}

LUB 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
źródło
0

W Panelu Certyfikatów kliknij prawym przyciskiem myszy jakiś certyfikat -> Wszystkie zadania -> Zarządzaj kluczem prywatnym -> Dodaj użytkownika IIS_IUSRS z pełną kontrolą

W moim przypadku nie musiałem instalować swojego certyfikatu z zaznaczoną opcją „Zezwól na eksport klucza prywatnego”, jak wspomniano w innych odpowiedziach.

Fernando Meneses Gomes
źródło