Como dar acesso ASP.NET a uma chave privada em um certificado no armazenamento de certificados?

111

Eu tenho um aplicativo ASP.NET que acessa a chave privada em um certificado no armazenamento de certificados. No Windows Server 2003, consegui usar winhttpcertcfg.exe para fornecer acesso de chave privada à conta NETWORK SERVICE. Como concedo permissões para acessar uma chave privada em um certificado no armazenamento de certificados (Computador Local \ Pessoal) em um Windows Server 2008 R2 em um site do IIS 7.5?

Tentei fornecer acesso de confiança total a "Todos", "IIS AppPool \ DefaultAppPool", "IIS_IUSRS" e todas as outras contas de segurança que consegui encontrar usando o MMC de certificados (Server 2008 R2). No entanto, o código a seguir demonstra que o código não tem acesso à Chave Privada de um certificado que foi importado com a chave privada. Em vez disso, o código gera um erro sempre que a propriedade da chave privada é acessada.

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

Tamisa
fonte

Respostas:

195
  1. Criar / adquirir certificado. Certifique-se de que possui uma chave privada.
  2. Importe o certificado para a conta "Computador Local". Melhor usar certificados MMC. Certifique-se de marcar "Permitir que a chave privada seja exportada"
  3. Com base nisso, a identidade do pool de aplicativos do IIS 7.5 usa um dos seguintes.

    • O site do IIS 7.5 está sendo executado em ApplicationPoolIdentity. Abra MMC => Adicionar certificados (computador local) snap-in => Certificados (computador local) => Pessoal => Certificados => Clique com o botão direito do mouse no certificado de interesse => Todas as tarefas => Gerenciar chave privada => Adicionar IIS AppPool\AppPoolNamee conceder Full control. Substitua " AppPoolName " pelo nome do seu pool de aplicativos (às vezes IIS_IUSRS)
    • O site do IIS 7.5 está sendo executado no SERVIÇO DE REDE. Usando Certificados MMC, adicionou "SERVIÇO DE REDE" à confiança total no certificado em "Computador Local \ Pessoal".
    • O site do IIS 7.5 está sendo executado na conta de usuário do computador local "MyIISUser". Usando Certificados MMC, adicionado "MyIISUser" (uma nova conta de usuário do computador local) para Full Trust on certificado em "Local Computer \ Personal".

Atualização com base no comentário de @Phil Hale:

Cuidado, se você estiver em um domínio, seu domínio será selecionado por padrão na caixa 'do local'. Certifique-se de mudar para "Computador Local". Mude o local para "Computador Local" para visualizar as identidades do pool de aplicativos.

Tamisa
fonte
3
Como configurar ("XXX" para Full Trust no certificado em "Local Computer \ Personal") no Windows Server 2008 R2? executar / mmc / arquivo / adicionar snap-in / certificados e ??? Obrigado
Cobaia
7
Quando você abrir os Certificados MMC em Computador Local \ Pessoal, clique em "certificado" para ver os certificados. (nota: o seguinte pressupõe que o certificado já foi importado, se não, importe o certificado primeiro) Clique com o botão direito no certificado ao qual deseja conceder controle total. No menu de contexto, clique em "Todas as tarefas" e, no submenu, clique em "Gerenciar chaves privadas". A partir daí, você pode adicionar quaisquer usuários que gostaria de ter acesso de 'leitura' para a chave privada para o certificado.
thames
5
Certifique-se de que o computador local esteja selecionado na caixa "da localização". Isso me deixou perplexo por um tempo. O domínio foi selecionado por padrão, de modo que não encontrou o usuário de identidade do pool de aplicativos até que eu alterasse o local para o computador local
Phil Hale,
3
Nas VMs Windows 2012 R2 EC2 da AWS (baseado em IIS 8), você precisa dar IIS_IUSRSacesso à chave privada do certificado
DeepSpace101
4
Alguma ideia de como fazer isso via PowerShell?
sonjz
43

