【问题标题】:Generate PFX with root cert and chain使用根证书和链生成 PFX
【发布时间】:2019-04-03 13:13:00
【问题描述】:

我需要一些帮助来使用 bouncycastle 生成 PKCS12 pfx 文件。

我正在使用休闲命令来生成 PKCS12 pfx 文件:

keytool -genkey -storetype PKCS12 -dname "CN=%CN, OU=%OU, O=Company, L=City, ST=State, C=US" -alias clientcert -keyalg RSA -keysize 2048 -keystore %keystore_name% -storepass %default_keystore_pwd% -keypass %default_keystore_pwd%

以 root 身份导入 ca.crt:

keytool -import -trustcacerts -noprompt -alias root -file ca.crt -keystore %keystore_name% -storepass %default_keystore_pwd% 

ca.crt 是用于签署生成的 CSR 的根证书

keytool -certreq -alias clientcert -keystore %keystore_name% -file clientcert.csr -keypass %default_keystore_pwd% -storepass %default_keystore_pwd%

此时,我获得了 CSR,我使用 ca.crt

在专用服务器上签名

然后我将签名证书导入 pfx:

keytool -import -alias clientcert -file signed.crt -keystore %keystore_name% -storepass %default_keystore_pwd% -keypass %default_keystore_pwd%

我正在使用 bouncycastle 库创建 CSR 和私钥。然后我用 ca.crt 在服务器上签署 CSR。

最终文件夹中有3个文件:

  • ca.crt
  • signed.crt - 用 ca.crt 签名的证书
  • private_key.key(未加密的 RSA 密钥)

使用我之前提供的命令,最终的 pfx 文件在提取时如下所示:

keytool -list -rfc -keystore client_keystore.pfx
Enter keystore password:
Keystore type: jks
Keystore provider: SUN

Your keystore contains 2 entries

Alias name: clientcert
Creation date: Mar 22, 2019
Entry type: PrivateKeyEntry
Certificate chain length: 2
Certificate[1]:
-----BEGIN CERTIFICATE-----
//removed
-----END CERTIFICATE-----
Certificate[2]:
-----BEGIN CERTIFICATE-----
//removed
-----END CERTIFICATE-----


*******************************************
*******************************************


Alias name: root
Creation date: Apr 3, 2019
Entry type: trustedCertEntry

-----BEGIN CERTIFICATE-----
//removed
-----END CERTIFICATE-----


*******************************************
*******************************************

我已经签署了 ca.crt、certificate.crt 和 private.key。 如何使用 bouncycastle 库创建相同结构的 pfx 文件?

生成 CSR 和密钥:

public void TDE(string CName, string OUnit, string Country, string State, string City, string EmailAdr, string password)
        {



            AsymmetricCipherKeyPair pair;
            Pkcs10CertificationRequest csr;
            Asn1SignatureFactory signatureFactory;
            var random = new SecureRandom(new CryptoApiRandomGenerator());

            var values = new Dictionary<DerObjectIdentifier, string>
            {
                {X509Name.CN, CName},
                {X509Name.OU, OUnit},
                {X509Name.O, "Company"},
                {X509Name.L, City},
                {X509Name.ST, State},
                {X509Name.C, Country},
                {X509Name.EmailAddress, EmailAdr },
            };


            var extensions = new Dictionary<DerObjectIdentifier, X509Extension>()
            {
                {X509Extensions.BasicConstraints, new X509Extension(true, new DerOctetString(new BasicConstraints(false)))},
                {X509Extensions.KeyUsage, new X509Extension(true, new DerOctetString(new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.KeyEncipherment | KeyUsage.DataEncipherment | KeyUsage.NonRepudiation)))},
                {X509Extensions.ExtendedKeyUsage, new X509Extension(false, new DerOctetString(new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth, KeyPurposeID.IdKPClientAuth)))},
            };

            var subject = new X509Name(values.Keys.Reverse().ToList(), values);


            var gen = new RsaKeyPairGenerator();
            gen.Init(new KeyGenerationParameters(random, 2048));


            pair = gen.GenerateKeyPair();
            signatureFactory = new Asn1SignatureFactory("SHA256withRSA", pair.Private);

            extensions.Add(X509Extensions.SubjectKeyIdentifier, new X509Extension(false, new DerOctetString(new SubjectKeyIdentifierStructure(pair.Public))));
            csr = new Pkcs10CertificationRequest(signatureFactory, subject, pair.Public, new DerSet(new AttributePkcs(PkcsObjectIdentifiers.Pkcs9AtExtensionRequest, new DerSet(new X509Extensions(extensions)))), pair.Private);


            //Convert BouncyCastle csr to .PEM file.
            var csrPem = new StringBuilder();
            var csrPemWriter = new PemWriter(new StringWriter(csrPem));
            csrPemWriter.WriteObject(csr);
            csrPemWriter.Writer.Flush();

            //Writes password to file
            Directory.CreateDirectory(Environment.CurrentDirectory + "\\" + CName + "_" + OUnit);
            File.AppendAllText(Environment.CurrentDirectory + "\\" + CName + "_" + OUnit + "\\key_password.txt", password);

            //writes CSR to file
            File.AppendAllText(Environment.CurrentDirectory + "\\" + CName + "_" + OUnit + "\\" + CName + "_csr", csrPem.ToString());


            //Convert BouncyCastle Private Key to .PEM file.
            var privateKeyPem = new StringBuilder();
            var privateKeyPemWriter = new PemWriter(new StringWriter(privateKeyPem));
            privateKeyPemWriter.WriteObject(pair.Private);
            privateKeyPemWriter.Writer.Flush();

            //privateKeyPem.ToString();
            File.AppendAllText(Environment.CurrentDirectory + "\\" + CName + "_" + OUnit + "\\" + CName + "_" + OUnit + "_prvNE.key", privateKeyPem.ToString());



        }

