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

使用Bouncycastle存储PKCS#12容器(pfx)

墨安阳
2023-03-14

我正在努力创建一个pfx文件与Xamarin和BouncyCastle。我有以下设置/规格

  • .NET:PCL.NET Framework 4.5.1
  • 沙马林:4.5.0.476
  • BouncyCastle:BouncyCastle签名的1.7.0.1(NuGet包)

我想为我的移动客户端生成一个自签名证书,以便根据我的服务器对自己进行身份验证。使用BouncyCastle的创建工作非常好。我的问题是,当我想将证书及其私钥存储为PKCS#12(pfx)容器,并从该容器中还原它以发送由它签名的web请求时,会引发一个异常,告诉我输入数据不能编码为有效证书

下面是创建证书、密钥和存储pfx容器的步骤。

private AsymmetricCipherKeyPair GenerateKeyPair()
{
    var random = new SecureRandom();
    var keyGenerationParameter = new KeyGenerationParameters(random, 4096);
    var keyPairGenerator = new RsaKeyPairGenerator();
    keyPairGenerator.Init(keyGenerationParameter);
    var keyPair = keyPairGenerator.GenerateKeyPair();

   return keyPair;
}
public void CreateCertificate()
{
    var random = new SecureRandom();
    var keyPair = this.GenerateKeyPair();

    // generate certificate generator and set public key.
    var generator = new X509V3CertificateGenerator();
    generator.SetPublicKey(keyPair.Public);

    // generate and set serial number
    BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
    generator.SetSerialNumber(serialNumber);

    // set signing algorithm.
    generator.SetSignatureAlgorithm(Core.Constants.SignatureAlgorithmName);

    // set name
    string fullQualifiedName = $"CN=dummy,O=DummyOrg,OU=Dummy";
    var name = new X509Name(fullQualifiedName);
    generator.SetSubjectDN(name);
    generator.SetIssuerDN(name);

    // set valide time
    generator.SetNotBefore(DateTime.UtcNow.AddHours(-1));
    generator.SetNotAfter(DateTime.UtcNow.AddYears(10));

    // add extensions.
    var authorityKeyIdentifier = new AuthorityKeyIdentifier(
        SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public).GetEncoded(),
        new GeneralNames(new GeneralName(name)),
        serialNumber);
    var subjectKeyIdentifier = new SubjectKeyIdentifier(
        SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public).GetEncoded());
    generator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, authorityKeyIdentifier);
    generator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, subjectKeyIdentifier);
    generator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(0));

    // generate and validate certificate.
    var cert = generator.Generate(keyPair.Private, random);
    cert.CheckValidity(DateTime.UtcNow);
    cert.Verify(keyPair.Public);

    // generate pem string
    using (var stringWriter = new StringWriter())
    {
        var writer = new PemWriter(stringWriter);
        var pog = new PemObject("CERTIFICATE", cert.GetEncoded());
        writer.WriteObject(pog);
        // pem value
        var value = stringWriter.ToString();
        this.StoreCertificate(value);
    }

    // store the private key
    using (var stringWriter = new StringWriter())
    {
        var writer = new PemWriter(stringWriter);
        PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
        var pog = new PemObject("RSA PRIVATE KEY", info.ToAsn1Object().GetEncoded());
        writer.WriteObject(pog);
        // pem value
        var value = stringWriter.ToString();
        this.StorePrivateKey(value);
    }
    try
    {
        this.CreatePfxFile();
    }
    catch (Exception ex)
    {
        throw;
    }
}
    public void CreatePfxFile()
{
    var certificate = this.GetCertificate();
    var certEntry = new X509CertificateEntry(certificate);
    string friendlyName = certificate.SubjectDN.ToString();
    var privateKey = this.ReadPrivateKey();

    PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey);
    byte[] keyBytes = info.ToAsn1Object().GetEncoded();

    var store = new Pkcs12StoreBuilder().Build();

    var keyEntry = new AsymmetricKeyEntry(privateKey, );

    ////store.SetCertificateEntry(Core.Constants.CertificateAlias, certEntry);
    store.SetKeyEntry(Core.Constants.PrivateKeyAlias, new AsymmetricKeyEntry(privateKey), new X509CertificateEntry[] { certEntry });

    MemoryStream stream = new MemoryStream();
    store.Save(stream, Core.Constants.Password.ToCharArray(), new SecureRandom());

    var pfxBytes = stream.ToArray();

    var base64 = Convert.ToBase64String(pfxBytes);

    this.StorePfxContainer(base64);
 }

方法GetCertifice()ReadPrivateKey()将返回X509CertificeAsymmetricKeyParameter。方法storepfxcontainer将只将base64字符串存储到设置变量中。

注意:我删除了store.setCertificateEntry(core.constants.certificateAlias,certEntry);行,因为它导致pfx容器两次包含证书。

