如何在c#中为DSA对创建具有自签名证书的pkcs12p12文件



我需要生成我自己的DSA密钥对,并使用c#将其作为私钥和证书的捆绑包存储在。p12文件中。

这个问题

如何在c#中创建PKCS12 .p12文件?

似乎非常相似,但不幸的是它对我没有帮助,因为有一些显著的差异(RSA与DSA等)

我正在尝试使用System.Security.Cryptography.DSACryptoServiceProvider生成密钥对,然后使用Bouncy Castle生成X509证书:

using (DSACryptoServiceProvider csp = new DSACryptoServiceProvider(1024))
{
       privKeyDSA = csp.ExportParameters(true);
       pubKeyDSA = csp.ExportParameters(false);
       var keypair = DotNetUtilities.GetDsaKeyPair(privKeyDSA);
       var gen = new X509V3CertificateGenerator();
       var CN = new X509Name("CN=" + "TEST");
       var SN = BigInteger.ProbablePrime(120, new Random());
       gen.SetSerialNumber(SN);
       gen.SetSubjectDN(CN);
       gen.SetIssuerDN(CN);
       gen.SetNotAfter(DateTime.MaxValue);
       gen.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0)));
       gen.SetSignatureAlgorithm("sha1WithDSA");
       gen.SetPublicKey(DotNetUtilities.GetDsaPublicKey(pubKeyDSA));
       var newCert = gen.Generate(keypair.Private);

       certificateDSA = new X509Certificate2(DotNetUtilities.ToX509Certificate((Org.BouncyCastle.X509.X509Certificate)newCert));
       certificateDSA.PrivateKey = csp;
       StringBuilder builder = new StringBuilder();
        builder.AppendLine("-----BEGIN CERTIFICATE-----");
        builder.AppendLine(Convert.ToBase64String(certificateDSA.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
        builder.AppendLine("-----END CERTIFICATE-----");
        string result = builder.ToString();
        byte[] pkcsData = certificateDSA.Export(X509ContentType.Pfx, "changeit");
}

然而,certificateDSA.PrivateKey = csp;行抛出CryptographicUnexpectedOperationException,消息为:"The public key of the certificate does not match the value specified."

我真的不明白发生了什么事。我做错了什么?谢谢!

对此很感兴趣,这是我的小调查。当您设置certificateDSA的PrivateKey时,.NET代码大致是这样做的:

byte[] numArray1 = ((ICspAsymmetricAlgorithm) certificateDSA.PublicKey.Key).ExportCspBlob(false);
byte[] numArray2 = csp.ExportCspBlob(false);
// And then those two blobs are compared byte by byte

这些blobs从位置420开始是不同的(它们的长度为444)。所以csp参数有问题。比较原始字节并不容易,所以让我们使用以下命令将它们转换为可读的xml:

var xml1 = certificateDSA.PublicKey.Key.ToXmlString(false);
var xml2 = csp.ToXmlString(false);

我们得到的结果是:

<DSAKeyValue> <!--this is parameters of cert public key-->
    <P>2arEQPD3/tKm7pJF1y4gN0/4WzSGfkgFwdmtmoUf/gHoXpdBetRH/5j98qo4k1ybePxM4om4y6n9vhxijocMw5LaeQPceGyNOEScWXXrNKAcUsK74klQmiPOoI2qI1zU5v2HrilKmkOELH81U8/Qmmjmg7ouOdOHqlZAxW9Sv8M=</P>
    <Q>lzRdUtp56eZHIgxRemvdHciGIfc=</Q>
    <G>Z/2T+jXvv0ZLswbuMd9DxrHldakJxZ8JNGRf1QzN09B2VO9WYAzUy0S+J8hbYQjP/jzWbmL5LaK57v+MUOmOHzFwNqfVMe9OUglUfF3nN990ur9hp6csu8+vCEQt3EoI8Wmh/b2yqhtKRN6U494vf33WKo1NCNQapB+iWVQ/egQ=</G>
    <Y>ykcPXFIxWvYDDbbY05oD3hD6LsM5rk76FakUY8YiCo8ZwWbMIlQw+v5nOYS9vpQaZAzUqxx9OXIGSTUGItruTARkDqZ0nGKL0r94Zhog1Y0wU2AVKJh8Vjq/dLFyDDGZZsxBZtmI8TDyKGJbZqvzGbdGLhoRxRFmNi1fVsADv+U=</Y>
</DSAKeyValue>
<DSAKeyValue> <!-- this is paramteres of original DSACryptoServiceProvider-->
    <P>2arEQPD3/tKm7pJF1y4gN0/4WzSGfkgFwdmtmoUf/gHoXpdBetRH/5j98qo4k1ybePxM4om4y6n9vhxijocMw5LaeQPceGyNOEScWXXrNKAcUsK74klQmiPOoI2qI1zU5v2HrilKmkOELH81U8/Qmmjmg7ouOdOHqlZAxW9Sv8M=</P>
    <Q>lzRdUtp56eZHIgxRemvdHciGIfc=</Q>
    <G>Z/2T+jXvv0ZLswbuMd9DxrHldakJxZ8JNGRf1QzN09B2VO9WYAzUy0S+J8hbYQjP/jzWbmL5LaK57v+MUOmOHzFwNqfVMe9OUglUfF3nN990ur9hp6csu8+vCEQt3EoI8Wmh/b2yqhtKRN6U494vf33WKo1NCNQapB+iWVQ/egQ=</G>
    <Y>ykcPXFIxWvYDDbbY05oD3hD6LsM5rk76FakUY8YiCo8ZwWbMIlQw+v5nOYS9vpQaZAzUqxx9OXIGSTUGItruTARkDqZ0nGKL0r94Zhog1Y0wU2AVKJh8Vjq/dLFyDDGZZsxBZtmI8TDyKGJbZqvzGbdGLhoRxRFmNi1fVsADv+U=</Y>
    <Seed>1hiZoCQFivF9xDZdQEGue65oObA=</Seed>
    <PgenCounter>Og==</PgenCounter>
</DSAKeyValue>

您可以看到原始DSACryptoServiceProvider包含Seed和PgenCounter,而在使用Bouncy Castle生成证书后,证书的公钥不包含它们。这些参数是可选的(从某种意义上说,公钥可能不包含它们),但是如果它们存在,它们应该同时存在于双方(私有和公共)。我们如何解决这个问题?下面是代码:

using (DSACryptoServiceProvider csp = new DSACryptoServiceProvider(1024)) {
            var parameters = csp.ExportParameters(true);                
            var keypair = DotNetUtilities.GetDsaKeyPair(parameters);
            var gen = new X509V3CertificateGenerator();
            var CN = new X509Name("CN=" + "TEST");
            var SN = BigInteger.ProbablePrime(120, new Random());
            gen.SetSerialNumber(SN);
            gen.SetSubjectDN(CN);
            gen.SetIssuerDN(CN);
            gen.SetNotAfter(DateTime.Now.AddDays(1));
            gen.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0)));
            gen.SetSignatureAlgorithm("sha1WithDSA");
            gen.SetPublicKey(keypair.Public);
            var newCert = gen.Generate(keypair.Private);
            var certificateDSA = new X509Certificate2(DotNetUtilities.ToX509Certificate(newCert));
            // added block
            parameters.Seed = new byte[20];
            unchecked {
                parameters.Counter = (int) 0xFFFFFFFF;
            }
            csp.ImportParameters(parameters);
            // end of added block
            certificateDSA.PrivateKey = csp;
            StringBuilder builder = new StringBuilder();
            builder.AppendLine("-----BEGIN CERTIFICATE-----");
            builder.AppendLine(Convert.ToBase64String(certificateDSA.Export(X509ContentType.Pkcs12), Base64FormattingOptions.InsertLineBreaks));
            builder.AppendLine("-----END CERTIFICATE-----");
            string result = builder.ToString();                
        }

我们在这里做的是在生成所有内容之后,但在为证书分配私钥之前,我们从DSACryptoServiceProvider参数中"删除"种子和计数器。这段代码不会抛出错误,并且完成得很好。也许在这个解决方法中有一些警告,但是它可能对进一步调查问题有用,即使它不能完全修复它。

最新更新