public static void PeekContentBytes_InvalidLength()
        {
            byte[] badLength = "04040203".HexToByteArray();

            AsnReader reader = new AsnReader(badLength, AsnEncodingRules.BER);

            Assert.Throws <AsnContentException>(() => reader.PeekContentBytes());
        }
        public override byte[] DecodeOctetString(byte[] encodedOctets)
        {
            // Read using BER because the CMS specification says the encoding is BER.
            AsnReader reader = new AsnReader(encodedOctets, AsnEncodingRules.BER);

            const int   ArbitraryStackLimit = 256;
            Span <byte> tmp = stackalloc byte[ArbitraryStackLimit];

            // Use stackalloc 0 so data can later hold a slice of tmp.
#if __MonoCS__
            ReadOnlySpan <byte> data = new byte[0];
#else
            ReadOnlySpan <byte> data = stackalloc byte[0];
#endif
            byte[] poolBytes = null;

            try
            {
                if (!reader.TryGetPrimitiveOctetStringBytes(out var contents))
                {
                    if (reader.TryCopyOctetStringBytes(tmp, out int bytesWritten))
                    {
                        data = tmp.Slice(0, bytesWritten);
                    }
                    else
                    {
                        poolBytes = ArrayPool <byte> .Shared.Rent(reader.PeekContentBytes().Length);

                        if (!reader.TryCopyOctetStringBytes(poolBytes, out bytesWritten))
                        {
                            Debug.Fail("TryCopyOctetStringBytes failed with a provably-large-enough buffer");
                            throw new CryptographicException();
                        }

                        data = new ReadOnlySpan <byte>(poolBytes, 0, bytesWritten);
                    }
                }
                else
                {
                    data = contents.Span;
                }

                if (reader.HasData)
                {
                    throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                }

                return(data.ToArray());
            }
            finally
            {
                if (poolBytes != null)
                {
                    Array.Clear(poolBytes, 0, data.Length);
                    ArrayPool <byte> .Shared.Return(poolBytes);
                }
            }
        }
Ejemplo n.º 3
0
        public static void ValidateDer(ReadOnlyMemory <byte> encodedValue)
        {
            try
            {
                Asn1Tag   tag;
                AsnReader reader = new AsnReader(encodedValue, AsnEncodingRules.DER);

                while (reader.HasData)
                {
                    tag = reader.PeekTag();

                    // If the tag is in the UNIVERSAL class
                    //
                    // DER limits the constructed encoding to SEQUENCE and SET, as well as anything which gets
                    // a defined encoding as being an IMPLICIT SEQUENCE.
                    if (tag.TagClass == TagClass.Universal)
                    {
                        switch ((UniversalTagNumber)tag.TagValue)
                        {
                        case UniversalTagNumber.External:
                        case UniversalTagNumber.Embedded:
                        case UniversalTagNumber.Sequence:
                        case UniversalTagNumber.Set:
                        case UniversalTagNumber.UnrestrictedCharacterString:
                            if (!tag.IsConstructed)
                            {
                                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                            }

                            break;

                        default:
                            if (tag.IsConstructed)
                            {
                                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                            }

                            break;
                        }
                    }

                    if (tag.IsConstructed)
                    {
                        ValidateDer(reader.PeekContentBytes());
                    }

                    // Skip past the current value.
                    reader.ReadEncodedValue();
                }
            }
            catch (AsnContentException e)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
            }
        }
Ejemplo n.º 4
0
        public static byte[] DecodeOctetString(ReadOnlyMemory <byte> encodedOctets)
        {
            // Read using BER because the CMS specification says the encoding is BER.
            AsnReader reader = new AsnReader(encodedOctets, AsnEncodingRules.BER);

            const int   ArbitraryStackLimit = 256;
            Span <byte> tmp = stackalloc byte[ArbitraryStackLimit];
            // Use stackalloc 0 so data can later hold a slice of tmp.
            ReadOnlySpan <byte> data = stackalloc byte[0];

            byte[] poolBytes = null;

            try
            {
                if (!reader.TryReadPrimitiveOctetStringBytes(out var contents))
                {
                    if (reader.TryCopyOctetStringBytes(tmp, out int bytesWritten))
                    {
                        data = tmp.Slice(0, bytesWritten);
                    }
                    else
                    {
                        poolBytes = CryptoPool.Rent(reader.PeekContentBytes().Length);

                        if (!reader.TryCopyOctetStringBytes(poolBytes, out bytesWritten))
                        {
                            Debug.Fail("TryCopyOctetStringBytes failed with a provably-large-enough buffer");
                            throw new CryptographicException();
                        }

                        data = new ReadOnlySpan <byte>(poolBytes, 0, bytesWritten);
                    }
                }
                else
                {
                    data = contents.Span;
                }

                reader.ThrowIfNotEmpty();

                return(data.ToArray());
            }
            finally
            {
                if (poolBytes != null)
                {
                    CryptoPool.Return(poolBytes, data.Length);
                }
            }
        }
