使用根证书和链生成PFX

时间:2019-04-03 13:13:01

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

我需要一些帮助来生成带有弹力城堡的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%

将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,该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());



        }

谢谢

1 个答案:

答案 0 :(得分:0)

这个问题花了比我诚实的预期更长的时间来解决。显然,有多种方法可以使用各种方法来导出PFX或P12二进制格式,但是为了简单起见,我将使用BouncyCastle库。

首先,我使用的是克里斯托弗(Cristoph)的答案,您可以在https://stackoverflow.com/a/44798441/5797504处找到链接。本质上,它使用的是Pkcs12StoreBuilder,它很干净而且很整洁,还使用了MemoryStream,我不知道为什么,但是无论如何我还是会用它。

其次,我修改了答案以适应证书链和其他一些显而易见的事情。

最重要的部分是创建X509CertificateEntry[]的数组,并从主证书/最终证书,中间CA(sub-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文件)的完整方法。

如果要归功于克里斯托夫(Cristoph)作为原作者,我会给予荣誉。

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;
        }