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);
                }
            }
        }
Exemple #2
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);
                }
            }
        }
Exemple #3
0
        public static void TryCopyOctetStringBytes_Success_CER_MaxPrimitiveLength()
        {
            // CER says that the maximum encoding length for an OctetString primitive
            // is 1000.
            //
            // So we need 04 [1000] { 1000 anythings }
            // 1000 => 0x3E8, so the length encoding is 82 03 E8.
            // 1000 + 3 + 1 == 1004
            byte[] input = new byte[1004];
            input[0] = 0x04;
            input[1] = 0x82;
            input[2] = 0x03;
            input[3] = 0xE8;

            // Content
            input[4]    = 0x02;
            input[5]    = 0xA0;
            input[1002] = 0xA5;
            input[1003] = 0xFC;

            byte[] output = new byte[1000];

            AsnReader reader = new AsnReader(input, AsnEncodingRules.CER);

            bool success = reader.TryCopyOctetStringBytes(
                output,
                out int bytesWritten);

            Assert.True(success, "reader.TryCopyOctetStringBytes");
            Assert.Equal(1000, bytesWritten);

            Assert.Equal(
                input.AsSpan(4).ByteArrayToHex(),
                output.ByteArrayToHex());
        }
Exemple #4
0
        public static ReadOnlyMemory <byte> DecodeOctetStringAsMemory(ReadOnlyMemory <byte> encodedOctetString)
        {
            AsnReader reader = new AsnReader(encodedOctetString, AsnEncodingRules.BER);

            if (reader.PeekEncodedValue().Length != encodedOctetString.Length)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
            }

            // Almost everything in X.509 is DER-encoded, which means Octet String values are
            // encoded as a primitive (non-segmented)
            //
            // Even in BER Octet Strings are usually encoded as a primitive.
            if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> primitiveContents))
            {
                return(primitiveContents);
            }

            byte[] tooBig = new byte[encodedOctetString.Length];

            if (reader.TryCopyOctetStringBytes(tooBig, out int bytesWritten))
            {
                return(tooBig.AsMemory(0, bytesWritten));
            }

            Debug.Fail("TryCopyOctetStringBytes failed with an over-allocated array");
            throw new CryptographicException();
        }
Exemple #5
0
        public static void TryCopyOctetStringBytes_ExtremelyNested()
        {
            byte[] dataBytes = new byte[4 * 16384];

            // This will build 2^14 nested indefinite length values.
            // In the end, none of them contain any content.
            //
            // For what it's worth, the initial algorithm succeeded at 1061, and StackOverflowed with 1062.
            int end = dataBytes.Length / 2;

            // UNIVERSAL OCTET STRING [Constructed]
            const byte Tag = 0x20 | (byte)UniversalTagNumber.OctetString;

            for (int i = 0; i < end; i += 2)
            {
                dataBytes[i] = Tag;
                // Indefinite length
                dataBytes[i + 1] = 0x80;
            }

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

            int bytesWritten;

            Assert.True(reader.TryCopyOctetStringBytes(Span <byte> .Empty, out bytesWritten));
            Assert.Equal(0, bytesWritten);
        }
