Как сгенерировать ответ на CSR в .NET Core (то есть написать сервер подписи CSR)? - PullRequest
0 голосов
/ 01 июля 2018

Я изучаю запросы на подпись сертификатов и серверы подписи через пакет Nuget System.Security.Cryptography.Cng. И что может быть лучше, чем попытаться воссоздать его. Кажется, в данный момент есть блок, который я не могу обойти, это сторона, подписывающая сервер, а именно в следующем коде я получаю System.InvalidOperationException: 'An X509Extension with OID '2.5.29.37' has already been specified.' на request.Create( в предложении using. Я вижу на http://oid -info.com / get / 2.5.29.37 о расширенном использовании ключа.

Вопросы:

  1. Вероятно, MakeLocalhostCert неправильный, что нужно изменить, чтобы сделать его сертификатом для подписи CSR?
  2. Можно ли добавлять / удалять расширения / OID в возвращаемом CSR? Я верю, что это так, но каким-то образом эта часть ускользает от меня в настоящее время.

Я использовал превосходные ответы https://stackoverflow.com/users/6535399/bartonjs в https://stackoverflow.com/a/45240640/1332416 и в https://stackoverflow.com/a/44073726/1332416, чтобы получить это далеко. :)

    private static void CsrSigningTest()
    {
        //Both ECDSA and RSA included here, though ECDSA is probably better.
        using(ECDsa privateClientEcdsaKey = ECDsa.Create(ECCurve.NamedCurves.nistP256))
        //using(RSA privateClientRsaKey = RSA.Create(2048))
        {
            //A client creates a certificate signing request.
            CertificateRequest request = new CertificateRequest(
                new X500DistinguishedName("CN=example.com, O=Example Ltd, OU=AllOver, L=Sacremento, ST=\"California\", C=US, E=some@example.com"),
                privateClientEcdsaKey,
                HashAlgorithmName.SHA256);
            /*CertificateRequest request = new CertificateRequest(
                new X500DistinguishedName("CN=example.com, O=Example Ltd, OU=AllOver, L=Sacremento, ST=\"California\", C=US, E=some@example.com"),
                privateClientRsaKey,
                HashAlgorithmName.SHA256,
                RSASignaturePadding.Pkcs1);*/

            var sanBuilder = new SubjectAlternativeNameBuilder();
            sanBuilder.AddDnsName("example.com");
            request.CertificateExtensions.Add(sanBuilder.Build());

            //Not a CA, a server certificate.
            request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
            request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
            request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.8") }, false));

            byte[] derEncodedCsr = request.CreateSigningRequest();
            var csrSb = new StringBuilder();
            csrSb.AppendLine("-----BEGIN CERTIFICATE REQUEST-----");
            csrSb.AppendLine(Convert.ToBase64String(derEncodedCsr));
            csrSb.AppendLine("-----END CERTIFICATE REQUEST-----");

            //Thus far OK, this csr seems to be working when using an online checker.
            var csr = csrSb.ToString();

            //Now, sending this to a server... How does the server function:
            //1) Read the CSR to be processed?
            //2) How does this CSR get signed?
            //In the following, can the signing cert be self-signed could be had from
            //https://stackoverflow.com/a/45240640/1332416

            byte[] serial = new byte[16];
            using(var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(serial);
            }

            DateTimeOffset notBefore = DateTimeOffset.UtcNow;
            DateTimeOffset notAfter = notBefore.AddYears(1);
            var issuerCertificate = MakeLocalhostCert();
            //For the part 1) there, this doesn't seem to work, likely since CSR isn't a X509 certificate.
            //But then again, there doesn't seem to be anything in CertificateRequest to read this.
            //In reality in the server the prologue and epilogue strings should be removed and the string read.
            //var testRequest = new X509Certificate2(derEncodedCsr);
            using(X509Certificate2 responseToCsr = request.Create(issuerCertificate, notBefore, notAfter, serial))
            {
                //How to add extensions here?
                var csrResSb = new StringBuilder();
                csrResSb.AppendLine("-----BEGIN CERTIFICATE-----");
                csrResSb.AppendLine(Convert.ToBase64String(responseToCsr.GetRawCertData()));
                csrResSb.AppendLine("-----END CERTIFICATE-----");

                var signedCert = csrResSb.ToString();
            }
        }
    }

    private static X509Certificate2 MakeLocalhostCert()
    {
        using(ECDsa key = ECDsa.Create(ECCurve.NamedCurves.nistP384))
        {
            var request = new CertificateRequest(
                "CN=localhost",
                key,
                HashAlgorithmName.SHA384);

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

            const X509KeyUsageFlags endEntityTypicalUsages =
                X509KeyUsageFlags.DataEncipherment |
                X509KeyUsageFlags.KeyEncipherment |
                X509KeyUsageFlags.DigitalSignature |
                X509KeyUsageFlags.NonRepudiation |
                X509KeyUsageFlags.KeyCertSign;

            request.CertificateExtensions.Add(
                new X509KeyUsageExtension(endEntityTypicalUsages, true));

            var sanBuilder = new SubjectAlternativeNameBuilder();
            sanBuilder.AddDnsName("localhost");
            sanBuilder.AddIpAddress(IPAddress.Loopback);
            sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);

            request.CertificateExtensions.Add(sanBuilder.Build());

            /*request.CertificateExtensions.Add(
                new X509EnhancedKeyUsageExtension(
                    new OidCollection
                    {
                // server and client authentication
                new Oid("1.3.6.1.5.5.7.3.1"),
                new Oid("1.3.6.1.5.5.7.3.2")
                    },
                    false));*/

            DateTimeOffset now = DateTimeOffset.UtcNow.AddMinutes(-1);

            return request.CreateSelfSigned(now, now.AddYears(2));
        }
    }

