我正在创建 CA 根证书,然后使用 Bouncy Castle 库创建自签名机器证书,如下所示。我在下面列出了整个实现。我能够创建受密码保护的 .PFX 文件。
尽管我创建了 CA 根证书,但我没有安装它,因为它已经安装在机器上(具有相同的主题和颁发者)。除此之外,我不确定原始 CAR 根证书 (MyCARoot.cer) 是如何创建的,但它是通过以下命令安装的:
certmgr.exe /add MyCARoot.cer /c /s /r localMachine root
在某些时候,我能够安装使用 certutil.exe 创建的自签名机器证书 (.PFX),但是现在它总是返回上述错误。
我的问题针对一个或多个领域:
- 我下面的实现是否正确 - 我是否错过了“允许私钥导出”或类似的东西......
- 整个方法是否正确 - 我切换到它是因为我需要手动创建和安装自签名机器证书,而不是使用 makecert.exe 和 pvk2pfx.exe 一起提示用户输入私钥密码 4 次。 ..
如何验证我生成的 .pfx 是否正确?(除了尝试使用 certutil.exe 安装它?
AsymmetricKeyParameter caPrivateKey = null; try { // Create Root CA certificate X509Certificate2 x509 = CertMaker.GenerateCACertificate(mSubject, ref caPrivateKey); // Save certificate to file byte[] certBytes = x509.Export(X509ContentType.Cert, mRootCaPwd); string certFilePath = Path.Combine(mInstallPath, mSubject + ".cer"); File.WriteAllBytes(certFilePath, certBytes); } catch(Exception ex) { Log.Verbose(ex); TextLog.Info("Failed to generate CA Root Certificate"); return; } if (caPrivateKey == null) { Log.Verbose("Failed to generate CA Root Certificate: Failed to generate private key."); TextLog.Info("Failed to generate CA Root Certificate: Failed to generate private key."); return; } try { X509Certificate2 cert = CertMaker.GenerateSelfSignedCertificate(mSubject, mIssuer, caPrivateKey); // Save certificate to file byte[] certBytes = cert.Export(X509ContentType.Pkcs12, mComputerCertPwd); string filename = String.Format("MyServer_{0}.pfx", mSubject); string certFilePath = Path.Combine(mInstallPath, filename); File.WriteAllBytes(certFilePath, certBytes); } catch(Exception ex) { Log.Verbose("Failed to generate Machine Certificate." + ex.Message); TextLog.Info("Failed to generate Machine Certificate."); }
和功能实现:
public static X509Certificate2 GenerateSelfSignedCertificate(string subjectName, string issuerName, AsymmetricKeyParameter issuerPrivKey, int keyStrength = 4096)
{
using (Log.VerboseCall())
{
try
{
// Generating Random Numbers
var randomGenerator = new CryptoApiRandomGenerator();
var random = new SecureRandom(randomGenerator);
Log.Verbose("Generated Random Numbers");
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", issuerPrivKey, random);
Log.Verbose("Generated SignatureFactory");
// The Certificate Generator
var certificateGenerator = new X509V3CertificateGenerator();
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, true, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
// Serial Number
var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
certificateGenerator.SetSerialNumber(serialNumber);
Log.Verbose("Added Serial Number to CertificateGenerator");
// Issuer and Subject Name
X509Name subjectDN = new X509Name("CN=" + subjectName);
X509Name issuerDN = new X509Name("CN=" + issuerName);
certificateGenerator.SetIssuerDN(issuerDN);
certificateGenerator.SetSubjectDN(subjectDN);
Log.Verbose("Had set Subject Name and Issuer Name");
// Valid For
var notBefore = DateTime.UtcNow.Date;
var notAfter = notBefore.AddYears(100);
certificateGenerator.SetNotBefore(notBefore);
certificateGenerator.SetNotAfter(notAfter);
// Subject Public Key
AsymmetricCipherKeyPair subjectKeyPair;
var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
var keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
Log.Verbose("Had generated and set Subject Public Key");
// Generating the Certificate
var issuerKeyPair = subjectKeyPair;
// NOTE #1
// In another reference I saw this - instead
//var dotNetPrivateKey = ToDotNetKey(privateKey);
//var dotNetCert = new X509Certificate2(DotNetUtilities.ToX509Certificate(newCert));
//dotNetCert.PrivateKey = dotNetPrivateKey;
// self-signed certificate
var certificate = certificateGenerator.Generate(signatureFactory);
var dotNetPrivateKey = ToDotNetKey((RsaPrivateCrtKeyParameters)subjectKeyPair.Private);
Log.Verbose("Generated Certificate");
// correcponding private key
PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
Log.Verbose("Created Private Key");
// merge into X509Certificate2
var x509 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certificate.GetEncoded());
Log.Verbose("Generated Encoded Certificate");
var seq = (Asn1Sequence)Asn1Object.FromByteArray(info.ParsePrivateKey().GetDerEncoded());
if (seq.Count != 9)
throw new PemException("Malformed sequence in RSA private key");
RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(seq);
RsaPrivateCrtKeyParameters rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent,
rsa.PrivateExponent, rsa.Prime1,
rsa.Prime2, rsa.Exponent1,
rsa.Exponent2, rsa.Coefficient);
Log.Verbose("Generated RSA Key Parameters");
x509.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
Log.Verbose("Had set CA Private Key");
return x509;
}
catch (Exception ex)
{
Console.WriteLine("Failed to create Self-Signed Machine certificate: {0}", ex.Message);
return null;
}
}
}
/// <summary>
/// Generate Self-Signed Root CA Certificate
/// </summary>
/// <param name="subjectName"></param>
/// <param name="CaPrivateKey"></param>
/// <returns>Returns the X509 certificate and reference to Root CA Private Key</returns>
public static X509Certificate2 GenerateCACertificate(string subjectName, ref AsymmetricKeyParameter CaPrivateKey)
{
using (Log.VerboseCall())
{
try
{
int keyStrength = 4096;
// Generating Random Numbers
CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
SecureRandom random = new SecureRandom(randomGenerator);
Log.Verbose("Generated Random Numbers");
// The Certificate Generator
X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
// Serial Number
BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
certificateGenerator.SetSerialNumber(serialNumber);
Log.Verbose("Added Serial Number to CertificateGenerator");
// Issuer and Subject Name
X509Name subjectDN = new X509Name("CN="+subjectName);
X509Name issuerDN = subjectDN;
certificateGenerator.SetIssuerDN(issuerDN);
certificateGenerator.SetSubjectDN(subjectDN);
Log.Verbose("Had set Subject Name and Issuer Name");
// Valid For
var notBefore = DateTime.UtcNow.Date;
var notAfter = notBefore.AddYears(100);
certificateGenerator.SetNotBefore(notBefore);
certificateGenerator.SetNotAfter(notAfter);
// Subject Public Key
AsymmetricCipherKeyPair subjectKeyPair;
var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
var keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
Log.Verbose("Had generated and set Subject Public Key");
// Generating the Certificate
AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair;
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", issuerKeyPair.Private, random);
Log.Verbose("Created SignatureFactory");
// selfsign certificate
// certificateGenerator.Generate signs the certificate using the private key,
// but doesn't put the private key in the certificate, which wouldn't make sense.
Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(signatureFactory);
X509Certificate2 x509 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certificate.GetEncoded());
Log.Verbose("Generated CA Root Certificate");
CaPrivateKey = issuerKeyPair.Private;
Log.Verbose("Had set CA Private Key");
return x509;
}
catch (Exception ex)
{
Log.VerboseFormat("Failed to create Root CA certificate: {0}", ex.Message);
return null;
}
}
}
/// <summary>
/// See NOTE #1
/// </summary>
/// <param name="privateKey"></param>
/// <returns></returns>
public static AsymmetricAlgorithm ToDotNetKey(RsaPrivateCrtKeyParameters privateKey)
{
// If you don't do this (cspParams), when you execute the netsh command you get the error 1312. i.e. of the netsh command:
//
// netsh http add sslcert ipport = 192.168.0.15:8081 certhash =5424476237fc2785ed2d0fd620a9131d7c999f6f appid = { 02639d71 - 0935 - 35e8 - 9d1b - 9dd1a2a34627 }
var cspParams = new CspParameters
{
KeyContainerName = Guid.NewGuid().ToString(),
KeyNumber = (int)KeyNumber.Exchange,
Flags = CspProviderFlags.UseMachineKeyStore
};
var rsaProvider = new RSACryptoServiceProvider(cspParams);
var parameters = new RSAParameters
{
Modulus = privateKey.Modulus.ToByteArrayUnsigned(),
P = privateKey.P.ToByteArrayUnsigned(),
Q = privateKey.Q.ToByteArrayUnsigned(),
DP = privateKey.DP.ToByteArrayUnsigned(),
DQ = privateKey.DQ.ToByteArrayUnsigned(),
InverseQ = privateKey.QInv.ToByteArrayUnsigned(),
D = privateKey.Exponent.ToByteArrayUnsigned(),
Exponent = privateKey.PublicExponent.ToByteArrayUnsigned()
};
rsaProvider.ImportParameters(parameters);
return rsaProvider;
}