Exemple #6
0
        internal static ReadOnlyMemory <byte> GetContent(
            ReadOnlyMemory <byte> wrappedContent,
            string contentType)
        {
            // Read the input.
            //
            // PKCS7's id-data is written in both PKCS#7 and CMS as an OCTET STRING wrapping
            // the arbitrary bytes, so the OCTET STRING must always be present.
            //
            // For other types, CMS says to always write an OCTET STRING, and to put the properly
            // encoded data within it.
            // PKCS#7 originally ommitted the OCTET STRING wrapper for this model, so this is the
            // dynamic adapter.
            //
            // See https://tools.ietf.org/html/rfc5652#section-5.2.1
            byte[] rented       = null;
            int    bytesWritten = 0;

            try
            {
                AsnReader reader = new AsnReader(wrappedContent, AsnEncodingRules.BER);

                if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> inner))
                {
                    return(inner);
                }

                rented = ArrayPool <byte> .Shared.Rent(wrappedContent.Length);

                if (!reader.TryCopyOctetStringBytes(rented, out bytesWritten))
                {
                    Debug.Fail($"TryCopyOctetStringBytes failed with an array larger than the encoded value");
                    throw new CryptographicException();
                }

                return(rented.AsSpan(0, bytesWritten).ToArray());
            }
            catch (Exception)
            {
                if (contentType == Oids.Pkcs7Data)
                {
                    throw;
                }
            }
            finally
            {
                if (rented != null)
                {
                    rented.AsSpan(0, bytesWritten).Clear();
                    ArrayPool <byte> .Shared.Return(rented);
                }
            }

            // PKCS#7 encoding for something other than id-data.
            Debug.Assert(contentType != Oids.Pkcs7Data);
            return(wrappedContent);
        }
Exemple #7
0
        public static void TryCopyOctetStringBytes_Fails(PublicEncodingRules ruleSet, string inputHex)
        {
            byte[]    inputData = inputHex.HexToByteArray();
            AsnReader reader    = new AsnReader(inputData, (AsnEncodingRules)ruleSet);

            bool didRead = reader.TryCopyOctetStringBytes(
                Span <byte> .Empty,
                out int bytesWritten);

            Assert.False(didRead, "reader.TryCopyOctetStringBytes");
            Assert.Equal(0, bytesWritten);
        }
Exemple #8
0
 private static void TryCopyOctetStringBytes_Throws(
     PublicEncodingRules ruleSet,
     byte[] input)
 {
     Assert.Throws <CryptographicException>(
         () =>
     {
         AsnReader reader = new AsnReader(input, (AsnEncodingRules)ruleSet);
         reader.TryCopyOctetStringBytes(
             Span <byte> .Empty,
             out int bytesWritten);
     });
 }
Exemple #9
0
        private static SymmetricAlgorithm OpenAlgorithm(AlgorithmIdentifierAsn contentEncryptionAlgorithm)
        {
            SymmetricAlgorithm alg = OpenAlgorithm(contentEncryptionAlgorithm.Algorithm);

            if (alg is RC2)
            {
                if (contentEncryptionAlgorithm.Parameters == null)
                {
                    // Windows issues CRYPT_E_BAD_DECODE
                    throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                }

                Rc2CbcParameters rc2Params = Rc2CbcParameters.Decode(
                    contentEncryptionAlgorithm.Parameters.Value,
                    AsnEncodingRules.BER);

                alg.KeySize = rc2Params.GetEffectiveKeyBits();
                alg.IV      = rc2Params.Iv.ToArray();
            }
            else
            {
                if (contentEncryptionAlgorithm.Parameters == null)
                {
                    // Windows issues CRYPT_E_BAD_DECODE
                    throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                }

                AsnReader reader = new AsnReader(contentEncryptionAlgorithm.Parameters.Value, AsnEncodingRules.BER);

                if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> primitiveBytes))
                {
                    alg.IV = primitiveBytes.ToArray();
                }
                else
                {
                    byte[] iv = new byte[alg.BlockSize / 8];

                    if (!reader.TryCopyOctetStringBytes(iv, out int bytesWritten) ||
                        bytesWritten != iv.Length)
                    {
                        throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                    }

                    alg.IV = iv;
                }
            }

            return(alg);
        }
Exemple #10
0
        public static void TryCopyOctetStringBytes_Success(
            PublicEncodingRules ruleSet,
            string inputHex,
            string expectedHex)
        {
            byte[]    inputData = inputHex.HexToByteArray();
            byte[]    output    = new byte[expectedHex.Length / 2];
            AsnReader reader    = new AsnReader(inputData, (AsnEncodingRules)ruleSet);

            bool didRead = reader.TryCopyOctetStringBytes(
                output,
                out int bytesWritten);

            Assert.True(didRead, "reader.TryCopyOctetStringBytes");
            Assert.Equal(expectedHex, output.AsSpan(0, bytesWritten).ByteArrayToHex());
        }