Ejemplo n.º 5
0
        public static void PeekContentSpan_Primitive()
        {
            const string EncodedContents = "010203040506";
            const string EncodedValue    = "0406" + EncodedContents;

            byte[] data = (EncodedValue + "0500").HexToByteArray();

            AsnReader reader = new AsnReader(data, AsnEncodingRules.BER);

            Assert.Equal(EncodedContents, reader.PeekContentBytes().ByteArrayToHex());

            // It's Peek, so it's reproducible.
            Assert.Equal(EncodedValue, reader.PeekEncodedValue().ByteArrayToHex());
        }
Ejemplo n.º 6
0
        public static void PeekContentSpan_Corrupt_Throws()
        {
            const string EncodedContents = "040101" + "04050203040506";
            // Constructed bit isn't set, so indefinite length is invalid.
            const string EncodedValue = "0480" + EncodedContents + "0000";

            byte[] data = (EncodedValue + "0500").HexToByteArray();

            Assert.Throws <CryptographicException>(
                () =>
            {
                AsnReader reader = new AsnReader(data, AsnEncodingRules.BER);
                reader.PeekContentBytes();
            });
        }
Ejemplo n.º 7
0
        internal ReadOnlySpan <byte> GetHashableContentSpan()
        {
            ReadOnlyMemory <byte> content = _heldContent.Value;

            if (!_hasPkcs7Content)
            {
                return(content.Span);
            }

            // In PKCS#7 compat, only return the contents within the outermost tag.
            // See https://tools.ietf.org/html/rfc5652#section-5.2.1
            AsnReader reader = new AsnReader(content, AsnEncodingRules.BER);

            // This span is safe to return because it's still bound under _heldContent.
            return(reader.PeekContentBytes().Span);
        }
Ejemplo n.º 8
0
        private byte[] EncryptContent(
            ContentInfo contentInfo,
            AlgorithmIdentifier contentEncryptionAlgorithm,
            out byte[] cek,
            out byte[] parameterBytes)
        {
            using (SymmetricAlgorithm alg = OpenAlgorithm(contentEncryptionAlgorithm))
                using (ICryptoTransform encryptor = alg.CreateEncryptor())
                {
                    cek = alg.Key;

                    if (alg is RC2)
                    {
                        Rc2CbcParameters rc2Params = new Rc2CbcParameters(alg.IV, alg.KeySize);

                        using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
                        {
                            rc2Params.Encode(writer);
                            parameterBytes = writer.Encode();
                        }
                    }
                    else
                    {
                        parameterBytes = EncodeOctetString(alg.IV);
                    }

                    byte[] toEncrypt = contentInfo.Content;

                    if (contentInfo.ContentType.Value == Oids.Pkcs7Data)
                    {
                        toEncrypt = EncodeOctetString(toEncrypt);
                        return(encryptor.OneShot(toEncrypt));
                    }
                    else
                    {
                        if (contentInfo.Content.Length == 0)
                        {
                            return(encryptor.OneShot(contentInfo.Content));
                        }
                        else
                        {
                            AsnReader reader = new AsnReader(contentInfo.Content, AsnEncodingRules.BER);
                            return(encryptor.OneShot(reader.PeekContentBytes().ToArray()));
                        }
                    }
                }
        }