Как только я применил исправление об OID и изменил последние биты на

using(X509Certificate2 responseToCsr = request.Create(issuerCertificate, notBefore, notAfter, serial))
            {
                request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(issuerCertificate.PublicKey, false));
                var csrResSb = new StringBuilder();
                csrResSb.AppendLine("-----BEGIN CERTIFICATE-----");
                csrResSb.AppendLine(Convert.ToBase64String(responseToCsr.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
                csrResSb.AppendLine("-----END CERTIFICATE-----");

                var signedCert = csrResSb.ToString();
            }

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

В CoreFx GH есть проблема, которая отслеживает некоторые из затронутых здесь проблем: Крипт безопасности - дорожная карта .

1 Ответ

0 голосов
/ 01 июля 2018

Чтобы исправить ваше исключение, вы хотите, чтобы в вашем коде было установлено одно расширение EKU с двумя целевыми идентификаторами OID вместо двух расширений по одному на каждое.

// Defined two EKU extensions
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.8") }, false));

до

// One extension carrying multiple purpose OIDs
request.CertificateExtensions.Add(
    new X509EnhancedKeyUsageExtension(
        new OidCollection
        {
            new Oid("1.3.6.1.5.5.7.3.1"),
            new Oid("1.3.6.1.5.5.7.3.8"),
        },
        false));

Тогда в вашем заголовке / вопросе есть пара других проблем:

  • «Как сгенерировать ответ на CSR в .NET Core (т.е. написать сервер подписи CSR)?»
    • Этот код не читает CSR, поэтому он не отвечает ни на один из них.
      • Цель этого класса - для модульных тестов и других потребностей среды разработки, а также для создания CSR для отправки в реальный продукт CA.
      • .NET Core даже не умеет читать CSR, просто напишите их.
  • «Я узнаю о запросах на подпись сертификатов и серверах подписи через пакет Nuget System.Security.Cryptography.Cng»
    • Кажется, что ни один из вашего кода не использует типы Cng (что хорошо, вас это не должно волновать). CertificateRequest является частью System.Security.Cryptography.X509Certificates.dll, которая предоставляется через Microsoft.NETCore.App
  • "Можно ли добавлять / удалять расширения / OID в возвращаемом CSR?"
    • Да, вы добавляете их в свойство CertificateExtensions перед вызовом CreateSigningRequest.
  • (подразумевается) "Возможно ли добавить / удалить расширения / OID в возвращаемом сертификате ?"
    • Да, вы добавляете их в свойство CertificateExtensions перед вызовом Create или CreateSelfSigned.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...