请不要,因为现在我们只是使用BouncyCastle的东西。

private void SetCertificate(HttpWebRequest request, byte[] cert)
{
    try
    {
        System.Security.Cryptography.X509Certificates.X509Certificate2 certificate =
        new System.Security.Cryptography.X509Certificates.X509Certificate2(cert, Core.Constants.Password); // <-- throws the exception

        request.ClientCertificates.Add(certificate);
    }
    catch (Exception ex)
    {
        string message = ex.Message;
    }
}

我用openssl测试了证书和密钥,我还尝试从我的pem证书和pem私钥生成一个pfx容器,也很好地工作。只有当我将证书加载到代码中时,它才会不起作用。所以我认为容器的创建有一个我现在还没有弄清楚的bug。

谢谢你的帮助。

共有1个答案

胡景焕
2023-03-14

经过更多的尝试和错误,我发现了我的实现的错误所在。

首先,我认为我所经历的行为是PCL和Mono的弹力城堡的结合。

我生成pfx容器的新方法如下所示。

private void CreatePfxFile(X509Certificate certificate, AsymmetricKeyParameter privateKey)
{
    // create certificate entry
    var certEntry = new X509CertificateEntry(certificate);
    string friendlyName = certificate.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();

    // create store entry
    store.SetKeyEntry(Core.Constants.PrivateKeyAlias, new AsymmetricKeyEntry(privateKey), new X509CertificateEntry[] { certEntry });

    byte[] pfxBytes = null;

    var password = Guid.NewGuid().ToString("N");

    using (MemoryStream stream = new MemoryStream())
    {
        store.Save(stream, password.ToCharArray(), new SecureRandom());
        pfxBytes = stream.ToArray();
    }

    var result = Pkcs12Utilities.ConvertToDefiniteLength(pfxBytes);
    this.StoreCertificate(Convert.ToBase64String(result));
}

第三个更改是使用pkcs12urialles.convertToDefiniteLength()方法为数据设置一个确定的长度。

经过这些更改后,我的证书可以存储并恢复,不会出现任何问题。

注意:还要确保您没有使用string.empty作为密码来保存容器。我经常看到这是与容器的问题的答案。

 类似资料:
  • 问题内容: 我使用keytool命令生成证书: 然后,如果我尝试使用java安全性API加载它,则在将文件作为字节[]获得后: 我得到一个DerInputStream.getLength():lengthTag = 127,太大的异常。 怎么了? 问题答案: 我遇到了这个问题,我已经搜索了谷歌的深度,但仍然找不到答案。经过几天的苦苦挣扎之后,我发现了导致此错误的原因。 此方法使用一个InputSt

  • 本章将介绍在Qt5中使用QtQuick存储数据。QtQuick只提供了有限的方法来直接存储本地数据。在这样的场景下,它更多的扮演了一个浏览者的角色。在大多数项目中,存储数据由C++后端来完成,并需要将这个功能导入到QtQuick前端。QtQucik没有提供类似Qt C++的主机文件系统接口来读取和写入文件。所以后端工程师需要编写一个这样的插件或者使用网络通道与本地服务器通信来提供这些功能。 每个应

  • 我花了无数个小时在这个图书馆里,但仍然无法让它工作。 我想用充气城堡库发送SMIME消息,用RSASSA-PSS签名,用AES加密,其中密钥传输应该是RSAES-OAEP,所有P1#v2.1 签名者优先,这是它的创建方式: 所以现在当它应该被签名、加密和使用OAEP填充时: 我找不到正确的setAlgorithmMapping()说明,因此尝试了以下组合: 顺便说一句,有人能解释一下,这个模式在这

  • 我有一篇docker文章。yml,我有一个Postgres数据库,Grafana在上面运行以查询数据。 我用命令docker compose up启动这个compose,但是如果我不想丢失任何数据,我必须运行docker compose stop,而不是docker compose down。 我还阅读了,但“提交操作将不包括安装在容器内的卷中包含的任何数据”,所以我想它对我的需求没有用。 存储创

  • 我在Windows7上有OpenSSL x64,这是我从Google代码上的OpenSSL-for Windows下载的。我正试图跑: 但我有个错误。 如何使用OpenSSL从PKCS#12存储中提取PEM中的证书?

  • 作为Docker的菜鸟,考虑使用大小为几百千兆字节或更多的SQL服务器进行存储,在我看来,在容器中存储这么多是不可行的。加载一个大文件需要时间,太字节范围内的文件的合理位置是将它与容器分开装载。 在谷歌搜索这些信息几天后,询问社区似乎更符合逻辑。希望一幅画能抵得上千言万语。 SQL Server容器如何装载外部SQL Server源(mdf、ldf、ndf),因为这些源位于Fortress(请参见