Ejemplo n.º 9
0
        private static void ReencodeIfUsingIndefiniteLengthEncodingOnOuterStructure(ref byte[] encodedContent)
        {
            AsnReader reader = new AsnReader(encodedContent, AsnEncodingRules.BER);
            Asn1Tag   tag    = reader.ReadTagAndLength(out int?contentsLength, out int _);

            if (contentsLength != null)
            {
                // definite length, do nothing
                return;
            }

            using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER))
            {
                // Tag doesn't matter here as we won't write it into the document
                writer.WriteOctetString(reader.PeekContentBytes().Span);
                encodedContent = writer.Encode();
            }
        }
Ejemplo n.º 10
0
        public static void PeekContentSpan_ExtremelyNested(bool fullArray)
        {
            byte[] dataBytes = new byte[4 * 16384];

            // For a full array this will build 2^14 nested indefinite length values.
            // PeekContentBytes should return dataBytes.Slice(2, dataBytes.Length - 4)
            //
            // For what it's worth, the initial algorithm succeeded at 1650, and StackOverflowed with 1651.
            //
            // With the counter-and-no-recursion algorithm a nesting depth of 534773759 was verified,
            // at a cost of 10 minutes of execution and a 2139095036 byte array.
            // (The size was "a little bit less than int.MaxValue, since that OOMed my 32-bit process")
            int end            = dataBytes.Length / 2;
            int expectedLength = dataBytes.Length - 4;

            if (!fullArray)
            {
                // Use 3/4 of what's available, just to prove we're not counting from the end.
                // So with "full" being a nesting value 16384 this will use 12288
                end            = end / 4 * 3;
                expectedLength = 2 * end - 4;
            }

            for (int i = 0; i < end; i += 2)
            {
                // Context-Specific 0 [Constructed]
                dataBytes[i] = 0xA0;
                // Indefinite length
                dataBytes[i + 1] = 0x80;
            }

            AsnReader             reader   = new AsnReader(dataBytes, AsnEncodingRules.BER);
            ReadOnlyMemory <byte> contents = reader.PeekContentBytes();

            Assert.Equal(expectedLength, contents.Length);
            Assert.True(Unsafe.AreSame(ref dataBytes[2], ref contents.Span.DangerousGetPinnableReference()));
        }
