Як надати ASP.NET доступ до приватного ключа в сертифікаті в магазині сертифікатів?


111

У мене є програма ASP.NET, яка отримує доступ до приватного ключа в сертифікаті в магазині сертифікатів. У Windows Server 2003 мені вдалося скористатися winhttpcertcfg.exe, щоб надати приватному ключу доступ до облікового запису NETWORK SERVICE. Як надати дозволи на доступ до приватного ключа в сертифікаті в магазині сертифікатів (Місцевий комп'ютер \ Персональний) на веб-сайті Windows Server 2008 R2 на веб-сайті IIS 7.5?

Я намагався надати повний доступ до довіри "Усі", "IIS AppPool \ DefaultAppPool", "IIS_IUSRS" та всіх інших облікових записів безпеки, які я міг знайти за допомогою сертифікатів MMC (Server 2008 R2). Однак наведений нижче код демонструє, що код не має доступу до приватного ключа сертифіката, імпортованого за допомогою приватного ключа. Код натомість кидає та помиляє щоразу, коли доступ до властивості приватного ключа.

Типово.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>

Типово.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";
    }
}

Відповіді:


195
  1. Створити / придбати сертифікат. Переконайтеся, що в ньому є приватний ключ.
  2. Імпортуйте сертифікат до облікового запису "Локальний комп'ютер". Найкраще використовувати сертифікати MMC. Обов’язково поставте прапорець "Дозволити експорт приватного ключа"
  3. На підставі цього, ідентичність IIS 7.5 Application Pool використовує одне з наступних.

    • Веб-сайт IIS 7.5 працює під ApplicationPoolIdentity. Відкрийте оснащення MMC => Додати сертифікати (локальний комп'ютер) => Сертифікати (локальний комп'ютер) => Особисті => Сертифікати => Клацніть правою кнопкою миші сертифікат, що цікавить => Усі завдання => Керувати приватним ключем => Додати IIS AppPool\AppPoolNameта надати його Full control. Замініть " AppPoolName " на ім'я пулу додатків (іноді IIS_IUSRS)
    • Веб-сайт IIS 7.5 працює за допомогою мережі. Використовуючи сертифікати MMC, додано "МЕРЕЖНЕ ОБСЛУГОВУВАННЯ" до Повної довіри на сертифікат у "Місцевий комп'ютер \ Особистий".
    • Веб-сайт IIS 7.5 працює під обліковим записом користувачів локального комп'ютера "MyIISUser". Використовуючи сертифікати MMC, додано "MyIISUser" (новий обліковий запис користувачів локального комп'ютера) до "Повного довіри" на сертифікаті в "Місцевий комп'ютер \ Особистий".

Оновлення на основі коментаря @Phil Hale:

Остерігайтеся, якщо ви перебуваєте в домені, ваш домен буде вибрано за замовчуванням у полі "з поля розташування". Обов’язково змініть це на "Локальний комп'ютер". Змініть місце розташування на "Місцевий комп'ютер", щоб переглянути особи пулу додатків.


3
Як налаштувати ("XXX" на Повне довіру на сертифікат у "Місцевий комп'ютер \ Особистий") у Windows Server 2008 R2? запустити / mmc / файл / додати оснащення / сертифікати та ??? Спасибі
Кобея

7
Коли у вас відкриті сертифікати MMC для локального комп'ютера \ персонального, натисніть "сертифікат", щоб переглянути сертифікати. . У контекстному меню натисніть «Усі завдання», потім у підменю натисніть «Керувати приватними ключами». Звідти ви можете додати всіх користувачів, яким ви хочете отримати доступ до приватного ключа для сертифіката.
Темза

5
Переконайтесь, що в полі "з місця" вибрано локальний комп'ютер. Це натрапило на деякий час. Домен був вибраний за замовчуванням, тому він не знайшов ідентифікатора користувача пулу додатків, поки я не змінив місцеположення на локальний комп'ютер
Phil Hale

