¿Cómo dar acceso ASP.NET a una clave privada en un certificado en el almacén de certificados?

111

Tengo una aplicación ASP.NET que accede a la clave privada en un certificado en la tienda de certificados. En Windows Server 2003 pude usar winhttpcertcfg.exe para dar acceso de clave privada a la cuenta de SERVICIO DE RED. ¿Cómo otorgo permisos para acceder a una clave privada en un certificado en el almacén de certificados (equipo local \ personal) en un Windows Server 2008 R2 en un sitio web de IIS 7.5?

Intenté dar acceso de Confianza Total a "Todos", "IIS AppPool \ DefaultAppPool", "IIS_IUSRS" y todas las demás cuentas de seguridad que pude encontrar utilizando Certificados MMC (Server 2008 R2). Sin embargo, el siguiente código demuestra que el código no tiene acceso a la clave privada de un certificado que se importó con la clave privada. En cambio, el código arroja un error cada vez que se accede a la propiedad de la clave privada.

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

Támesis
fuente

Respuestas:

195
  1. Crear / Comprar certificado. Asegúrate de que tenga una clave privada.
  2. Importe el certificado a la cuenta "Equipo local". Lo mejor es utilizar Certificados MMC. Asegúrate de marcar "Permitir exportar la clave privada"
  3. Según lo cual, la identidad del grupo de aplicaciones de IIS 7.5 utiliza uno de los siguientes.

    • El sitio web de IIS 7.5 se ejecuta bajo ApplicationPoolIdentity. Abra MMC => Complemento Agregar certificados (computadora local) => Certificados (computadora local) => Personal => Certificados => Haga clic con el botón derecho en el certificado de interés => Todas las tareas => Administrar clave privada => Agregar IIS AppPool\AppPoolNamey otorgarlo Full control. Reemplaza " AppPoolName " con el nombre de tu grupo de aplicaciones (a veces IIS_IUSRS)
    • El sitio web de IIS 7.5 se ejecuta bajo el SERVICIO DE RED. Utilizando Certificados MMC, se agregó "SERVICIO DE RED" a Confianza total en el certificado en "Equipo local \ Personal".
    • El sitio web de IIS 7.5 se ejecuta en la cuenta de usuario de la computadora local "MyIISUser". Usando Certificados MMC, agregó "MyIISUser" (una nueva cuenta de usuario de computadora local) a Plena confianza en el certificado en "Computadora local \ Personal".

Actualización basada en el comentario de @Phil Hale:

Tenga cuidado, si está en un dominio, su dominio se seleccionará de forma predeterminada en el "cuadro de ubicación". Asegúrese de cambiar eso a "Equipo local". Cambie la ubicación a "Equipo local" para ver las identidades del grupo de aplicaciones.

Támesis
fuente
3
¿Cómo configurar ("XXX" a Plena confianza en el certificado en "Equipo local \ Personal") en Windows Server 2008 R2? ejecutar / mmc / archivo / agregar complemento / certificados y ??? Gracias
Cobaia
7
Cuando tenga los Certificados MMC abiertos en Equipo local \ Personal, haga clic en el "certificado" para ver los certificados. (nota: lo siguiente asume que el certificado ya está importado, de lo contrario, importe el certificado primero) Haga clic con el botón derecho en el certificado al que desea otorgar control total. En el menú contextual, haga clic en "Todas las tareas", luego en el submenú haga clic en "Administrar claves privadas". Desde allí, puede agregar los usuarios que desee que tengan acceso de "lectura" a la clave privada del certificado.
Támesis
5
Asegúrese de que la computadora local esté seleccionada en el cuadro "desde la ubicación". Esto me dejó perplejo por un tiempo. El dominio se seleccionó de forma predeterminada, por lo que no encontró el usuario de identidad del grupo de aplicaciones hasta que cambié la ubicación a la computadora local
Phil Hale
3
En las VM de AWS Windows 2012 R2 EC2 (basadas en IIS 8), debe otorgar IIS_IUSRSacceso a la clave privada del certificado
DeepSpace101
4
¿Alguna idea de cómo hacer esto a través de powershell?
sonjz
43

