C# 从自签名根 CA 生成中间证书
C# generate intermediate certificate from self signed Root CA
我在 Visual Studio 2019 中使用 c# 和 Bouncy Castle
版本 1.8.5
。
我已成功生成 Certificate Authority
(CA),现在想生成 Intermediate Certificate
。
在我当前的工作流程中,CA 证书作为 X509Certificate2
对象返回,我传递该对象以生成中间证书。
从那里我想阅读 PrivateKey
但我很难这样做。
对于 CA 生成(CACertificateDetails
是一个简单的 class 用于存储要传递的字符串:
public static X509Certificate2 GenerateCA(CACertificateDetails details)
{
// generate a random number
var random = GetSecureRandom();
// init the certificate generator
X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
// serial number
certificateGenerator.SetSerialNumber(SerialNumber(random));
// set issuer and subject name
var subjectDN = new X509Name(details.SubjectName);
var issuerDN = new X509Name(details.IssuerName);
certificateGenerator.SetIssuerDN(issuerDN);
certificateGenerator.SetSubjectDN(subjectDN);
// validation time
var notBefore = DateTime.UtcNow.Date;
var notAfter = notBefore.AddYears(details.ValidYears);
certificateGenerator.SetNotBefore(notBefore);
certificateGenerator.SetNotAfter(notAfter);
// subject public Key
AsymmetricCipherKeyPair subjectKeyPair;
KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, details.KeyStrength);
RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
// set the hash algorithm
AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair;
ISignatureFactory signatureFactory = new Asn1SignatureFactory(PkcsObjectIdentifiers.Sha512WithRsaEncryption.ToString(), issuerKeyPair.Private, random);
// generate the certificate
var certificate = certificateGenerator.Generate(signatureFactory);
var x509Certificate = new X509Certificate2(certificate.GetEncoded());
PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
Asn1Sequence 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);
var convertedRsa = DotNetUtilities.ToRSA(rsaparams);
var x509CertificateRet = x509Certificate.CopyWithPrivateKey(convertedRsa);
x509CertificateRet.FriendlyName = details.FriendlyName;
return x509CertificateRet;
}
中间证书的生成方法如下(刚才生成的CA现在作为对象传递):
public static X509Certificate2 GenerateCertificate(CertificateDetails details, X509Certificate2 issuer)
{
// generate a random number
var random = GetSecureRandom();
// init the certificate generator
X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
// serial number
certificateGenerator.SetSerialNumber(SerialNumber(random));
// set issuer and subject name
var issuerDN = new X509Name(issuer.Issuer);
certificateGenerator.SetIssuerDN(issuerDN);
var distinguishedNames = DistinguishedName(details);
var nameOids = new ArrayList(distinguishedNames.Select(x => x.Item1).ToArray());
var nameValues = new ArrayList(distinguishedNames.Select(x => x.Item2).ToArray());
var subjectDN = new X509Name(nameOids, nameValues);
certificateGenerator.SetSubjectDN(subjectDN);
// authority key identifier
var authorityKeyIdentifier = new AuthorityKeyIdentifierStructure(DotNetUtilities.FromX509Certificate(issuer));
certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifier);
// Basic Constraints - certificate is allowed to be used as intermediate.
certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(details.IsIntermediateCertificate));
// key usage
if (!details.IsIntermediateCertificate)
{
certificateGenerator.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.DataEncipherment | KeyUsage.KeyAgreement));
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(new[] { KeyPurposeID.IdKPServerAuth, KeyPurposeID.IdKPServerAuth }));
}
else
{
certificateGenerator.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.DataEncipherment | KeyUsage.KeyAgreement | KeyUsage.KeyCertSign));
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeID.AnyExtendedKeyUsage));
}
// validation time
var notBefore = DateTime.UtcNow.Date;
var notAfter = notBefore.AddYears(details.ValidYears);
certificateGenerator.SetNotBefore(notBefore);
certificateGenerator.SetNotAfter(notAfter);
// subject public Key
AsymmetricCipherKeyPair subjectKeyPair;
KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, details.KeyStrength);
RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
// set the hash algorithm
var issuerPrivateKey = TransformRSAPrivateKey(issuer.PrivateKey);
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA3-512withRSA", issuerPrivateKey, random);
// generate the certificate
var certificate = certificateGenerator.Generate(signatureFactory);
PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
var x509Certificate = new X509Certificate2(certificate.GetEncoded());
Asn1Sequence 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);
x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
x509Certificate.FriendlyName = details.FriendlyName;
return x509Certificate;
}
我现在在第 prov.ExportParameters(true)
行的函数 TransformRSAPrivateKey
失败了,可能是因为没有找到私钥数据,我觉得这很奇怪,因为我用 x509Certificate.CopyWithPrivateKey(convertedRsa)
复制了它在CA一代。
private static AsymmetricKeyParameter TransformRSAPrivateKey(AsymmetricAlgorithm privateKey)
{
RSACryptoServiceProvider prov = privateKey as RSACryptoServiceProvider;
RSAParameters parameters = prov.ExportParameters(true);
return new RsaPrivateCrtKeyParameters(
new BigInteger(1, parameters.Modulus),
new BigInteger(1, parameters.Exponent),
new BigInteger(1, parameters.D),
new BigInteger(1, parameters.P),
new BigInteger(1, parameters.Q),
new BigInteger(1, parameters.DP),
new BigInteger(1, parameters.DQ),
new BigInteger(1, parameters.InverseQ));
}
我如何继续使用 CA 生成(并签署)我的 Intermediate Certificate
?
我不想传递 AsymmetricKeyParameter
,如 Whosebug 上的多个示例所示。
在某些时候,我可能想将 CA 保存到磁盘并在另一时间读取它以生成具有相同 CA 的证书。那时我没有 CA 生成工作流,因此没有 AsymmetricKeyParameter
对象。
好的,问题出在这些行中:
// set the hash algorithm
var issuerPrivateKey = TransformRSAPrivateKey(issuer.PrivateKey);
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA3-512withRSA", issuerPrivateKey, random);
可以将 X509Certificate2
颁发者证书传递给 GenerateCertificate
函数,就像在原始 post 中一样。
诀窍是使用标志 X509KeyStorageFlags.Exportable
加载该证书,例如 X509Certificate2(certificatePath, password, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
.
此外,我将代码更改为
// set the hash algorithm
var issuerPrivateKey = DotNetUtilities.GetKeyPair(issuerCA.PrivateKey).Private;
ISignatureFactory signatureFactory = new Asn1SignatureFactory(PkcsObjectIdentifiers.Sha512WithRsaEncryption.ToString(), issuerPrivateKey, random);
我在 Visual Studio 2019 中使用 c# 和 Bouncy Castle
版本 1.8.5
。
我已成功生成 Certificate Authority
(CA),现在想生成 Intermediate Certificate
。
在我当前的工作流程中,CA 证书作为 X509Certificate2
对象返回,我传递该对象以生成中间证书。
从那里我想阅读 PrivateKey
但我很难这样做。
对于 CA 生成(CACertificateDetails
是一个简单的 class 用于存储要传递的字符串:
public static X509Certificate2 GenerateCA(CACertificateDetails details)
{
// generate a random number
var random = GetSecureRandom();
// init the certificate generator
X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
// serial number
certificateGenerator.SetSerialNumber(SerialNumber(random));
// set issuer and subject name
var subjectDN = new X509Name(details.SubjectName);
var issuerDN = new X509Name(details.IssuerName);
certificateGenerator.SetIssuerDN(issuerDN);
certificateGenerator.SetSubjectDN(subjectDN);
// validation time
var notBefore = DateTime.UtcNow.Date;
var notAfter = notBefore.AddYears(details.ValidYears);
certificateGenerator.SetNotBefore(notBefore);
certificateGenerator.SetNotAfter(notAfter);
// subject public Key
AsymmetricCipherKeyPair subjectKeyPair;
KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, details.KeyStrength);
RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
// set the hash algorithm
AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair;
ISignatureFactory signatureFactory = new Asn1SignatureFactory(PkcsObjectIdentifiers.Sha512WithRsaEncryption.ToString(), issuerKeyPair.Private, random);
// generate the certificate
var certificate = certificateGenerator.Generate(signatureFactory);
var x509Certificate = new X509Certificate2(certificate.GetEncoded());
PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
Asn1Sequence 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);
var convertedRsa = DotNetUtilities.ToRSA(rsaparams);
var x509CertificateRet = x509Certificate.CopyWithPrivateKey(convertedRsa);
x509CertificateRet.FriendlyName = details.FriendlyName;
return x509CertificateRet;
}
中间证书的生成方法如下(刚才生成的CA现在作为对象传递):
public static X509Certificate2 GenerateCertificate(CertificateDetails details, X509Certificate2 issuer)
{
// generate a random number
var random = GetSecureRandom();
// init the certificate generator
X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
// serial number
certificateGenerator.SetSerialNumber(SerialNumber(random));
// set issuer and subject name
var issuerDN = new X509Name(issuer.Issuer);
certificateGenerator.SetIssuerDN(issuerDN);
var distinguishedNames = DistinguishedName(details);
var nameOids = new ArrayList(distinguishedNames.Select(x => x.Item1).ToArray());
var nameValues = new ArrayList(distinguishedNames.Select(x => x.Item2).ToArray());
var subjectDN = new X509Name(nameOids, nameValues);
certificateGenerator.SetSubjectDN(subjectDN);
// authority key identifier
var authorityKeyIdentifier = new AuthorityKeyIdentifierStructure(DotNetUtilities.FromX509Certificate(issuer));
certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifier);
// Basic Constraints - certificate is allowed to be used as intermediate.
certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(details.IsIntermediateCertificate));
// key usage
if (!details.IsIntermediateCertificate)
{
certificateGenerator.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.DataEncipherment | KeyUsage.KeyAgreement));
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(new[] { KeyPurposeID.IdKPServerAuth, KeyPurposeID.IdKPServerAuth }));
}
else
{
certificateGenerator.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.DataEncipherment | KeyUsage.KeyAgreement | KeyUsage.KeyCertSign));
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeID.AnyExtendedKeyUsage));
}
// validation time
var notBefore = DateTime.UtcNow.Date;
var notAfter = notBefore.AddYears(details.ValidYears);
certificateGenerator.SetNotBefore(notBefore);
certificateGenerator.SetNotAfter(notAfter);
// subject public Key
AsymmetricCipherKeyPair subjectKeyPair;
KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, details.KeyStrength);
RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
// set the hash algorithm
var issuerPrivateKey = TransformRSAPrivateKey(issuer.PrivateKey);
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA3-512withRSA", issuerPrivateKey, random);
// generate the certificate
var certificate = certificateGenerator.Generate(signatureFactory);
PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
var x509Certificate = new X509Certificate2(certificate.GetEncoded());
Asn1Sequence 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);
x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
x509Certificate.FriendlyName = details.FriendlyName;
return x509Certificate;
}
我现在在第 prov.ExportParameters(true)
行的函数 TransformRSAPrivateKey
失败了,可能是因为没有找到私钥数据,我觉得这很奇怪,因为我用 x509Certificate.CopyWithPrivateKey(convertedRsa)
复制了它在CA一代。
private static AsymmetricKeyParameter TransformRSAPrivateKey(AsymmetricAlgorithm privateKey)
{
RSACryptoServiceProvider prov = privateKey as RSACryptoServiceProvider;
RSAParameters parameters = prov.ExportParameters(true);
return new RsaPrivateCrtKeyParameters(
new BigInteger(1, parameters.Modulus),
new BigInteger(1, parameters.Exponent),
new BigInteger(1, parameters.D),
new BigInteger(1, parameters.P),
new BigInteger(1, parameters.Q),
new BigInteger(1, parameters.DP),
new BigInteger(1, parameters.DQ),
new BigInteger(1, parameters.InverseQ));
}
我如何继续使用 CA 生成(并签署)我的 Intermediate Certificate
?
我不想传递 AsymmetricKeyParameter
,如 Whosebug 上的多个示例所示。
在某些时候,我可能想将 CA 保存到磁盘并在另一时间读取它以生成具有相同 CA 的证书。那时我没有 CA 生成工作流,因此没有 AsymmetricKeyParameter
对象。
好的,问题出在这些行中:
// set the hash algorithm
var issuerPrivateKey = TransformRSAPrivateKey(issuer.PrivateKey);
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA3-512withRSA", issuerPrivateKey, random);
可以将 X509Certificate2
颁发者证书传递给 GenerateCertificate
函数,就像在原始 post 中一样。
诀窍是使用标志 X509KeyStorageFlags.Exportable
加载该证书,例如 X509Certificate2(certificatePath, password, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
.
此外,我将代码更改为
// set the hash algorithm
var issuerPrivateKey = DotNetUtilities.GetKeyPair(issuerCA.PrivateKey).Private;
ISignatureFactory signatureFactory = new Asn1SignatureFactory(PkcsObjectIdentifiers.Sha512WithRsaEncryption.ToString(), issuerPrivateKey, random);