Ejemplo n.º 11
0
        internal SignerInfoAsn Sign(
            ReadOnlyMemory <byte> data,
            string contentTypeOid,
            bool silent,
            out X509Certificate2Collection chainCerts)
        {
            HashAlgorithmName hashAlgorithmName = PkcsHelpers.GetDigestAlgorithm(DigestAlgorithm);
            IncrementalHash   hasher            = IncrementalHash.CreateHash(hashAlgorithmName);

            hasher.AppendData(data.Span);
            byte[] dataHash = hasher.GetHashAndReset();

            SignerInfoAsn newSignerInfo = new SignerInfoAsn();

            newSignerInfo.DigestAlgorithm.Algorithm = DigestAlgorithm;

            // If the user specified attributes (not null, count > 0) we need attributes.
            // If the content type is null we're counter-signing, and need the message digest attr.
            // If the content type is otherwise not-data we need to record it as the content-type attr.
            if (SignedAttributes?.Count > 0 || contentTypeOid != Oids.Pkcs7Data)
            {
                List <AttributeAsn> signedAttrs = BuildAttributes(SignedAttributes);

                using (var writer = new AsnWriter(AsnEncodingRules.DER))
                {
                    writer.PushSetOf();
                    writer.WriteOctetString(dataHash);
                    writer.PopSetOf();

                    signedAttrs.Add(
                        new AttributeAsn
                    {
                        AttrType   = new Oid(Oids.MessageDigest, Oids.MessageDigest),
                        AttrValues = writer.Encode(),
                    });
                }

                if (contentTypeOid != null)
                {
                    using (var writer = new AsnWriter(AsnEncodingRules.DER))
                    {
                        writer.PushSetOf();
                        writer.WriteObjectIdentifier(contentTypeOid);
                        writer.PopSetOf();

                        signedAttrs.Add(
                            new AttributeAsn
                        {
                            AttrType   = new Oid(Oids.ContentType, Oids.ContentType),
                            AttrValues = writer.Encode(),
                        });
                    }
                }

                // Use the serializer/deserializer to DER-normalize the attribute order.
                SignedAttributesSet signedAttrsSet = new SignedAttributesSet();
                signedAttrsSet.SignedAttributes = PkcsHelpers.NormalizeSet(
                    signedAttrs.ToArray(),
                    normalized =>
                {
                    AsnReader reader = new AsnReader(normalized, AsnEncodingRules.DER);
                    hasher.AppendData(reader.PeekContentBytes().Span);
                });

                // Since this contains user data in a context where BER is permitted, use BER.
                // There shouldn't be any observable difference here between BER and DER, though,
                // since the top level fields were written by NormalizeSet.
                using (AsnWriter attrsWriter = AsnSerializer.Serialize(signedAttrsSet, AsnEncodingRules.BER))
                {
                    newSignerInfo.SignedAttributes = attrsWriter.Encode();
                }

                dataHash = hasher.GetHashAndReset();
            }

            switch (SignerIdentifierType)
            {
            case SubjectIdentifierType.IssuerAndSerialNumber:
                byte[] serial = Certificate.GetSerialNumber();
                Array.Reverse(serial);

                newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
                {
                    Issuer       = Certificate.IssuerName.RawData,
                    SerialNumber = serial,
                };

                newSignerInfo.Version = 1;
                break;

            case SubjectIdentifierType.SubjectKeyIdentifier:
                newSignerInfo.Sid.SubjectKeyIdentifier = PkcsPal.Instance.GetSubjectKeyIdentifier(Certificate);
                newSignerInfo.Version = 3;
                break;

            case SubjectIdentifierType.NoSignature:
                newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
                {
                    Issuer       = SubjectIdentifier.DummySignerEncodedValue,
                    SerialNumber = new byte[1],
                };
                newSignerInfo.Version = 1;
                break;

            default:
                Debug.Fail($"Unresolved SignerIdentifierType value: {SignerIdentifierType}");
                throw new CryptographicException();
            }

            if (UnsignedAttributes != null && UnsignedAttributes.Count > 0)
            {
                List <AttributeAsn> attrs = BuildAttributes(UnsignedAttributes);

                newSignerInfo.UnsignedAttributes = PkcsHelpers.NormalizeSet(attrs.ToArray());
            }

            bool signed = CmsSignature.Sign(
                dataHash,
                hashAlgorithmName,
                Certificate,
                PrivateKey,
                silent,
                out Oid signatureAlgorithm,
                out ReadOnlyMemory <byte> signatureValue);

            if (!signed)
            {
                throw new CryptographicException(SR.Cryptography_Cms_CannotDetermineSignatureAlgorithm);
            }

            newSignerInfo.SignatureValue = signatureValue;
            newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm;

            X509Certificate2Collection certs = new X509Certificate2Collection();

            certs.AddRange(Certificates);

            if (SignerIdentifierType != SubjectIdentifierType.NoSignature)
            {
                if (IncludeOption == X509IncludeOption.EndCertOnly)
                {
                    certs.Add(Certificate);
                }
                else if (IncludeOption != X509IncludeOption.None)
                {
                    X509Chain chain = new X509Chain();
                    chain.ChainPolicy.RevocationMode    = X509RevocationMode.NoCheck;
                    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;

                    if (!chain.Build(Certificate))
                    {
                        foreach (X509ChainStatus status in chain.ChainStatus)
                        {
                            if (status.Status == X509ChainStatusFlags.PartialChain)
                            {
                                throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain);
                            }
                        }
                    }

                    X509ChainElementCollection elements = chain.ChainElements;
                    int count = elements.Count;
                    int last  = count - 1;

                    if (last == 0)
                    {
                        // If there's always one cert treat it as EE, not root.
                        last = -1;
                    }

                    for (int i = 0; i < count; i++)
                    {
                        X509Certificate2 cert = elements[i].Certificate;

                        if (i == last &&
                            IncludeOption == X509IncludeOption.ExcludeRoot &&
                            cert.SubjectName.RawData.AsSpan().SequenceEqual(cert.IssuerName.RawData))
                        {
                            break;
                        }

                        certs.Add(cert);
                    }
                }
            }

            chainCerts = certs;
            return(newSignerInfo);
        }