Exemple #11
0
        public static ReadOnlyMemory <byte> DecodeOctetString(ReadOnlyMemory <byte> encodedOctetString)
        {
            AsnReader reader = new AsnReader(encodedOctetString, AsnEncodingRules.BER);

            if (reader.PeekEncodedValue().Length != encodedOctetString.Length)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
            }

            if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> primitiveContents))
            {
                return(primitiveContents);
            }

            byte[] tooBig = new byte[encodedOctetString.Length];

            if (reader.TryCopyOctetStringBytes(tooBig, out int bytesWritten))
            {
                return(tooBig.AsMemory(0, bytesWritten));
            }

            Debug.Fail("TryCopyOctetStringBytes failed with an over-allocated array");
            throw new CryptographicException();
        }
Exemple #12
0
        public KerberosError(ref AsnReader reader)
        {
            var tag = reader.ReadTagAndLength(out var contentLength, out var bytesRead);

            reader = reader.AdvanceReader(bytesRead);

            if (tag.TagClass != TagClass.Universal || tag.TagValue != (int)UniversalTagNumber.Sequence)
            {
                throw new InvalidOperationException("Not a sequence but " + tag);
            }

            while (reader.HasData)
            {
                tag    = reader.ReadTagAndLength(out contentLength, out bytesRead);
                reader = reader.AdvanceReader(bytesRead);

                if (tag.TagClass == TagClass.ContextSpecific)
                {
                    switch (tag.TagValue)
                    {
                    case 0:
                        //         pvno            [0] INTEGER (5),
                        ProtocolVersionNumber = (int)reader.GetInteger();
                        break;

                    case 1:
                        //         msg-type        [1] INTEGER (30),
                        MessageType = (MessageType)(int)reader.GetInteger();
                        break;

                    case 2:
                        //         ctime           [2] KerberosTime OPTIONAL,
                        CTime = reader.GetGeneralizedTime(disallowFractions: true);
                        break;

                    case 3:
                        //         cusec           [3] Microseconds OPTIONAL,
                        var cusec = reader.GetInteger();
                        CUsec = new Microseconds((int)cusec);
                        break;

                    case 4:
                        //         stime           [4] KerberosTime,
                        STime = reader.GetGeneralizedTime(disallowFractions: true);
                        break;

                    case 5:
                        //         susec           [5] Microseconds,
                        var susec = reader.GetInteger();
                        SUsec = new Microseconds((int)susec);
                        break;

                    case 6:
                        //         error-code      [6] Int32,
                        ErrorCode = (KrbErrorCode)(int)reader.GetInteger();
                        break;

                    case 7:
                        //         crealm          [7] Realm OPTIONAL,
                        CRealm = reader.GetCharacterString(new Asn1Tag(UniversalTagNumber.GeneralString), KerberosTags.RealmTag);
                        break;

                    case 8:
                        //         cname           [8] PrincipalName OPTIONAL,
                        CName = new PrincipalName(ref reader);
                        break;

                    case 9:
                        //         realm           [9] Realm -- service realm --,
                        ServiceRealm = reader.GetCharacterString(new Asn1Tag(UniversalTagNumber.GeneralString), KerberosTags.RealmTag);
                        break;

                    case 10:
                        //         sname           [10] PrincipalName -- service name --,
                        SName = new PrincipalName(ref reader);
                        break;

                    case 11:
                        //         e-text          [11] KerberosString OPTIONAL,
                        EText = reader.GetCharacterString(new Asn1Tag(UniversalTagNumber.GeneralString), KerberosTags.KerberosStringTag);
                        break;

                    case 12:
                        //         e-data          [12] OCTET STRING OPTIONAL
                        EData = new byte[contentLength.Value];
                        reader.TryCopyOctetStringBytes(EData, out _);
                        break;
                    }
                }
            }
        }