谢谢

【问题讨论】:

    标签: c# .net cryptography bouncycastle pkcs#12


    【解决方案1】:

    这个问题花费的时间比我真正预期的要长。显然,有几种方法可以使用各种方式导出 PFX 或 P12 二进制格式,但为了简单起见,我将使用 BouncyCastle 库。

    首先,我使用了 Cristoph 的答案,您可以在此处找到链接 https://stackoverflow.com/a/44798441/5797504。本质上,它使用Pkcs12StoreBuilder,它很整洁,很干净,MemoryStream,我不知道为什么,但我还是会用它。

    其次,我修改了答案以适应证书链和其他一些很明显的东西。

    最重要的部分是创建一个X509CertificateEntry[] 数组,并创建从您的主/最终证书、中间 CA(子 CA)一直到您的根 CA 证书的证书链。该数组需要进入SetKeyEntry 方法参数。

                // create chain certificate
                var rootCert = DotNetUtilities.FromX509Certificate(rootCA);
                var endCert = DotNetUtilities.FromX509Certificate(x509Cert);
    
                X509CertificateEntry[] chains = new X509CertificateEntry[2];
                chains[0] = new X509CertificateEntry(endCert);
                chains[1] = new X509CertificateEntry(rootCert);
                ////....
                store.SetKeyEntry(commonName, new AsymmetricKeyEntry(privateKey), chains);
    

    以下部分显示了我如何创建 PKCS12 文件的完整方法,无论是 PFX 文件还是 P12 文件。

    我要归功于克里斯托夫作为原作者。

    public static byte[] CreatePkcs12File(X509Certificate2 x509Cert, X509Certificate2 rootCA, AsymmetricKeyParameter privateKey, string passphrase)
            {
                // create store entry
                string commonName = x509Cert.GetNameInfo(X509NameType.SimpleName, false);
    
                // create chain certificate
                var rootCert = DotNetUtilities.FromX509Certificate(rootCA);
                var endCert = DotNetUtilities.FromX509Certificate(x509Cert);
    			
                X509CertificateEntry[] chains = new X509CertificateEntry[2];
                chains[0] = new X509CertificateEntry(endCert);
                chains[1] = new X509CertificateEntry(rootCert);
    
                // create certificate entry
                var certEntry = new X509CertificateEntry(endCert);
                string friendlyName = endCert.SubjectDN.ToString();
    
                // get bytes of private key.
                PrivateKeyInfo keyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey);
                byte[] keyBytes = keyInfo.ToAsn1Object().GetEncoded();
    
                var builder = new Pkcs12StoreBuilder();
                builder.SetUseDerEncoding(true);
                var store = builder.Build();            
                
                store.SetKeyEntry(commonName, new AsymmetricKeyEntry(privateKey), chains);
                byte[] pfxBytes = null;
    
                var password = passphrase;
                
                using (MemoryStream stream = new MemoryStream())
                {
                    store.Save(stream, password.ToCharArray(), new SecureRandom());
                    pfxBytes = stream.ToArray();
                }
    
                var result = Pkcs12Utilities.ConvertToDefiniteLength(pfxBytes);
                return result;
            }

    【讨论】:

    • 您好,感谢您的回答,但还有更多问题。 store.SetKeyEntry("clientcert", new AsymmetricKeyEntry(privateKey),chains) 创建带有签名证书、rootCA 和私钥的证书链。我还需要再添加一个链,它只包含没有 prvate 密钥的 rootCA。我试过 store.SetCertificateEntry("root", rootCAentry);但它没有出现在最终的 PKSC12 密钥库中。
    • 嗨,据我了解。您试图创建一个包含您自己的证书及其根的 p12/pfx 文件。现在您正尝试在您的根证书和您自己的证书之间添加一个中间 CA(称为子 CA)?那是对的吗?如果是这样,只需在您自己的证书 (endCert) (chain[0]) 之间添加另一个链 (chain[1]) 并将您的 rootCert 移动到最后 (chain[2])。
    • 所以在我的理解中,您希望创建一个结构如下的 PKCS#12:(1)您自己的证书(2)中间 CA(3)根 CA。对于数字 (1)。您将需要 (1.a) 您的私钥 (1.b) 您的签名证书 - 中间 CA 还包含公钥。另一方面,对于 num (2) 和 (3),您只需要他们的证书(公钥)而不需要他们的私钥。对不起帽子
    • 不完全是。请看看我的问题。第二个链是trustedCAentry 别名:root 条目类型:trustedCertEntry -----BEGIN CERTIFICATE----- //removed -----END CERTIFICATE----- 第二个链包含与第一个链第二个条目相同的rootCA : 链 [0] - 签名证书链 [1] - rootCA 用于签署 shain [0] 所以我需要添加的第二个链与链 [1] 相同,但没有任何私钥:链 1 [0] == 链 [ 1]
    猜你喜欢
    • 1970-01-01
    • 2021-02-06
    • 2013-09-18
    • 1970-01-01
    • 2019-12-09
    • 1970-01-01
    • 1970-01-01
    • 2018-02-27
    • 1970-01-01
    相关资源
    最近更新 更多