Ejemplo n.º 12
0
        public unsafe string ProcessNegotiateChallenge(string challengeString)
        {
            Console.WriteLine($"ChallengesString {challengeString}");

            NegState state = NegState.Unknown;
            string   mech  = null;

            byte[] blob = null;

            byte[]    data            = Convert.FromBase64String(challengeString);
            AsnReader reader          = new AsnReader(data, AsnEncodingRules.DER);
            AsnReader challengeReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegotiationToken.NegTokenResp));

            // NegTokenResp::= SEQUENCE {
            //    negState[0] ENUMERATED {
            //        accept - completed(0),
            //        accept - incomplete(1),
            //        reject(2),
            //        request - mic(3)
            //    } OPTIONAL,
            // --REQUIRED in the first reply from the target
            //    supportedMech[1] MechType OPTIONAL,
            // --present only in the first reply from the target
            // responseToken[2] OCTET STRING  OPTIONAL,
            // mechListMIC[3] OCTET STRING  OPTIONAL,
            // ...
            // }

            challengeReader = challengeReader.ReadSequence();
            while (challengeReader.HasData)
            {
                Asn1Tag tag = challengeReader.PeekTag();
                if (tag.TagClass == TagClass.ContextSpecific)
                {
                    NegTokenResp dataType      = (NegTokenResp)tag.TagValue;
                    AsnReader    specificValue = new AsnReader(challengeReader.PeekContentBytes(), AsnEncodingRules.DER);

                    switch (dataType)
                    {
                    case NegTokenResp.NegState:
                        state = specificValue.ReadEnumeratedValue <NegState>();
                        break;

                    case NegTokenResp.SupportedMech:
                        mech = specificValue.ReadObjectIdentifier();
                        break;

                    case NegTokenResp.ResponseToken:
                        blob = specificValue.ReadOctetString();
                        break;

                    default:
                        // Ignore everything else
                        break;
                    }
                }

                challengeReader.ReadEncodedValue();
            }

            if (Diag)
            {
                Console.WriteLine("Negotiate challenege: {0} - {1} in {2}", challengeString, mech, state);
            }

            // Mechanism should be set on first message. That means always
            // as NTLM has only one challenege message.
            if (!NtlmOid.Equals(mech))
            {
                throw new NotSupportedException($"'{mech}' mechanism is not supported");
            }


            if (state != NegState.Unknown && state != NegState.AcceptIncomplete)
            {
                // If state was set, it should be AcceptIncomplete for us to proseed.
                return("");
            }

            if (blob?.Length > 0)
            {
                // Process decoded NTLM blob.
                byte[] response = ProcessChallengeMessage(blob);
                if (response?.Length > 0)
                {
                    AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);

                    using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegotiationToken.NegTokenResp)))
                    {
                        writer.PushSequence();
                        using (writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenInit.MechToken)))
                        {
                            writer.WriteOctetString(response);
                        }

                        writer.PopSequence();
                    }

                    return("Negotiate " + Convert.ToBase64String(writer.Encode(), Base64FormattingOptions.None));
                }
            }

            return("");
        }