Observação sobre a concessão de permissões via MMC, Certs, Select Cert, clique com o botão direito, todas as tarefas, "Manage Private Keys"

Gerenciar chaves privadas está apenas na lista de menu para Pessoal ... Portanto, se você colocou seu certificado em Pessoas confiáveis, etc., você está sem sorte.

Encontramos uma maneira de contornar isso que funcionou para nós. Arraste e solte o certificado para Pessoal, faça o Gerenciar Chaves Privadas para conceder permissões. Lembre-se de definir o uso de built-ins de tipo de objeto e usar a máquina local, não o domínio. Concedemos direitos ao usuário DefaultAppPool e deixamos por isso mesmo.

Quando terminar, arraste e solte o certificado de volta onde você o tinha originalmente. Presto.

Garrett Goebel
fonte
sim, isso funciona bem. Mencionei isso em uma resposta no post a seguir, no entanto, outra resposta foi aceita, embora a resposta aceita seja muito mais longa e requer o download de um arquivo WCF. stackoverflow.com/questions/10580326/…
thames
2
alguma solução para servidor win2003? ele não tem Gerenciar Chaves Privadas como uma opção como o Windows 7
sonjz
1
@sonjz - verifique este technet , ele menciona o uso da linha de comandowinhttpcertcfg
mlhDev
Se você precisar de chaves privadas para certificados para qualquer coisa que não seja pessoal, provavelmente está fazendo algo errado ... Todos os outros locais são para outras entidades externas em que você confia. Você não deve ter suas chaves privadas. Suas chaves públicas (certificados) devem ser suficientes. Eu até ousaria dizer que, se você tem suas chaves privadas, não deve confiar nelas.
Martin
15

Se você estiver tentando carregar um certificado de um arquivo .pfx no IIS, a solução pode ser tão simples quanto habilitar esta opção para o Application Pool .

Clique com o botão direito no App Pool e selecione Advanced Settings .

Então habilite Load User Profile


insira a descrição da imagem aqui

Simon_Weaver
fonte
1
Por que isso faz diferença?
MichaelD
3
Deve ser apenas a maneira como o Windows está conectado. Que talvez esteja carregando temporariamente o perfil em um perfil de usuário, por isso precisa dessa opção. Concordo que é estranho que isso seja necessário ao carregar de um arquivo ao qual o IIS tem acesso.
Simon_Weaver
Isso me ajudou quando eu estava configurando assinaturas digitais para PDFs.
Fred Wilson
7

Eu descobri como fazer isso no Powershell que alguém perguntou:

$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
fonte
6

Para mim, não foi nada mais do que reimportar o certificado com a opção "Permitir que a chave privada seja exportada" marcada.

Acho que é necessário, mas fico nervoso, pois é um aplicativo de terceiros acessando este certificado.

Nathan Hartley
fonte
obrigado Fiz assim X509Certificate2 cert = new X509Certificate2 (certBytes, senha, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
Juan Lozoya de
1

Complementando as respostas, este é um guia para encontrar a chave privada do certificado e adicionar as permissões.

Este é o guia para obter FindPrivateKey.exe encontrado no guia para localizar a chave privada do certificado.

Juan Lozoya
fonte
0

Embora eu tenha participado do acima, cheguei a este ponto depois de muitas tentativas. 1- Se você deseja acessar o certificado da loja, pode fazer isso como um exemplo 2- É muito mais fácil e mais limpo produzir o certificado e usá-lo por meio de um caminho

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
fonte
0

No painel de certificados, clique com o botão direito em algum certificado -> Todas as tarefas -> Gerenciar chave privada -> Adicionar usuário IIS_IUSRS com controle total

No meu caso, não precisei instalar meu certificado com a opção "Permitir que chave privada seja exportada" marcada, como falado em outras respostas.

Fernando Meneses Gomes
fonte