Exemple #13
0
            public static unsafe ContentInfo TryDecryptCore(
                byte[] cek,
                string contentType,
                ReadOnlyMemory <byte>?content,
                AlgorithmIdentifierAsn contentEncryptionAlgorithm,
                out Exception exception)
            {
                if (content == null)
                {
                    exception = null;

                    return(new ContentInfo(
                               new Oid(contentType),
                               Array.Empty <byte>()));
                }

                byte[] decrypted = DecryptContent(content.Value, cek, contentEncryptionAlgorithm, out exception);

                if (exception != null)
                {
                    return(null);
                }

                if (contentType == Oids.Pkcs7Data)
                {
                    byte[] tmp = null;

                    try
                    {
                        AsnReader reader = new AsnReader(decrypted, AsnEncodingRules.BER);

                        if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> contents))
                        {
                            decrypted = contents.ToArray();
                        }
                        else
                        {
                            tmp = ArrayPool <byte> .Shared.Rent(decrypted.Length);

                            if (reader.TryCopyOctetStringBytes(tmp, out int written))
                            {
                                Span <byte> innerContents = new Span <byte>(tmp, 0, written);
                                decrypted = innerContents.ToArray();
                                innerContents.Clear();
                            }
                            else
                            {
                                Debug.Fail("Octet string grew during copy");
                                // If this happens (which requires decrypted was overwritten, which
                                // shouldn't be possible), just leave decrypted alone.
                            }
                        }
                    }
                    catch (CryptographicException)
                    {
                    }
                    finally
                    {
                        if (tmp != null)
                        {
                            // Already cleared
                            ArrayPool <byte> .Shared.Return(tmp);
                        }
                    }
                }
                else
                {
                    decrypted = GetAsnSequenceWithContentNoValidation(decrypted);
                }

                exception = null;
                return(new ContentInfo(
                           new Oid(contentType),
                           decrypted));
            }
Exemple #14
0
            public static unsafe ContentInfo?TryDecryptCore(
                byte[] cek,
                string contentType,
                ReadOnlyMemory <byte>?content,
                AlgorithmIdentifierAsn contentEncryptionAlgorithm,
                out Exception?exception)
            {
                if (content == null)
                {
                    exception = null;

                    return(new ContentInfo(
                               new Oid(contentType),
                               Array.Empty <byte>()));
                }

                byte[]? decrypted = DecryptContent(content.Value, cek, contentEncryptionAlgorithm, out exception);

                if (exception != null)
                {
                    return(null);
                }

                // Compat: Previous versions of the managed PAL encryptor would wrap the contents in an octet stream
                // which is not correct and is incompatible with other CMS readers. To maintain compatibility with
                // existing CMS that have the incorrect wrapping, we attempt to remove it.
                if (contentType == Oids.Pkcs7Data)
                {
                    byte[]? tmp = null;

                    try
                    {
                        AsnReader reader = new AsnReader(decrypted, AsnEncodingRules.BER);

                        if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> contents))
                        {
                            decrypted = contents.ToArray();
                        }
                        else
                        {
                            tmp = CryptoPool.Rent(decrypted !.Length);

                            if (reader.TryCopyOctetStringBytes(tmp, out int written))
                            {
                                Span <byte> innerContents = new Span <byte>(tmp, 0, written);
                                decrypted = innerContents.ToArray();
                                innerContents.Clear();
                            }
                            else
                            {
                                Debug.Fail("Octet string grew during copy");
                                // If this happens (which requires decrypted was overwritten, which
                                // shouldn't be possible), just leave decrypted alone.
                            }
                        }
                    }
                    catch (CryptographicException)
                    {
                    }
                    finally
                    {
                        if (tmp != null)
                        {
                            // Already cleared
                            CryptoPool.Return(tmp, clearSize: 0);
                        }
                    }
                }
                else
                {
                    decrypted = GetAsnSequenceWithContentNoValidation(decrypted);
                }

                exception = null;
                return(new ContentInfo(
                           new Oid(contentType),
                           decrypted !));
            }