Nota sobre la concesión de permisos a través de MMC, certificados, certificado seleccionado, clic derecho, todas las tareas, "Administrar claves privadas"

Administrar claves privadas está solo en la lista del menú Personal ... Entonces, si ha puesto su certificado en Personas de confianza, etc., no tendrá suerte.

Encontramos una forma de evitar esto que funcionó para nosotros. Arrastre y suelte el certificado a Personal, haga lo de Administrar claves privadas para otorgar permisos. Recuerde configurar el uso de elementos integrados de tipo objeto y usar la máquina local, no el dominio. Otorgamos derechos al usuario DefaultAppPool y lo dejamos así.

Una vez que haya terminado, arrastre y suelte el certificado donde lo tenía originalmente. Presto.

Garrett Goebel
fuente
sí, esto funciona bien. Lo mencioné en una respuesta en la siguiente publicación, sin embargo, se aceptó otra respuesta a pesar de que la respuesta aceptada es mucho más larga y requiere descargar un archivo WCF. stackoverflow.com/questions/10580326/…
Thames
2
alguna solución para el servidor win2003? no tiene la opción Administrar claves privadas como una opción como Windows 7
sonjz
1
@sonjz - echa un vistazo a este technet , menciona el uso de la línea de comandowinhttpcertcfg
mlhDev
Si necesita claves privadas para certificados para cualquier cosa que no sea personal, lo más probable es que esté haciendo algo mal ... Todas las demás ubicaciones son para otras entidades externas en las que confía. No debería tener sus claves privadas. Sus claves públicas (certificados) deberían ser suficientes. Incluso me atrevería a decir que si tienes sus claves privadas, no deberías confiar en ellas.
Martin
15

Si está intentando cargar un certificado desde un archivo .pfx en IIS, la solución puede ser tan simple como habilitar esta opción para Application Pool.

Haga clic derecho en el grupo de aplicaciones y seleccione Advanced Settings.

Entonces habilita Load User Profile


ingrese la descripción de la imagen aquí

Simon_Weaver
fuente
1
Por qué esto hace una diferencia?
MichaelD
3
Debe ser la forma en que se conectan las ventanas. Que tal vez esté cargando temporalmente el perfil en un perfil de usuario, por lo que necesita esta opción. Estoy de acuerdo en que es extraño que esto sea necesario cuando se carga desde un archivo al que IIS tiene acceso.
Simon_Weaver
Esto me ayudó cuando estaba configurando firmas digitales para archivos PDF.
Fred Wilson
7

Descubrí cómo hacer esto en Powershell sobre lo que alguien preguntó:

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

Para mí, no fue más que volver a importar el certificado con la opción "Permitir exportar la clave privada" marcada.

Supongo que es necesario, pero me pone nervioso ya que es una aplicación de terceros que accede a este certificado.

Nathan Hartley
fuente
gracias, lo hice de esta manera X509Certificate2 cert = new X509Certificate2 (certBytes, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
Juan Lozoya
1

Complementando las respuestas, esta es una guía para encontrar la clave privada del certificado y agregar los permisos.

Esta es la guía para obtener FindPrivateKey.exe que se encuentra en la guía para encontrar la clave privada del certificado.

Juan Lozoya
fuente
0

Aunque he asistido a lo anterior, he llegado a este punto después de muchos intentos. 1- Si desea acceder al certificado de la tienda, puede hacerlo como un ejemplo 2- Es mucho más fácil y limpio producir el certificado y usarlo a través de una ruta

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

    }
}

O 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
fuente
0

En el Panel de certificados, haga clic con el botón derecho en algún certificado -> Todas las tareas -> Administrar clave privada -> Agregar usuario IIS_IUSRS con control total

En mi caso, no necesitaba instalar mi certificado con la opción "Permitir exportar la clave privada" marcada, como se dice en otras respuestas.

Fernando Meneses Gomes
fuente