3
На VM для Windows 2012 R2 EC2 V2 (на базі IIS 8) вам потрібно надати IIS_IUSRSприватний ключ сертифіката
DeepSpace101

4
Будь-яка ідея, як це зробити за допомогою повноважень?
sonjz

43

Примітка про надання дозволів через MMC, Certs, Select Cert, клацніть правою кнопкою миші, усіма завданнями, "Керуйте приватними ключами"

Управління приватними клавішами є лише у списку меню для Особистих ... Тож якщо ви помістили свою довідку у "Довірених людей" тощо, вам не пощастить.

Ми знайшли спосіб, який працював на нас. Перетягніть сертифікат на "Особисте", виконайте завдання "Керувати приватними ключами", щоб надати дозволи. Не забудьте встановити використовувати вбудовані об'єктні типи та використовувати локальну машину не домен. Ми надали права користувачу DefaultAppPool і залишили його при цьому.

Як тільки ви закінчите, перетягніть церт назад туди, куди ви її спочатку мали. Престо.


так, це працює добре. Я згадував про це у відповіді в наступному дописі, проте інша відповідь була прийнята, хоча прийнята відповідь значно довша і вимагає завантаження WCF-файлу. stackoverflow.com/questions/10580326 / ...
Thames

2
будь-яке рішення для сервера win2003? у нього немає функції Manage Private Keys як варіант, наприклад Windows 7
sonjz

1
@sonjz - ознайомтеся з цією технологією , вона згадує, використовуючи командний рядокwinhttpcertcfg
mlhDev

Якщо вам потрібні приватні ключі для сертифікатів ні на що, окрім персоналу, ви, швидше за все, робите щось не так ... Усі інші локації призначені для інших / зовнішніх організацій, яким ви довіряєте. У вас не повинно бути їх приватних ключів. Їх відкритих ключів (сертифікатів) повинно бути достатньо. Я б навіть наважився сказати, що якщо у вас є їхні приватні ключі, ви не повинні їм довіряти.
Мартін

15

Якщо ви намагаєтеся завантажити файл cert з .pfx-файлу в IIS, рішення може бути таким же простим, як включення цієї опції для Application Pool.

Клацніть правою кнопкою миші на пул додатків та виберіть Advanced Settings.

Потім увімкніть Load User Profile


введіть тут опис зображення


1
Чому це має значення?
MichaelD

3
Це просто так, як підключаються вікна. Це може бути тимчасове завантаження профілю в профіль користувача, тому йому потрібна ця опція. Я погоджуюся, що дивно, що це необхідно під час завантаження з файлу, до якого має доступ IIS.
Simon_Weaver

Це допомогло мені, коли я встановлював цифрові підписи для PDF-файлів.
Фред Вілсон,

7

Я зрозумів, як це зробити в Powershell, про який хтось питав:

$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

Для мене це було не що інше, як повторний імпорт сертифікату з позначкою "Дозволити приватний ключ на експорт".

Я думаю, що це потрібно, але це нервує мене, оскільки це стороннє додаток, що отримує доступ до цього сертифіката.


дякую, я зробив це так X509Certificate2 cert = новий X509Certificate2 (certBytes, пароль, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
Хуан

1

Доповнюючи відповіді, це посібник для пошуку приватного ключа сертифіката та додавання дозволів.

Це керівництво для пошуку FindPrivateKey.exe, знайденого в посібнику для пошуку приватного ключа сертифіката.


0

Хоча я брав участь у вищесказаному, я прийшов до цього моменту після багатьох спроб. 1- Якщо ви хочете отримати доступ до сертифіката з магазину, ви можете зробити це як приклад 2 - Це набагато простіше і чіткіше виготовити сертифікат і використовувати його через шлях

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

    }
}

АБО 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

На Панелі сертифікатів клацніть правою кнопкою миші деякий сертифікат -> Усі завдання -> Керування приватним ключем -> Додати користувача IIS_IUSRS з повним контролем

У моєму випадку мені не потрібно було встановлювати свій сертифікат із опцією "Дозволити приватний ключ експортувати", як зазначено в інших відповідях.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.