Exemple #15
0
        public static void TryCopyOctetStringBytes_Success_CER_MinConstructedLength()
        {
            // CER says that the maximum encoding length for an OctetString primitive
            // is 1000, and that a constructed form must be used for values greater
            // than 1000 bytes, with segments dividing up for each thousand
            // [1000, 1000, ..., len%1000].
            //
            // So our smallest constructed form is 1001 bytes, [1000, 1]
            //
            // 24 80 (indefinite constructed octet string)
            //    04 82 03 E9 (primitive octet string, 1000 bytes)
            //       [1000 content bytes]
            //    04 01 (primitive octet string, 1 byte)
            //       pp
            //    00 00 (end of contents, 0 bytes)
            // 1011 total.
            byte[] input  = new byte[1011];
            int    offset = 0;

            // CONSTRUCTED OCTET STRING (Indefinite)
            input[offset++] = 0x24;
            input[offset++] = 0x80;
            // OCTET STRING (1000)
            input[offset++] = 0x04;
            input[offset++] = 0x82;
            input[offset++] = 0x03;
            input[offset++] = 0xE8;

            // Primitive 1: (55 A0 :: A5 FC) (1000)
            input[offset++] = 0x55;
            input[offset]   = 0xA0;
            offset         += 997;
            input[offset++] = 0xA5;
            input[offset++] = 0xFC;

            // OCTET STRING (1)
            input[offset++] = 0x04;
            input[offset++] = 0x01;

            // Primitive 2: One more byte
            input[offset] = 0xF7;

            byte[] expected = new byte[1001];
            offset             = 0;
            expected[offset++] = 0x55;
            expected[offset]   = 0xA0;
            offset            += 997;
            expected[offset++] = 0xA5;
            expected[offset++] = 0xFC;
            expected[offset]   = 0xF7;

            byte[] output = new byte[1001];

            AsnReader reader = new AsnReader(input, AsnEncodingRules.CER);

            bool success = reader.TryCopyOctetStringBytes(
                output,
                out int bytesWritten);

            Assert.True(success, "reader.TryCopyOctetStringBytes");
            Assert.Equal(1001, bytesWritten);

            Assert.Equal(
                expected.ByteArrayToHex(),
                output.ByteArrayToHex());
        }
Exemple #16
0
        internal static AlgorithmIdentifier ToPresentationObject(this AlgorithmIdentifierAsn asn)
        {
            int keyLength;

            switch (asn.Algorithm.Value)
            {
            case Oids.Rc2Cbc:
            {
                if (asn.Parameters == null)
                {
                    keyLength = 0;
                    break;
                }

                Rc2CbcParameters rc2Params = AsnSerializer.Deserialize <Rc2CbcParameters>(
                    asn.Parameters.Value,
                    AsnEncodingRules.BER);

                int keySize = rc2Params.GetEffectiveKeyBits();

                // These are the only values .NET Framework would set.
                switch (keySize)
                {
                case 40:
                case 56:
                case 64:
                case 128:
                    keyLength = keySize;
                    break;

                default:
                    keyLength = 0;
                    break;
                }

                break;
            }

            case Oids.Rc4:
            {
                if (asn.Parameters == null)
                {
                    keyLength = 0;
                    break;
                }

                int       saltLen = 0;
                AsnReader reader  = new AsnReader(asn.Parameters.Value, AsnEncodingRules.BER);

                // DER NULL is considered the same as not present.
                // No call to ReadNull() is necessary because the serializer already verified that
                // there's no data after the [AnyValue] value.
                if (reader.PeekTag() != Asn1Tag.Null)
                {
                    if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> contents))
                    {
                        saltLen = contents.Length;
                    }
                    else
                    {
                        Span <byte> salt = stackalloc byte[KeyLengths.Rc4Max_128Bit / 8];

                        if (!reader.TryCopyOctetStringBytes(salt, out saltLen))
                        {
                            throw new CryptographicException();
                        }
                    }
                }

                keyLength = KeyLengths.Rc4Max_128Bit - 8 * saltLen;
                break;
            }

            case Oids.DesCbc:
                keyLength = KeyLengths.Des_64Bit;
                break;

            case Oids.TripleDesCbc:
                keyLength = KeyLengths.TripleDes_192Bit;
                break;

            default:
                // .NET Framework doesn't set a keylength for AES, or any other algorithm than the ones
                // listed here.
                keyLength = 0;
                break;
            }

            return(new AlgorithmIdentifier(new Oid(asn.Algorithm), keyLength));
        }
