/// <summary> /// Sign the current certificate request to create a chain-signed or self-signed certificate. /// </summary> /// <param name="issuerName">The X500DistinguishedName for the Issuer</param> /// <param name="generator"> /// An <see cref="X509SignatureGenerator"/> representing the issuing certificate authority. /// </param> /// <param name="notBefore"> /// The oldest date and time where this certificate is considered valid. /// Typically <see cref="DateTimeOffset.UtcNow"/>, plus or minus a few seconds. /// </param> /// <param name="notAfter"> /// The date and time where this certificate is no longer considered valid. /// </param> /// <param name="serialNumber"> /// The serial number to use for the new certificate. This value should be unique per issuer. /// The value is interpreted as an unsigned (big) integer in big endian byte ordering. /// </param> /// <returns> /// The ASN.1 DER-encoded certificate, suitable to be passed to <see cref="X509Certificate2(byte[])"/>. /// </returns> /// <exception cref="ArgumentNullException"><paramref name="issuerName"/> is null.</exception> /// <exception cref="ArgumentNullException"><paramref name="generator"/> is null.</exception> /// <exception cref="ArgumentException"> /// <paramref name="notAfter"/> represents a date and time before <paramref name="notBefore"/>. /// </exception> /// <exception cref="ArgumentException"><paramref name="serialNumber"/> is null or has length 0.</exception> /// <exception cref="CryptographicException">Any error occurs during the signing operation.</exception> public X509Certificate2 Create( X500DistinguishedName issuerName, X509SignatureGenerator generator, DateTimeOffset notBefore, DateTimeOffset notAfter, byte[] serialNumber) { if (issuerName == null) { throw new ArgumentNullException(nameof(issuerName)); } if (generator == null) { throw new ArgumentNullException(nameof(generator)); } if (notAfter < notBefore) { throw new ArgumentException(SR.Cryptography_CertReq_DatesReversed); } if (serialNumber == null || serialNumber.Length < 1) { throw new ArgumentException(SR.Arg_EmptyOrNullArray, nameof(serialNumber)); } byte[] signatureAlgorithm = generator.GetSignatureAlgorithmIdentifier(HashAlgorithm); AlgorithmIdentifierAsn signatureAlgorithmAsn; // Deserialization also does validation of the value (except for Parameters, which have to be validated separately). signatureAlgorithmAsn = AsnSerializer.Deserialize <AlgorithmIdentifierAsn>(signatureAlgorithm, AsnEncodingRules.DER); if (signatureAlgorithmAsn.Parameters.HasValue) { Helpers.ValidateDer(signatureAlgorithmAsn.Parameters.Value); } TbsCertificateAsn tbsCertificate = new TbsCertificateAsn { Version = 2, SerialNumber = NormalizeSerialNumber(serialNumber), SignatureAlgorithm = signatureAlgorithmAsn, Issuer = issuerName.RawData, SubjectPublicKeyInfo = new SubjectPublicKeyInfoAsn { Algorithm = new AlgorithmIdentifierAsn { Algorithm = PublicKey.Oid, Parameters = PublicKey.EncodedParameters.RawData, }, SubjectPublicKey = PublicKey.EncodedKeyValue.RawData, }, Validity = new ValidityAsn(notBefore, notAfter), Subject = SubjectName.RawData, }; if (CertificateExtensions.Count > 0) { HashSet <string> usedOids = new HashSet <string>(CertificateExtensions.Count); List <X509ExtensionAsn> extensionAsns = new List <X509ExtensionAsn>(CertificateExtensions.Count); // An interesting quirk of skipping null values here is that // Extensions.Count == 0 => no extensions // Extensions.ContainsOnly(null) => empty extensions list foreach (X509Extension extension in CertificateExtensions) { if (extension == null) { continue; } if (!usedOids.Add(extension.Oid.Value)) { throw new InvalidOperationException( SR.Format(SR.Cryptography_CertReq_DuplicateExtension, extension.Oid.Value)); } extensionAsns.Add(new X509ExtensionAsn(extension, false)); } tbsCertificate.Extensions = extensionAsns.ToArray(); } using (AsnWriter writer = AsnSerializer.Serialize(tbsCertificate, AsnEncodingRules.DER)) { byte[] encodedTbsCertificate = writer.Encode(); CertificateAsn certificate = new CertificateAsn { TbsCertificate = tbsCertificate, SignatureAlgorithm = signatureAlgorithmAsn, SignatureValue = generator.SignData(encodedTbsCertificate, HashAlgorithm), }; using (AsnWriter signedWriter = AsnSerializer.Serialize(certificate, AsnEncodingRules.DER)) { return(new X509Certificate2(signedWriter.Encode())); } } }