简体   繁体   English

是否可以仅使用C#以编程方式生成X509证书?

[英]Is it possible to programmatically generate an X509 certificate using only C#?

We're trying to generate an X509 certificate (including the private key) programmatically using C# and the BouncyCastle library. 我们正在尝试使用C#和BouncyCastle库以编程方式生成X509证书(包括私钥)。 We've tried using some of the code from this sample by Felix Kollmann but the private key part of the certificate returns null. 我们尝试使用Felix Kollmann的这个示例中的一些代码,但证书的私钥部分返回null。 Code and unit test are as below: 代码和单元测试如下:

using System;
using System.Collections;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;

namespace MyApp
{
    public class CertificateGenerator
    {
        /// <summary>
        /// 
        /// </summary>
        /// <remarks>Based on <see cref="http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx"/></remarks>
        /// <param name="subjectName"></param>
        /// <returns></returns>
        public static byte[] GenerateCertificate(string subjectName)
        {
            var kpgen = new RsaKeyPairGenerator();

            kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024));

            var kp = kpgen.GenerateKeyPair();

            var gen = new X509V3CertificateGenerator();

            var certName = new X509Name("CN=" + subjectName);
            var serialNo = BigInteger.ProbablePrime(120, new Random());

            gen.SetSerialNumber(serialNo);
            gen.SetSubjectDN(certName);
            gen.SetIssuerDN(certName);
            gen.SetNotAfter(DateTime.Now.AddYears(100));
            gen.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0)));
            gen.SetSignatureAlgorithm("MD5WithRSA");
            gen.SetPublicKey(kp.Public);

            gen.AddExtension(
                X509Extensions.AuthorityKeyIdentifier.Id,
                false,
                new AuthorityKeyIdentifier(
                    SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public),
                    new GeneralNames(new GeneralName(certName)),
                    serialNo));

            gen.AddExtension(
                X509Extensions.ExtendedKeyUsage.Id,
                false,
                new ExtendedKeyUsage(new ArrayList() { new DerObjectIdentifier("1.3.6.1.5.5.7.3.1") }));

            var newCert = gen.Generate(kp.Private);
            return DotNetUtilities.ToX509Certificate(newCert).Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "password");
        }
    }
}

Unit test: 单元测试:

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace MyApp
{
    [TestClass]
    public class CertificateGeneratorTests
    {
        [TestMethod]
        public void GenerateCertificate_Test_ValidCertificate()
        {
            // Arrange
            string subjectName = "test";

            // Act
            byte[] actual = CertificateGenerator.GenerateCertificate(subjectName);

            // Assert
            var cert = new X509Certificate2(actual, "password");
            Assert.AreEqual("CN=" + subjectName, cert.Subject);
            Assert.IsInstanceOfType(cert.PrivateKey, typeof(RSACryptoServiceProvider));
        }
    }
}

Just to clarify, an X.509 certificate does not contain the private key. 只是为了澄清,X.509证书不包含私钥。 The word certificate is sometimes misused to represent the combination of the certificate and the private key, but they are two distinct entities. 单词certificate有时被误用来表示证书和私钥的组合,但它们是两个不同的实体。 The whole point of using certificates is to send them more or less openly, without sending the private key, which must be kept secret. 使用证书的重点是或多或少地公开发送它们,而不必发送私钥,私钥必须保密。 An X509Certificate2 object may have a private key associated with it (via its PrivateKey property), but that's only a convenience as part of the design of this class. X509Certificate2对象可能有一个与之关联的私钥(通过其PrivateKey属性),但这只是这个类设计的一部分。

In your first BouncyCastle code example, newCert is really just the certificate and DotNetUtilities.ToX509Certificate(newCert) is built from the certificate only. 在您的第一个BouncyCastle代码示例中, newCert实际上只是证书而DotNetUtilities.ToX509Certificate(newCert)仅从证书构建。

Considering that the PKCS#12 format requires the presence of a private key, I'm quite surprised that the following part even works (considering you're calling it on a certificate which can't possibly know the private key): 考虑到PKCS#12格式需要私钥的存在,我很惊讶以下部分甚至可以工作(考虑到你在一个不可能知道私钥的证书上调用它):

.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12,
    "password");

( gen.Generate(kp.Private) signs the certificate using the private key, but doesn't put the private key in the certificate, which wouldn't make sense.) gen.Generate(kp.Private)使用私钥对证书进行签名,但不会将私钥放入证书中,这是没有意义的。)

If you want your method to return both the certificate and the private key you could either: 如果您希望您的方法同时返回证书和私钥,您可以:

  • Return an X509Certificate2 object in which you've initialized the PrivateKey property 返回已初始化PrivateKey属性的X509Certificate2对象
  • Build a PKCS#12 store and returns its byte[] content (as if it was a file). 构建一个PKCS#12存储并返回其byte[]内容(就像它是一个文件一样)。 Step 3 in the link you've sent ( mirror ) explains how to build a PKCS#12 store. 您发送的链接中的第3步镜像 )解释了如何构建PKCS#12商店。

Returning the byte[] (DER) structure for the X.509 certificate itself will not contain the private key. 返回X.509证书本身的byte[] (DER)结构将不包含私钥。

If your main concern (according to your test case) is to check that the certificate was built from an RSA key-pair, you can check the type of its public key instead. 如果您主要关注(根据您的测试用例)要检查证书是否是从RSA密钥对构建的,则可以检查其公钥的类型。

I realise this is an old post but I found these excellent articles which go through the process: 我意识到这是一个老帖子,但我发现这些优秀的文章经历了这个过程:

Using Bouncy Castle from .NET 使用.NET的Bouncy Castle

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM