如何在不使用过时的BouncyCastle 1.7.0代码的情况下生成自签名证书?

13
我有以下代码来生成一个漂亮的自签名证书,运行良好,但我想更新到最新的BouncyCastle(1.8.1.0),并且我收到了有关过时用法的警告。
var persistedCertificateFilename = "ClientCertificate.pfx";
if (!string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["PersistedCertificateFilename"])) { persistedCertificateFilename = ConfigurationManager.AppSettings["PersistedCertificateFilename"].Trim(); }
if (persistCertificateToDisk)
{
    if (File.Exists(persistedCertificateFilename))
    {
        var certBytes = File.ReadAllBytes(persistedCertificateFilename);
                this.clientCertificate = new X509Certificate2(certBytes, (string) null, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
    }
}

if (this.clientCertificate == null)
{
    // Initialize the new secure keys
    KeyGenerator keyGenerator = KeyGenerator.Create();
    KeyPair keyPair = keyGenerator.GenerateKeyPair();
    this.privateKey = keyPair.ToEncryptedPrivateKeyString(privateKeySecret);
    this.publicKey = keyPair.ToPublicKeyString();

    // Client certificate permissions
    var certificatePermissions = new ArrayList()
    {
         KeyPurposeID.IdKPCodeSigning,
         KeyPurposeID.IdKPServerAuth,
         KeyPurposeID.IdKPTimeStamping,
         KeyPurposeID.IdKPOcspSigning,
         KeyPurposeID.IdKPClientAuth
    };

    // Initialize the certificate generation
    var certificateGenerator = new X509V3CertificateGenerator();
    BigInteger serialNo = BigInteger.ProbablePrime(128, new Random());
    certificateGenerator.SetSerialNumber(serialNo);
    certificateGenerator.SetSubjectDN(GetLicenseeDN());
    certificateGenerator.SetIssuerDN(GetLicencerDN());
    certificateGenerator.SetNotAfter(DateTime.Now.AddYears(100));
    certificateGenerator.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0)));
    //ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", keyPair.PrivateKey); // ??
    certificateGenerator.SetSignatureAlgorithm("SHA512withRSA");
    certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, false, new ExtendedKeyUsage(certificatePermissions));
    var subjectKeyIdentifier = new SubjectKeyIdentifier(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.PublicKey));
    certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier.Id, false, subjectKeyIdentifier);
    certificateGenerator.SetPublicKey(keyPair.PublicKey);
    var result = certificateGenerator.Generate(keyPair.PrivateKey);
    var secure = new SecureString();
    foreach (char c in privateKeySecret)
    {
        secure.AppendChar(c);
    }

    X509KeyStorageFlags flags = X509KeyStorageFlags.MachineKeySet;
    if (persistCertificateToDisk) { flags |= X509KeyStorageFlags.Exportable; flags |= X509KeyStorageFlags.PersistKeySet; }
    this.clientCertificate = new X509Certificate2(Org.BouncyCastle.Security.DotNetUtilities.ToX509Certificate(result).Export(X509ContentType.Cert), secure, flags);

    // This section allows us to use this certificate on Azure (no file access required)
    CspParameters cspParams;
    const int PROVIDER_RSA_FULL = 1;
    cspParams = new CspParameters(PROVIDER_RSA_FULL);
    cspParams.KeyContainerName = new Guid().ToString();
    cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
    cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
    var rule = new CryptoKeyAccessRule("everyone", CryptoKeyRights.FullControl, AccessControlType.Allow);
    cspParams.CryptoKeySecurity = new CryptoKeySecurity();
    cspParams.CryptoKeySecurity.SetAccessRule(rule);

    // Set the private key
    var tempRcsp = (RSACryptoServiceProvider) Org.BouncyCastle.Security.DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters) keyPair.PrivateKey);
    var rcsp = new RSACryptoServiceProvider(cspParams);
    rcsp.ImportCspBlob(tempRcsp.ExportCspBlob(true));
    this.clientCertificate.PrivateKey = rcsp;

    if (persistCertificateToDisk)
    {
        if (!File.Exists(persistedCertificateFilename))
        {
            File.WriteAllBytes(persistedCertificateFilename, this.clientCertificate.Export(X509ContentType.Pkcs12, (string) null));
        }
    }
}

具体而言,警告如下:

'X509V3CertificateGenerator.SetSignatureAlgorithm(string)'已过时:'如果使用ISignatureFactory与Generate一起使用,则不需要'

以及

'X509V3CertificateGenerator.Generate(AsymmetricKeyParameter)'已过时:'请使用带有ISignatureFactory的Generate'

因此,我的问题是:

  1. 我需要担心这些警告吗?
  2. 如果需要,哪些行需要更改?
  3. 如果我更新此代码,是否会有性能收益?

注意:如果有人好奇,我将其持久化到磁盘的原因是该代码每次实例化客户端时创建一个证书,并且由于最小密钥大小为2048和1.7.0的性能,这特别严重。

1个回答

26

我也曾经为此苦苦挣扎。 我最终找到了解决方案。 我们来看一个错误:

'X509V3CertificateGenerator.Generate(AsymmetricKeyParameter)' is obsolete: 'Use Generate with an ISignatureFactory'

你基本上像我曾经做的那样,使用 Generate 方法:

var certificate = certificateGenerator.Generate(issuerCertificate.PrivateKey, random);

错误提示说:“'Use Generate with an ISignatureFactory'”。其中certificateGeneratorCertificateContainer类的实例。


因此,我们首先需要创建一个ISignatureFactory的实例。

ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", issuerKeyPair.Private, random);

为了使此功能正常工作,您还应声明以下内容:

var randomGenerator = new CryptoApiRandomGenerator();
var random = new SecureRandom(randomGenerator);
AsymmetricCipherKeyPair subjectKeyPair = default(AsymmetricCipherKeyPair);
var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);

keyPairGenerator.Init(keyGenerationParameters);
subjectKeyPair = keyPairGenerator.GenerateKeyPair();
AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair;

经过这些更改后,现在将Generate方法从以下内容进行更改:

var certificate = certificateGenerator.Generate(issuerCertificate.PrivateKey, random);

至:
var certificate = certificateGenerator.Generate(signatureFactory);

希望这可以帮助到你。


运行完美!谢谢! - djbyter
感谢您的反馈。 - drgmak
1
我在尝试使用解决方案时遇到了问题。您的代码中没有提到keystrenght变量,因此我决定使用任意整数值来查看我应该使用哪个。但是,我遇到了一个异常:系统算术异常bit.length <2 bouncy castle大整数。 - Kevin Avignon
@KevinAvignon 我知道这是一个很久以前的评论,你可能已经过去了,但对于未来的人们来说,考虑2的幂次方。1024很好用,尽管我不知道库中是否定义了一个好的常量。 - Saurbaum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接