using System; using System.Collections.Generic; using System.IO; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security.Certificates; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.X509.Extension; namespace Org.BouncyCastle.X509 { /// /// A class to Generate Version 3 X509Certificates. /// public class X509V3CertificateGenerator { private readonly X509ExtensionsGenerator extGenerator = new X509ExtensionsGenerator(); private V3TbsCertificateGenerator tbsGen; public X509V3CertificateGenerator() { tbsGen = new V3TbsCertificateGenerator(); } /// Create a generator for a version 3 certificate, initialised with another certificate. /// Template certificate to base the new one on. public X509V3CertificateGenerator(X509Certificate template) : this(template.CertificateStructure) { } public X509V3CertificateGenerator(X509CertificateStructure template) { tbsGen = new V3TbsCertificateGenerator(); tbsGen.SetSerialNumber(template.SerialNumber); tbsGen.SetIssuer(template.Issuer); tbsGen.SetStartDate(template.StartDate); tbsGen.SetEndDate(template.EndDate); tbsGen.SetSubject(template.Subject); tbsGen.SetSubjectPublicKeyInfo(template.SubjectPublicKeyInfo); var extensions = template.TbsCertificate.Extensions; foreach (var oid in extensions.ExtensionOids) { if (X509Extensions.SubjectAltPublicKeyInfo.Equals(oid) || X509Extensions.AltSignatureAlgorithm.Equals(oid) || X509Extensions.AltSignatureValue.Equals(oid)) { continue; } X509Extension ext = extensions.GetExtension(oid); extGenerator.AddExtension(oid, ext.critical, ext.Value.GetOctets()); } } /// /// Reset the Generator. /// public void Reset() { tbsGen = new V3TbsCertificateGenerator(); extGenerator.Reset(); } /// /// Set the certificate's serial number. /// /// Make serial numbers long, if you have no serial number policy make sure the number is at least 16 bytes of secure random data. /// You will be surprised how ugly a serial number collision can Get. /// The serial number. public void SetSerialNumber( BigInteger serialNumber) { if (serialNumber.SignValue <= 0) { throw new ArgumentException("serial number must be a positive integer", "serialNumber"); } tbsGen.SetSerialNumber(new DerInteger(serialNumber)); } /// /// Set the distinguished name of the issuer. /// The issuer is the entity which is signing the certificate. /// /// The issuer's DN. public void SetIssuerDN( X509Name issuer) { tbsGen.SetIssuer(issuer); } /// /// Set the date that this certificate is to be valid from. /// /// public void SetNotBefore( DateTime date) { tbsGen.SetStartDate(new Time(date)); } /// /// Set the date after which this certificate will no longer be valid. /// /// public void SetNotAfter( DateTime date) { tbsGen.SetEndDate(new Time(date)); } /// /// Set the DN of the entity that this certificate is about. /// /// public void SetSubjectDN( X509Name subject) { tbsGen.SetSubject(subject); } /// /// Set the public key that this certificate identifies. /// /// public void SetPublicKey( AsymmetricKeyParameter publicKey) { tbsGen.SetSubjectPublicKeyInfo(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey)); } /// /// Set the subject unique ID - note: it is very rare that it is correct to do this. /// /// public void SetSubjectUniqueID( bool[] uniqueID) { tbsGen.SetSubjectUniqueID(BooleanToBitString(uniqueID)); } /// /// Set the issuer unique ID - note: it is very rare that it is correct to do this. /// /// public void SetIssuerUniqueID( bool[] uniqueID) { tbsGen.SetIssuerUniqueID(BooleanToBitString(uniqueID)); } /// /// Add a given extension field for the standard extensions tag (tag 3). /// /// string containing a dotted decimal Object Identifier. /// Is it critical. /// The value. public void AddExtension( string oid, bool critical, Asn1Encodable extensionValue) { extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue); } /// /// Add an extension to this certificate. /// /// Its Object Identifier. /// Is it critical. /// The value. public void AddExtension( DerObjectIdentifier oid, bool critical, Asn1Encodable extensionValue) { extGenerator.AddExtension(oid, critical, extensionValue); } /// /// Add an extension using a string with a dotted decimal OID. /// /// string containing a dotted decimal Object Identifier. /// Is it critical. /// byte[] containing the value of this extension. public void AddExtension( string oid, bool critical, byte[] extensionValue) { extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, new DerOctetString(extensionValue)); } /// /// Add an extension to this certificate. /// /// Its Object Identifier. /// Is it critical. /// byte[] containing the value of this extension. public void AddExtension( DerObjectIdentifier oid, bool critical, byte[] extensionValue) { extGenerator.AddExtension(oid, critical, new DerOctetString(extensionValue)); } /// /// Add a given extension field for the standard extensions tag (tag 3), /// copying the extension value from another certificate. /// public void CopyAndAddExtension( string oid, bool critical, X509Certificate cert) { CopyAndAddExtension(new DerObjectIdentifier(oid), critical, cert); } /** * add a given extension field for the standard extensions tag (tag 3) * copying the extension value from another certificate. * @throws CertificateParsingException if the extension cannot be extracted. */ public void CopyAndAddExtension( DerObjectIdentifier oid, bool critical, X509Certificate cert) { Asn1OctetString extValue = cert.GetExtensionValue(oid); if (extValue == null) throw new CertificateParsingException("extension " + oid + " not present"); try { Asn1Encodable value = X509ExtensionUtilities.FromExtensionValue(extValue); this.AddExtension(oid, critical, value); } catch (Exception e) { throw new CertificateParsingException(e.Message, e); } } /// /// Generate a new using the provided . /// /// A signature factory with the necessary /// algorithm details. /// An . public X509Certificate Generate(ISignatureFactory signatureFactory) { var sigAlgID = (AlgorithmIdentifier)signatureFactory.AlgorithmDetails; tbsGen.SetSignature(sigAlgID); if (!extGenerator.IsEmpty) { tbsGen.SetExtensions(extGenerator.Generate()); } var tbsCertificate = tbsGen.GenerateTbsCertificate(); var signature = X509Utilities.GenerateSignature(signatureFactory, tbsCertificate); return new X509Certificate(new X509CertificateStructure(tbsCertificate, sigAlgID, signature)); } /// /// Generate a new using the provided and /// containing altSignatureAlgorithm and altSignatureValue extensions based on the passed /// . /// /// A signature factory with the necessary /// algorithm details. /// Whether the 'alt' extensions should be marked critical. /// A signature factory used to create the /// altSignatureAlgorithm and altSignatureValue extensions. /// An . public X509Certificate Generate(ISignatureFactory signatureFactory, bool isCritical, ISignatureFactory altSignatureFactory) { tbsGen.SetSignature(null); var altSigAlgID = (AlgorithmIdentifier)altSignatureFactory.AlgorithmDetails; extGenerator.AddExtension(X509Extensions.AltSignatureAlgorithm, isCritical, altSigAlgID); tbsGen.SetExtensions(extGenerator.Generate()); var altSignature = X509Utilities.GenerateSignature(altSignatureFactory, tbsGen.GeneratePreTbsCertificate()); extGenerator.AddExtension(X509Extensions.AltSignatureValue, isCritical, altSignature); return Generate(signatureFactory); } /// /// Allows enumeration of the signature names supported by the generator. /// public IEnumerable SignatureAlgNames => X509Utilities.GetAlgNames(); private static DerBitString BooleanToBitString(bool[] id) { int byteLength = (id.Length + 7) / 8; #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER Span bytes = byteLength <= 512 ? stackalloc byte[byteLength] : new byte[byteLength]; #else byte[] bytes = new byte[byteLength]; #endif for (int i = 0; i != id.Length; i++) { if (id[i]) { bytes[i >> 3] |= (byte)(0x80 >> (i & 7)); } } return new DerBitString(bytes, (8 - id.Length) & 7); } } }