Exemple #17
0
            public override unsafe ContentInfo TryDecrypt(
                RecipientInfo recipientInfo,
                X509Certificate2 cert,
                X509Certificate2Collection originatorCerts,
                X509Certificate2Collection extraStore,
                out Exception exception)
            {
                // When encryptedContent is null Windows seems to decrypt the CEK first,
                // then return a 0 byte answer.

                byte[] cek;

                if (recipientInfo.Pal is ManagedKeyTransPal ktri)
                {
                    cek = ktri.DecryptCek(cert, out exception);
                }
                else
                {
                    exception = new CryptographicException(
                        SR.Cryptography_Cms_RecipientType_NotSupported,
                        recipientInfo.Type.ToString());

                    return(null);
                }

                byte[] decrypted;

                // Pin CEK to prevent it from getting copied during heap compaction.
                fixed(byte *pinnedCek = cek)
                {
                    try
                    {
                        if (exception != null)
                        {
                            return(null);
                        }

                        ReadOnlyMemory <byte>?encryptedContent = _envelopedData.EncryptedContentInfo.EncryptedContent;

                        if (encryptedContent == null)
                        {
                            exception = null;

                            return(new ContentInfo(
                                       new Oid(_envelopedData.EncryptedContentInfo.ContentType),
                                       Array.Empty <byte>()));
                        }

                        decrypted = DecryptContent(encryptedContent.Value, cek, out exception);
                    }
                    finally
                    {
                        if (cek != null)
                        {
                            Array.Clear(cek, 0, cek.Length);
                        }
                    }
                }

                if (exception != null)
                {
                    return(null);
                }

                if (_envelopedData.EncryptedContentInfo.ContentType == Oids.Pkcs7Data)
                {
                    byte[] tmp = null;

                    try
                    {
                        AsnReader reader = new AsnReader(decrypted, AsnEncodingRules.BER);

                        if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> contents))
                        {
                            decrypted = contents.ToArray();
                        }
                        else
                        {
                            tmp = ArrayPool <byte> .Shared.Rent(decrypted.Length);

                            if (reader.TryCopyOctetStringBytes(tmp, out int written))
                            {
                                Span <byte> innerContents = new Span <byte>(tmp, 0, written);
                                decrypted = innerContents.ToArray();
                                innerContents.Clear();
                            }
                            else
                            {
                                Debug.Fail("Octet string grew during copy");
                                // If this happens (which requires decrypted was overwritten, which
                                // shouldn't be possible), just leave decrypted alone.
                            }
                        }
                    }
                    catch (CryptographicException)
                    {
                    }
                    finally
                    {
                        if (tmp != null)
                        {
                            // Already cleared
                            ArrayPool <byte> .Shared.Return(tmp);
                        }
                    }
                }
                else
                {
                    decrypted = GetAsnSequenceWithContentNoValidation(decrypted);
                }

                exception = null;
                return(new ContentInfo(
                           new Oid(_envelopedData.EncryptedContentInfo.ContentType),
                           decrypted));
            }