Ejemplo n.º 13
0
        internal SignerInfoAsn Sign(
            ReadOnlyMemory <byte> data,
            string contentTypeOid,
            bool silent,
            out X509Certificate2Collection chainCerts)
        {
            HashAlgorithmName hashAlgorithmName = Helpers.GetDigestAlgorithm(DigestAlgorithm);
            IncrementalHash   hasher            = IncrementalHash.CreateHash(hashAlgorithmName);

            hasher.AppendData(data.Span);
            byte[] dataHash = hasher.GetHashAndReset();

            SignerInfoAsn newSignerInfo = new SignerInfoAsn();

            newSignerInfo.DigestAlgorithm.Algorithm = DigestAlgorithm;

            if ((SignedAttributes != null && SignedAttributes.Count > 0) || contentTypeOid == null)
            {
                List <AttributeAsn> signedAttrs = BuildAttributes(SignedAttributes);
                var writer = new AsnWriter(AsnEncodingRules.DER);

                writer.PushSetOf();
                writer.WriteOctetString(dataHash);
                writer.PopSetOf();

                signedAttrs.Add(
                    new AttributeAsn
                {
                    AttrType   = new Oid(Oids.MessageDigest, Oids.MessageDigest),
                    AttrValues = writer.Encode(),
                });

                if (contentTypeOid != null)
                {
                    writer = new AsnWriter(AsnEncodingRules.DER);
                    writer.PushSetOf();
                    writer.WriteObjectIdentifier(contentTypeOid);
                    writer.PopSetOf();

                    signedAttrs.Add(
                        new AttributeAsn
                    {
                        AttrType   = new Oid(Oids.ContentType, Oids.ContentType),
                        AttrValues = writer.Encode(),
                    });
                }

                // Use the serializer/deserializer to DER-normalize the attribute order.
                newSignerInfo.SignedAttributes = Helpers.NormalizeSet(
                    signedAttrs.ToArray(),
                    normalized =>
                {
                    AsnReader reader = new AsnReader(normalized, AsnEncodingRules.DER);
                    hasher.AppendData(reader.PeekContentBytes().Span);
                });

                dataHash = hasher.GetHashAndReset();
            }

            switch (SignerIdentifierType)
            {
            case SubjectIdentifierType.IssuerAndSerialNumber:
                byte[] serial = Certificate.GetSerialNumber();
                Array.Reverse(serial);

                newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
                {
                    Issuer       = Certificate.IssuerName.RawData,
                    SerialNumber = serial,
                };

                newSignerInfo.Version = 1;
                break;

            case SubjectIdentifierType.SubjectKeyIdentifier:
                newSignerInfo.Sid.SubjectKeyIdentifier = Certificate.GetSubjectKeyIdentifier();
                newSignerInfo.Version = 3;
                break;

            case SubjectIdentifierType.NoSignature:
                newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
                {
                    Issuer       = SubjectIdentifier.DummySignerEncodedValue,
                    SerialNumber = new byte[1],
                };
                newSignerInfo.Version = 1;
                break;

            default:
                Debug.Fail($"Unresolved SignerIdentifierType value: {SignerIdentifierType}");
                throw new CryptographicException();
            }

            if (UnsignedAttributes != null && UnsignedAttributes.Count > 0)
            {
                List <AttributeAsn> attrs = BuildAttributes(UnsignedAttributes);

                newSignerInfo.UnsignedAttributes = Helpers.NormalizeSet(attrs.ToArray());
            }

            bool signed = CmsSignature.Sign(
                dataHash,
                hashAlgorithmName,
                Certificate,
                silent,
                out Oid signatureAlgorithm,
                out ReadOnlyMemory <byte> signatureValue);

            if (!signed)
            {
                throw new CryptographicException(SR.Cryptography_Cms_CannotDetermineSignatureAlgorithm);
            }

            newSignerInfo.SignatureValue = signatureValue;
            newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm;

            X509Certificate2Collection certs = new X509Certificate2Collection();

            certs.AddRange(Certificates);

            if (SignerIdentifierType != SubjectIdentifierType.NoSignature)
            {
                if (IncludeOption == X509IncludeOption.EndCertOnly)
                {
                    certs.Add(Certificate);
                }
                else if (IncludeOption != X509IncludeOption.None)
                {
                    X509Chain chain = new X509Chain();
                    chain.ChainPolicy.RevocationMode    = X509RevocationMode.NoCheck;
                    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;

                    if (!chain.Build(Certificate))
                    {
                        foreach (X509ChainStatus status in chain.ChainStatus)
                        {
                            if (status.Status == X509ChainStatusFlags.PartialChain)
                            {
                                throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain);
                            }
                        }
                    }

                    X509ChainElementCollection elements = chain.ChainElements;
                    int count = elements.Count;
                    int last  = count - 1;

                    if (last == 0)
                    {
                        // If there's always one cert treat it as EE, not root.
                        last = -1;
                    }

                    for (int i = 0; i < count; i++)
                    {
                        X509Certificate2 cert = elements[i].Certificate;

                        if (i == last &&
                            IncludeOption == X509IncludeOption.ExcludeRoot &&
                            cert.SubjectName.RawData.AsReadOnlySpan().SequenceEqual(cert.IssuerName.RawData))
                        {
                            break;
                        }

                        certs.Add(cert);
                    }
                }
            }

            chainCerts = certs;
            return(newSignerInfo);
        }