Генерация и подпись запроса сертификата с использованием чистого.net Framework

Я пытаюсь использовать чистый код.net для создания запроса на сертификат и создания сертификата на основе запроса сертификата на основе имеющегося у меня сертификата CA (в хранилище сертификатов Windows или в отдельном файле).

Я знаю, что у меня есть классы X509Certificate а также X509Certificate2 доступны для загрузки сертификатов и получения доступа к их информации, но я не вижу никаких классов или функций в System.Security.Cryptography пространство имен, которое можно использовать для создания запроса на сертификат или для подписи такого запроса на сертификат для создания нового подписанного сертификата.

И это хотя документация на System.Security.Cryptography.Pkcs Пространство имен говорит:

Пространство имен System.Security.Cryptography.Pkcs предоставляет элементы программирования для стандартов шифрования с открытым ключом (PKCS), в том числе методы подписи данных, обмена ключами, запроса сертификатов, шифрования и дешифрования открытого ключа и других функций безопасности.

Итак, как я могу создать запрос сертификата и подписать этот запрос для создания нового сертификата X509, используя только чистые классы.net из System.Security.Cryptography?


Замечания:

  • Я не хочу использовать внешний исполняемый файл, такой как openssl или MakeCert
  • Я не хочу использовать BouncyCastle
  • Я не хочу использовать API регистрации сертификатов Windows
  • Я не хочу использовать встроенные функции Win32 API

2 ответа

Решение

Краткий ответ: Вы можете начать с.NET Framework 4.7.2.

Первоначально эта функциональность была добавлена ​​в.NET Core 2.0 в форме класса CertificateRequest, который может создавать запрос подписи сертификации PKCS#10 или сертификат открытого ключа X.509 (самоподписанный или цепочечный).

Классы для этой функции были доступны в.NET Framework 4.7.2.

using (RSA parent = RSA.Create(4096))
using (RSA rsa = RSA.Create(2048))
{
    CertificateRequest parentReq = new CertificateRequest(
        "CN=Experimental Issuing Authority",
        parent,
        HashAlgorithmName.SHA256,
        RSASignaturePadding.Pkcs1);

    parentReq.CertificateExtensions.Add(
        new X509BasicConstraintsExtension(true, false, 0, true));

    parentReq.CertificateExtensions.Add(
        new X509SubjectKeyIdentifierExtension(parentReq.PublicKey, false));

    using (X509Certificate2 parentCert = parentReq.CreateSelfSigned(
        DateTimeOffset.UtcNow.AddDays(-45),
        DateTimeOffset.UtcNow.AddDays(365)))
    {
        CertificateRequest req = new CertificateRequest(
            "CN=Valid-Looking Timestamp Authority",
            rsa,
            HashAlgorithmName.SHA256,
            RSASignaturePadding.Pkcs1);

        req.CertificateExtensions.Add(
            new X509BasicConstraintsExtension(false, false, 0, false));

        req.CertificateExtensions.Add(
            new X509KeyUsageExtension(
                X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation,
                false));

        req.CertificateExtensions.Add(
            new X509EnhancedKeyUsageExtension(
                new OidCollection
                {
                    new Oid("1.3.6.1.5.5.7.3.8")
                },
                true));

        req.CertificateExtensions.Add(
            new X509SubjectKeyIdentifierExtension(req.PublicKey, false));

        using (X509Certificate2 cert = req.Create(
            parentCert,
            DateTimeOffset.UtcNow.AddDays(-1),
            DateTimeOffset.UtcNow.AddDays(90),
            new byte[] { 1, 2, 3, 4 }))
        {
            // Do something with these certs, like export them to PFX,
            // or add them to an X509Store, or whatever.
        }
    }
}

Более длинный ответ, если вы застряли на более старых версиях: чтобы достичь цели без добавления каких-либо новых вызовов P/Invoces, вам необходимо прочитать и понять следующие документы:

  • МСЭ-T X.680-201508, язык ASN.1
  • IETF RFC 5280 или ITU-T X.509, документы, поясняющие поля в сертификатах X.509.
  • IETF RFC 2986, объясняет запрос подписи сертификации PKCS#10
  • В МСЭ-T X.690 поясняется семейство кодирования BER для ASN.1 (включая DER), в котором рассказывается, как читать и записывать байты для достижения семантического значения из X.509 / PKCS#10.

И тогда вы могли бы написать писателя / читателя DER, и просто испустить байты для того, что вы хотите.

Я не могу комментировать ответ выше, так что это служит комментарием.@Keith

Если проблема заключается в закрытом ключе, используемом для сертификата сервера, отсутствует закрытый ключ, я надеюсь, что это объясняет, что происходит.

Чтобы объединить открытый и закрытый ключи в ответе выше,

cert = RSACertificateExtensions.CopyWithPrivateKey(cert, rsa);

Это свяжет закрытый ключ с сертификатом для экспорта в PFX с File.WriteAllBytes("filename", cert.Export(X509ContentType.Pfx, "passphrase for export"));

для примера, приведенного выше. Метод возвращает объект с прикрепленным открытым и закрытым ключом. Где, например, при подписании против корня или подчиненного метод создаст только компонент открытого ключа в объект.

Я думаю, это связано с тем, что обычные методы сертификатов будут использовать CSR для подписи и возврата открытого ключа для принятия клиентом, который никогда не предоставит закрытый ключ серверу подписи.

Другие вопросы по тегам