/// <summary>
 ///   Builds a Certificate Revocation List (CRL).
 /// </summary>
 /// <param name="issuerName">
 ///   The subject name of the certificate for the Certificate Authority (CA) that is
 ///   issuing this CRL.
 /// </param>
 /// <param name="generator">
 ///   A signature generator to produce the CA signature for this CRL.
 /// </param>
 /// <param name="crlNumber">
 ///   The sequence number for this CRL.  Per IETF RFC 5280, this value must always
 ///   increase from one CRL to the next for a given CA.
 /// </param>
 /// <param name="nextUpdate">
 ///   The latest possible time before the CA will publish a newer CRL, generally
 ///   treated as an expiration date for this CRL.
 /// </param>
 /// <param name="hashAlgorithm">
 ///   The hash algorithm to use when signing the CRL.
 /// </param>
 /// <param name="authorityKeyIdentifier">
 ///   The Authority Key Identifier to use in this CRL, identifying the CA certificate.
 /// </param>
 /// <param name="thisUpdate">
 ///   An optional value that specifies when this CRL was created, or
 ///   <see langword="null" /> to use the current system time.
 ///   The default is <see langword="null" />.
 /// </param>
 /// <returns>
 ///   An array that contains the bytes of the signed CRL.
 /// </returns>
 /// <exception cref="ArgumentNullException">
 ///   <para>
 ///     <paramref name="issuerName" />, <paramref name="generator" />, or
 ///     <paramref name="authorityKeyIdentifier" /> is <see langword="null" />.
 ///   </para>
 ///   <para>- or -</para>
 ///   <para>
 ///     <paramref name="hashAlgorithm" /> has <see langword="null" /> as the value of
 ///     <see cref="HashAlgorithmName.Name"/>.
 ///   </para>
 /// </exception>
 /// <exception cref="ArgumentException">
 ///   <para>
 ///     <paramref name="nextUpdate"/> is older than <paramref name="thisUpdate"/>.
 ///   </para>
 ///   <para>- or -</para>
 ///   <para>
 ///     <paramref name="hashAlgorithm" /> has the empty string as the value of
 ///     <see cref="HashAlgorithmName.Name"/>.
 ///   </para>
 /// </exception>
 /// <exception cref="ArgumentOutOfRangeException">
 ///   <paramref name="crlNumber"/> is negative.
 /// </exception>
 /// <exception cref="CryptographicException">
 ///   an error occurred during signing.
 /// </exception>
 public byte[] Build(
     X500DistinguishedName issuerName,
     X509SignatureGenerator generator,
     BigInteger crlNumber,
     DateTimeOffset nextUpdate,
     HashAlgorithmName hashAlgorithm,
     X509AuthorityKeyIdentifierExtension authorityKeyIdentifier,
     DateTimeOffset?thisUpdate = null)
 {
     return(Build(
                issuerName,
                generator,
                crlNumber,
                nextUpdate,
                thisUpdate.GetValueOrDefault(DateTimeOffset.UtcNow),
                hashAlgorithm,
                authorityKeyIdentifier));
 }
        private byte[] Build(
            X500DistinguishedName issuerName,
            X509SignatureGenerator generator,
            BigInteger crlNumber,
            DateTimeOffset nextUpdate,
            DateTimeOffset thisUpdate,
            HashAlgorithmName hashAlgorithm,
            X509AuthorityKeyIdentifierExtension authorityKeyIdentifier)
        {
            ArgumentNullException.ThrowIfNull(issuerName);
            ArgumentNullException.ThrowIfNull(generator);

            if (crlNumber < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(crlNumber), SR.ArgumentOutOfRange_NeedNonNegNum);
            }
            if (nextUpdate <= thisUpdate)
            {
                throw new ArgumentException(SR.Cryptography_CRLBuilder_DatesReversed);
            }

            ArgumentException.ThrowIfNullOrEmpty(hashAlgorithm.Name, nameof(hashAlgorithm));
            ArgumentNullException.ThrowIfNull(authorityKeyIdentifier);

            byte[] signatureAlgId = generator.GetSignatureAlgorithmIdentifier(hashAlgorithm);

            {
                AlgorithmIdentifierAsn signatureAlgorithmAsn;

                // Deserialization also does validation of the value (except for Parameters,
                // which have to be validated separately).
                signatureAlgorithmAsn = AlgorithmIdentifierAsn.Decode(signatureAlgId, AsnEncodingRules.DER);

                if (signatureAlgorithmAsn.Parameters.HasValue)
                {
                    Helpers.ValidateDer(signatureAlgorithmAsn.Parameters.GetValueOrDefault().Span);
                }
            }

            AsnWriter writer = (_writer ??= new AsnWriter(AsnEncodingRules.DER));

            writer.Reset();

            // TBSCertList
            using (writer.PushSequence())
            {
                // version v2(1)
                writer.WriteInteger(1);

                // signature (AlgorithmIdentifier)
                writer.WriteEncodedValue(signatureAlgId);

                // issuer
                writer.WriteEncodedValue(issuerName.RawData);

                // thisUpdate
                WriteX509Time(writer, thisUpdate);

                // nextUpdate
                WriteX509Time(writer, nextUpdate);

                // revokedCertificates (don't write down if empty)
                if (_revoked.Count > 0)
                {
                    // SEQUENCE OF
                    using (writer.PushSequence())
                    {
                        foreach (RevokedCertificate revoked in _revoked)
                        {
                            // Anonymous CRL Entry type
                            using (writer.PushSequence())
                            {
                                writer.WriteInteger(revoked.Serial);
                                WriteX509Time(writer, revoked.RevocationTime);

                                if (revoked.Extensions is not null)
                                {
                                    writer.WriteEncodedValue(revoked.Extensions);
                                }
                            }
                        }
                    }
                }

                // extensions [0] EXPLICIT Extensions
                using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)))
                {
                    // Extensions (SEQUENCE OF)
                    using (writer.PushSequence())
                    {
                        // Authority Key Identifier Extension
                        using (writer.PushSequence())
                        {
                            writer.WriteObjectIdentifier(authorityKeyIdentifier.Oid !.Value !);

                            if (authorityKeyIdentifier.Critical)
                            {
                                writer.WriteBoolean(true);
                            }

                            byte[] encodedAkid = authorityKeyIdentifier.RawData;
                            Helpers.ValidateDer(encodedAkid);
                            writer.WriteOctetString(encodedAkid);
                        }

                        // CRL Number Extension
                        using (writer.PushSequence())
                        {
                            writer.WriteObjectIdentifier(Oids.CrlNumber);

                            using (writer.PushOctetString())
                            {
                                writer.WriteInteger(crlNumber);
                            }
                        }
                    }
                }
            }

            byte[] tbsCertList = writer.Encode();
            writer.Reset();

            byte[] signature = generator.SignData(tbsCertList, hashAlgorithm);

            // CertificateList
            using (writer.PushSequence())
            {
                writer.WriteEncodedValue(tbsCertList);
                writer.WriteEncodedValue(signatureAlgId);
                writer.WriteBitString(signature);
            }

            byte[] crl = writer.Encode();
            return(crl);
        }
        private byte[] Build(
            X509Certificate2 issuerCertificate,
            BigInteger crlNumber,
            DateTimeOffset nextUpdate,
            DateTimeOffset thisUpdate,
            HashAlgorithmName hashAlgorithm,
            RSASignaturePadding?rsaSignaturePadding)
        {
            ArgumentNullException.ThrowIfNull(issuerCertificate);

            if (!issuerCertificate.HasPrivateKey)
            {
                throw new ArgumentException(
                          SR.Cryptography_CertReq_IssuerRequiresPrivateKey,
                          nameof(issuerCertificate));
            }
            if (crlNumber < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(crlNumber), SR.ArgumentOutOfRange_NeedNonNegNum);
            }
            if (nextUpdate <= thisUpdate)
            {
                throw new ArgumentException(SR.Cryptography_CRLBuilder_DatesReversed);
            }

            ArgumentException.ThrowIfNullOrEmpty(hashAlgorithm.Name, nameof(hashAlgorithm));

            // Check the Basic Constraints and Key Usage extensions to help identify inappropriate certificates.
            // Note that this is not a security check. The system library backing X509Chain will use these same criteria
            // to determine if the CRL is valid; and a user can easily call the X509SignatureGenerator overload to
            // bypass this validation.  We're simply helping them at signing time understand that they've
            // chosen the wrong cert.
            var basicConstraints     = (X509BasicConstraintsExtension?)issuerCertificate.Extensions[Oids.BasicConstraints2];
            var keyUsage             = (X509KeyUsageExtension?)issuerCertificate.Extensions[Oids.KeyUsage];
            var subjectKeyIdentifier =
                (X509SubjectKeyIdentifierExtension?)issuerCertificate.Extensions[Oids.SubjectKeyIdentifier];

            if (basicConstraints == null)
            {
                throw new ArgumentException(
                          SR.Cryptography_CertReq_BasicConstraintsRequired,
                          nameof(issuerCertificate));
            }

            if (!basicConstraints.CertificateAuthority)
            {
                throw new ArgumentException(
                          SR.Cryptography_CertReq_IssuerBasicConstraintsInvalid,
                          nameof(issuerCertificate));
            }

            if (keyUsage != null && (keyUsage.KeyUsages & X509KeyUsageFlags.CrlSign) == 0)
            {
                throw new ArgumentException(
                          SR.Cryptography_CRLBuilder_IssuerKeyUsageInvalid,
                          nameof(issuerCertificate));
            }

            AsymmetricAlgorithm?key = null;
            string keyAlgorithm     = issuerCertificate.GetKeyAlgorithm();
            X509SignatureGenerator generator;

            try
            {
                switch (keyAlgorithm)
                {
                case Oids.Rsa:
                    if (rsaSignaturePadding is null)
                    {
                        throw new ArgumentException(SR.Cryptography_CertReq_RSAPaddingRequired);
                    }

                    RSA?rsa = issuerCertificate.GetRSAPrivateKey();
                    key       = rsa;
                    generator = X509SignatureGenerator.CreateForRSA(rsa !, rsaSignaturePadding);
                    break;

                case Oids.EcPublicKey:
                    ECDsa?ecdsa = issuerCertificate.GetECDsaPrivateKey();
                    key       = ecdsa;
                    generator = X509SignatureGenerator.CreateForECDsa(ecdsa !);
                    break;

                default:
                    throw new ArgumentException(
                              SR.Format(SR.Cryptography_UnknownKeyAlgorithm, keyAlgorithm),
                              nameof(issuerCertificate));
                }

                X509AuthorityKeyIdentifierExtension akid;

                if (subjectKeyIdentifier is not null)
                {
                    akid = X509AuthorityKeyIdentifierExtension.CreateFromSubjectKeyIdentifier(subjectKeyIdentifier);
                }
                else
                {
                    akid = X509AuthorityKeyIdentifierExtension.CreateFromIssuerNameAndSerialNumber(
                        issuerCertificate.IssuerName,
                        issuerCertificate.SerialNumberBytes.Span);
                }

                return(Build(
                           issuerCertificate.SubjectName,
                           generator,
                           crlNumber,
                           nextUpdate,
                           thisUpdate,
                           hashAlgorithm,
                           akid));
            }
            finally
            {
                key?.Dispose();
            }
        }