当前位置: 首页 > 知识库问答 >
问题:

如何在.NET中加载证书请求并从中创建证书

夏涵畅
2023-03-14
-----BEGIN CERTIFICATE REQUEST-----
MIIDejCCAmICAQAwZTE0MDIGCgmSJom....
-----END CERTIFICATE REQUEST-----

它是使用.NET 4.7.2CertificateRequest生成的,类似于本问题中的答案:使用纯.NET Framework生成和签名证书请求

然后将序列化的CSR发送到服务器,服务器需要创建证书--问题是如何做到这一点。

共有1个答案

宦琪
2023-03-14

解析一个认证请求(口语中称为证书签名请求或CSR)并盲目签名是一种非常非常糟糕的操作实践。

如果您想成为一个证书颁发机构,甚至是一个私有的证书颁发机构,那么您应该阅读并理解CA/Browser论坛当前(无论何时阅读这篇文章)的基线需求文档(https://cabforum.org/baseline-requirements-documents/)中的所有内容。也许你故意决定某件事对你不适用,但至少这是故意的。

至少您应该检查请求:

    null
    null
    null
internal static class CertificationRequestDecoder
{
    private const string BadPemRequest = "Input is not a PEM-encoded Certification Request.";

    /// <summary>
    ///   Load a CertificateRequest from a PEM-encoded Certification Request
    ///   (a.k.a. Certificate Signing Request, CSR)
    /// </summary>
    /// <param name="pem">The PEM-encoded Certification Request</param>
    /// <param name="signatureHashAlgorithm">
    ///   The hash algorithm to be used with the CA signature.
    /// </param>
    /// <returns>
    ///   A certificate request object containing the same data as the signing request.
    /// </returns>
    /// <exception cref="ArgumentNullException"><paramref name="pem"/> is <c>null</c>.</exception>
    /// <exception cref="ArgumentException">
    ///   <paramref name="pem"/> is not a well-formed PEM encoding for a Certification Request.
    /// </exception>
    /// <exception cref="AsnContentException">
    ///   <paramref name="pem"/> does not contain a well-formed Certification Request.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    ///   The request contains unsupported elements.
    /// </exception>
    /// <exception cref="CryptographicException">
    ///   The Certification Request signature is invalid.
    /// </exception>
    /// <seealso cref="DecodeDer(ReadOnlyMemory{byte},HashAlgorithmName"/>
    internal static CertificateRequest DecodePem(
        string pem,
        HashAlgorithmName signatureHashAlgorithm)
    {
        if (pem == null)
            throw new ArgumentNullException(nameof(pem));

        // This PEM reader is overly lax. It should check for a newline at the end of preEB
        // and another at the beginning of postEB, but it skips it for Unix/Windows newline
        // reasons.
        //
        // After all, this is just a sample, right?
        const string PreEB = "-----BEGIN CERTIFICATE REQUEST-----";
        const string PostEB = "-----END CERTIFICATE REQUEST-----";

        int startIdx = pem.IndexOf(PreEB, StringComparison.Ordinal);
        int endIdx = pem.IndexOf(PostEB, StringComparison.Ordinal);

        if (startIdx < 0 || endIdx < 0)
            throw new ArgumentException(BadPemRequest, nameof(pem));

        if (startIdx != 0 && !string.IsNullOrWhiteSpace(pem.Substring(0, startIdx)))
            throw new ArgumentException(BadPemRequest, nameof(pem));

        if (endIdx < startIdx || !string.IsNullOrWhiteSpace(pem.Substring(endIdx + PostEB.Length)))
            throw new ArgumentException(BadPemRequest, nameof(pem));

        byte[] der;

        try
        {
            int base64Start = startIdx + PreEB.Length;
            string base64 = pem.Substring(base64Start, endIdx - base64Start);

            der = Convert.FromBase64String(base64);
        }
        catch (FormatException e)
        {
            throw new ArgumentException(BadPemRequest, nameof(pem), e);
        }

        return DecodeDer(der, signatureHashAlgorithm);
    }

    internal static CertificateRequest DecodeDer(
        byte[] der,
        HashAlgorithmName signatureHashAlgorithm)
    {
        if (der == null)
            throw new ArgumentNullException(nameof(der));

        return DecodeDer(der.AsMemory(), signatureHashAlgorithm);
    }

    /// <summary>
    ///   Load a CertificateRequest from a DER-encoded Certification Request
    ///   (a.k.a. Certificate Signing Request, CSR)
    /// </summary>
    /// <param name="der">The DER-encoded Certification Request.</param>
    /// <param name="signatureHashAlgorithm">
    ///   The hash algorithm to be used with the CA signature.
    /// </param>
    /// <returns>
    ///   A certificate request object containing the same data as the signing request.
    /// </returns>
    /// <exception cref="FormatException">
    ///   <paramref name="der"/> is not well-formed.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    ///   The request contains unsupported elements.
    /// </exception>
    /// <exception cref="CryptographicException">
    ///   The Certification Request signature is invalid.
    /// </exception>
    /// <remarks>
    ///   This routine does not perform any sort of operational policy.
    ///   The caller is responsible for verifying that only valid extensions
    ///   are used, that the subject name is appropriate, and any other operational
    ///   concerns.
    /// </remarks>
    internal static CertificateRequest DecodeDer(
        ReadOnlyMemory<byte> der,
        HashAlgorithmName signatureHashAlgorithm)
    {
        AsnReader reader = new AsnReader(der, AsnEncodingRules.DER);
        AsnReader certificationRequest = reader.ReadSequence();
        reader.ThrowIfNotEmpty();

        byte[] encodedRequestInfo = certificationRequest.PeekEncodedValue().ToArray();
        AsnReader certificationRequestInfo = certificationRequest.ReadSequence();
        AsnReader algorithm = certificationRequest.ReadSequence();
        byte[] signature = certificationRequest.ReadBitString(out int unused);
        
        if (unused != 0)
        {
            throw new InvalidOperationException("The signature was not complete bytes.");
        }

        certificationRequest.ThrowIfNotEmpty();

        string algorithmOid = algorithm.ReadObjectIdentifier();
        HashAlgorithmName hashAlg;
        RSASignaturePadding signaturePadding = RSASignaturePadding.Pkcs1;

        // This only supports RSA.
        // Other algorithms could be added.
        switch (algorithmOid)
        {
            case "1.2.840.113549.1.1.5":
                hashAlg = HashAlgorithmName.SHA1;
                break;
            case "1.2.840.113549.1.1.11":
                hashAlg = HashAlgorithmName.SHA256;
                break;
            case "1.2.840.113549.1.1.12":
                hashAlg = HashAlgorithmName.SHA384;
                break;
            case "1.2.840.113549.1.1.13":
                hashAlg = HashAlgorithmName.SHA512;
                break;
            default:
                throw new InvalidOperationException(
                    $"No support for signature algorithm '{algorithmOid}'");
        }

        // Since only RSA-SSA-PKCS1 made it here, we know the parameters are missing, or NULL.
        if (algorithm.HasData)
        {
            algorithm.ReadNull();
        }

        algorithm.ThrowIfNotEmpty();

        CertificateRequest certReq =
            DecodeCertificationRequestInfo(certificationRequestInfo, signatureHashAlgorithm);

        RSA pubKey = GetRSA(certReq.PublicKey);

        if (pubKey == null)
        {
            throw new InvalidOperationException("Requested public key was not an RSA key.");
        }

        if (!pubKey.VerifyData(encodedRequestInfo, signature, hashAlg, signaturePadding))
        {
            throw new CryptographicException();
        }

        return certReq;
    }

    private static CertificateRequest DecodeCertificationRequestInfo(
        AsnReader certReqInfo,
        HashAlgorithmName signatureHashAlgorithm)
    {
        //https://tools.ietf.org/html/rfc2986#section-4.1
        // CertificationRequestInfo::= SEQUENCE {
        //     version INTEGER { v1(0) } (v1, ...),
        //     subject Name,
        //     subjectPKInfo SubjectPublicKeyInfo{ { PKInfoAlgorithms } },
        //     attributes[0] Attributes{ { CRIAttributes } }
        // }

        // As of Sept 2020, there's not a V2 request format.
        if (!certReqInfo.TryReadInt32(out int version) || version != 0)
        {
            throw new InvalidOperationException("Only V1 requests are supported.");
        }

        byte[] encodedSubject = certReqInfo.ReadEncodedValue().ToArray();
        X500DistinguishedName subject = new X500DistinguishedName(encodedSubject);

        AsnReader spki = certReqInfo.ReadSequence();
        AsnReader reqAttrs =certReqInfo.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 0));
        certReqInfo.ThrowIfNotEmpty();

        // https://tools.ietf.org/html/rfc3280#section-4.1
        // SubjectPublicKeyInfo::= SEQUENCE {
        //     algorithm AlgorithmIdentifier,
        //     subjectPublicKey     BIT STRING
        // }

        AsnReader pubKeyAlg = spki.ReadSequence();
        string algOid = pubKeyAlg.ReadObjectIdentifier();
        byte[] algParams;

        if (pubKeyAlg.HasData)
        {
            algParams = pubKeyAlg.ReadEncodedValue().ToArray();
            pubKeyAlg.ThrowIfNotEmpty();
        }
        else
        {
            algParams = new byte[] { 0x05, 0x00 };
        }

        byte[] keyBytes = spki.ReadBitString(out int unusedBitCount);

        if (unusedBitCount != 0)
        {
            throw new InvalidOperationException(
                "The subjectPublicKey field was not made of full bytes.");
        }

        PublicKey publicKey = new PublicKey(
            new Oid(algOid, null),
            new AsnEncodedData(algParams),
            new AsnEncodedData(keyBytes));

        CertificateRequest request = new CertificateRequest(
            subject,
            publicKey,
            signatureHashAlgorithm);

        if (reqAttrs.HasData)
        {
            // This decode routine only supports one extension: the PKCS#9 extensionRequest

            // https://tools.ietf.org/html/rfc2985
            // extensionRequest ATTRIBUTE ::= {
            //     WITH SYNTAX ExtensionRequest
            //     SINGLE VALUE TRUE
            //     ID pkcs-9-at-extensionRequest
            // }
            //
            // ExtensionRequest::= Extensions

            // https://www.itu.int/ITU-T/formal-language/itu-t/x/x501/2012/InformationFramework.html
            // Attribute{ATTRIBUTE: SupportedAttributes} ::= SEQUENCE {
            //    type ATTRIBUTE.&id({SupportedAttributes}),
            //    values SET SIZE(0..MAX) OF ATTRIBUTE.&Type({SupportedAttributes}{@type}),
            //    valuesWithContext SIZE(1..MAX) OF
            //      SEQUENCE {
            //        value ATTRIBUTE.&Type({SupportedAttributes}{@type}),
            //        contextList SET SIZE(1..MAX) OF Context,
            //        ...
            //      } OPTIONAL,
            //    ...
            // }

            // https://tools.ietf.org/html/rfc5280#section-4.1
            // Extensions::= SEQUENCE SIZE(1..MAX) OF Extension
            //
            // Extension::= SEQUENCE  {
            //     extnID OBJECT IDENTIFIER,
            //     critical BOOLEAN DEFAULT FALSE,
            //     extnValue OCTET STRING
            //       --contains the DER encoding of an ASN.1 value
            //       --corresponding to the extension type identified
            //       --by extnID
            // }

            AsnReader attribute = reqAttrs.ReadSequence();
            string attrType = attribute.ReadObjectIdentifier();
            AsnReader attrValues = attribute.ReadSetOf();

            if (attrType != "1.2.840.113549.1.9.14")
            {
                throw new InvalidOperationException(
                    $"Certification Request attribute '{attrType}' is not supported.");
            }

            // No contexts are defined for the extensionRequest attribute,
            // so valuesWithContext can't exist.
            attribute.ThrowIfNotEmpty();

            // The attribute is single-value, so it must be present
            // and there mustn't be a second one.
            AsnReader extensions = attrValues.ReadSequence();
            attrValues.ThrowIfNotEmpty();

            while (extensions.HasData)
            {
                AsnReader extension = extensions.ReadSequence();
                string extnId = extension.ReadObjectIdentifier();
                bool critical = false;
                byte[] extnValue;

                if (extension.PeekTag().HasSameClassAndValue(Asn1Tag.Boolean))
                {
                    critical = extension.ReadBoolean();
                }

                extnValue = extension.ReadOctetString();
                extension.ThrowIfNotEmpty();

                X509Extension ext = new X509Extension(
                    extnId,
                    extnValue,
                    critical);

                if (CryptoConfig.CreateFromName(extnId) is X509Extension typedExtn)
                {
                    typedExtn.CopyFrom(ext);
                    ext = typedExtn;
                }

                request.CertificateExtensions.Add(ext);
            }
        }

        return request;
    }

    private static RSA GetRSA(PublicKey certReqPublicKey)
    {
        try
        {
            return certReqPublicKey.Key as RSA;
        }
        catch (CryptographicException)
        {
        }
        catch (PlatformNotSupportedException)
        {
        }

        // The try will fail on .NET Framework with any RSA key whose public exponent
        // is bigger than uint.MaxValue, because RSACryptoServiceProvider (Windows CAPI)
        // doesn't support them.

        if (certReqPublicKey.Oid.Value != "1.2.840.113549.1.1.1")
        {
            throw new InvalidOperationException(
                $"The public key algorithm '{certReqPublicKey.Oid.Value}' is not supported.");
        }

        byte[] encodedParams = certReqPublicKey.EncodedParameters.RawData;

        if (encodedParams != null && encodedParams.Length != 0)
        {
            if (encodedParams.Length != 2 ||
                encodedParams[0] != 0x05 ||
                encodedParams[1] != 0x00)
            {
                throw new InvalidOperationException(
                    "Invalid algorithm parameters for an RSA key.");
            }
        }

        AsnReader encodedKey = new AsnReader(
            certReqPublicKey.EncodedKeyValue.RawData,
            AsnEncodingRules.DER);

        // https://tools.ietf.org/html/rfc3447#appendix-A.1.1
        // RSAPublicKey::= SEQUENCE {
        //     modulus INTEGER,  --n
        //     publicExponent INTEGER   --e
        // }

        AsnReader rsaPublicKey = encodedKey.ReadSequence();
        BigInteger modulus = rsaPublicKey.ReadInteger();
        BigInteger publicExponent = rsaPublicKey.ReadInteger();
        rsaPublicKey.ThrowIfNotEmpty();

        byte[] n = modulus.ToByteArray();
        byte[] e = publicExponent.ToByteArray();

        if (n[n.Length - 1] == 0)
        {
            Array.Resize(ref n, n.Length - 1);
        }

        if (e[e.Length - 1] == 0)
        {
            Array.Resize(ref e, e.Length - 1);
        }

        Array.Reverse(n);
        Array.Reverse(e);
        
        RSAParameters rsaParameters = new RSAParameters
        {
            Modulus = n,
            Exponent = e,
        };

        RSACng rsaCng = new RSACng();
        rsaCng.ImportParameters(rsaParameters);
        return rsaCng;
    }
}
 类似资料:
  • 我正在创建一个Java程序来从服务器获取信息,但我必须从Java程序与服务器执行ssl握手。 我有文件证书用于身份验证,但我不知道如何用java加载该证书,以便java程序可以与我想要从中获取信息的服务器进行“握手”。从哪里开始?

  • 做这件事的好方法是什么?

  • 我试了几个解决方案,为了前任。HTTPS/SSL上的Java客户端证书或获取javax.net.ssl.sslHandShakeException:收到致命警报:handshake_failure错误,从.p12文件中收到的证书和从浏览器导出的证书都不起作用... 更新2: 我尝试了以下方法:https://stackoverflow.com/a/11908693/1215791,并设法访问了Se

  • 问题内容: 尝试从中的响应获取SSL证书。 什么是这样做的好方法? 问题答案: 故意包装这样的低级内容。通常,您唯一要做的就是验证证书是否有效。为此,只需通过即可。如果要使用非标准的cacert捆绑包,也可以通过。例如: 另外,主要是围绕其他库的一组包装器,主要是stdlib的(或对于2.x而言)和。 有时候,答案是只是为了获得在较低级别的对象(例如,是),但在许多情况下,这是不可能的。 这就是其

  • 下面的链接介绍了如何为Java7的安全邮件证书创建jssecacerts http://infposs.blogspot.com/2013/06/installcert-and-java-7.html 但是,一旦我试图发送邮件异常给作为"java.security.cert.证书异常:没有主题替代名称存在..." 如果我没有错,新的jssecacerts证书文件应该保存在工作目录下,我们必须手动将

  • 我正尝试仅使用.NET代码创建证书请求,并将该请求提交给我们的前提Active Directory PKI证书颁发机构,然后取回证书。我有一个已经工作了几年的解决方案,但它使用了CERTCLILib和CERTENROLLLib,我希望摆脱这些依赖关系,并将此代码移植到.NET5。 然后将这些证书导入到Yubikey设备上。我们在Yubikey上生成密钥对,然后将公钥与CSR一起使用。 这里的问题使