Exemplo n.º 1
0
        public static void CopyEncryptedSafeContents(bool withSpan)
        {
            Pkcs12Builder builder1 = new Pkcs12Builder();
            Pkcs12Builder builder2 = new Pkcs12Builder();

            Pkcs12SafeContents contents = new Pkcs12SafeContents();

            contents.AddSecret(s_zeroOid, s_derNull);

            if (withSpan)
            {
                builder1.AddSafeContentsEncrypted(contents, ReadOnlySpan <byte> .Empty, s_pbkdf2Parameters);
            }
            else
            {
                builder1.AddSafeContentsEncrypted(contents, (byte[])null, s_pbkdf2Parameters);
            }

            builder1.SealWithoutIntegrity();

            byte[]     encoded1 = builder1.Encode();
            Pkcs12Info info     = Pkcs12Info.Decode(encoded1, out _, skipCopy: true);

            Assert.Equal(Pkcs12IntegrityMode.None, info.IntegrityMode);
            Assert.Equal(1, info.AuthenticatedSafe.Count);

            builder2.AddSafeContentsUnencrypted(info.AuthenticatedSafe[0]);
            builder2.SealWithoutIntegrity();

            byte[] encoded2 = builder2.Encode();
            Assert.Equal(encoded1.ByteArrayToHex(), encoded2.ByteArrayToHex());
        }
Exemplo n.º 2
0
        public static void AddContentsAfterSealing()
        {
            Pkcs12SafeContents contents = new Pkcs12SafeContents();

            contents.AddSecret(s_zeroOid, s_derNull);

            Pkcs12Builder builder = new Pkcs12Builder();

            builder.SealWithoutIntegrity();

            Assert.Throws <InvalidOperationException>(
                () => builder.AddSafeContentsUnencrypted(contents));

            Assert.Throws <InvalidOperationException>(
                () => builder.AddSafeContentsEncrypted(contents, Array.Empty <byte>(), s_pbkdf2Parameters));

            Assert.Throws <InvalidOperationException>(
                () => builder.AddSafeContentsEncrypted(contents, ReadOnlySpan <byte> .Empty, s_pbkdf2Parameters));

            Assert.Throws <InvalidOperationException>(
                () => builder.AddSafeContentsEncrypted(contents, string.Empty, s_pbkdf2Parameters));

            Assert.Throws <InvalidOperationException>(
                () => builder.AddSafeContentsEncrypted(contents, ReadOnlySpan <char> .Empty, s_pbkdf2Parameters));
        }
Exemplo n.º 3
0
        public static void BuildWithoutContents(bool withMac)
        {
            string        password;
            Pkcs12Builder builder = new Pkcs12Builder();

            if (withMac)
            {
                password = "******";
                builder.SealWithMac(password, HashAlgorithmName.SHA1, 2);
            }
            else
            {
                password = null;
                builder.SealWithoutIntegrity();
            }

            byte[] encoded = builder.Encode();

            const string FullyEmptyHex =
                "3016020103301106092A864886F70D010701A00404023000";

            if (withMac)
            {
                Assert.Equal(60 + FullyEmptyHex.Length / 2, encoded.Length);
            }
            else
            {
                Assert.Equal(FullyEmptyHex, encoded.ByteArrayToHex());
            }

            X509Certificate2Collection collection = new X509Certificate2Collection();

            collection.Import(encoded, password, X509KeyStorageFlags.DefaultKeySet);
            Assert.Equal(0, collection.Count);
        }
Exemplo n.º 4
0
        public void EmptyPfx_NoMac()
        {
            Pkcs12Builder builder = new Pkcs12Builder();

            builder.SealWithoutIntegrity();
            ReadEmptyPfx(builder.Encode(), correctPassword: null);
        }
Exemplo n.º 5
0
        public static void EncryptEncryptedSafeContents()
        {
            Pkcs12Builder builder1 = new Pkcs12Builder();
            Pkcs12Builder builder2 = new Pkcs12Builder();

            Pkcs12SafeContents contents = new Pkcs12SafeContents();

            contents.AddSecret(s_zeroOid, s_derNull);

            builder1.AddSafeContentsEncrypted(contents, ReadOnlySpan <byte> .Empty, s_pbkdf2Parameters);
            builder1.SealWithoutIntegrity();

            byte[]     encoded = builder1.Encode();
            Pkcs12Info info    = Pkcs12Info.Decode(encoded, out _, skipCopy: true);

            Assert.Equal(Pkcs12IntegrityMode.None, info.IntegrityMode);
            Assert.Equal(1, info.AuthenticatedSafe.Count);

            AssertExtensions.Throws <ArgumentException>(
                "safeContents",
                () => builder2.AddSafeContentsEncrypted(
                    info.AuthenticatedSafe[0],
                    "nope",
                    s_pbkdf2Parameters));

            AssertExtensions.Throws <ArgumentException>(
                "safeContents",
                () => builder2.AddSafeContentsEncrypted(
                    info.AuthenticatedSafe[0],
                    s_derNull.Span,
                    s_pbkdf2Parameters));
        }
Exemplo n.º 6
0
        public static void ReadSerializedData(bool encryptSafe)
        {
            Pkcs12SafeContents container = new Pkcs12SafeContents();

            Pkcs12SafeContents builtContents = new Pkcs12SafeContents();

            builtContents.AddSecret(s_zeroOid, s_derNull);

            builtContents.AddSecret(s_zeroOid, new byte[] { 4, 1, 2 }).Attributes.Add(
                new Pkcs9LocalKeyId(s_derNull.Span));

            builtContents.AddSecret(s_zeroOid, new byte[] { 4, 1, 3 });
            container.AddNestedContents(builtContents);

            Pkcs12Builder builder = new Pkcs12Builder();

            if (encryptSafe)
            {
                builder.AddSafeContentsEncrypted(container, s_derNull.Span, s_pbkdf2Parameters);
            }
            else
            {
                builder.AddSafeContentsUnencrypted(container);
            }

            builder.SealWithoutIntegrity();
            byte[] encoded = builder.Encode();

            Pkcs12Info         info     = Pkcs12Info.Decode(encoded, out _, skipCopy: true);
            Pkcs12SafeContents onlySafe = info.AuthenticatedSafe.Single();

            if (encryptSafe)
            {
                onlySafe.Decrypt(s_derNull.Span);
            }

            Pkcs12SafeBag         onlyBag         = onlySafe.GetBags().Single();
            Pkcs12SafeContentsBag safeContentsBag = Assert.IsType <Pkcs12SafeContentsBag>(onlyBag);
            Pkcs12SafeContents    readContents    = safeContentsBag.SafeContents;

            Assert.Equal(
                Pkcs12ConfidentialityMode.None,
                readContents.ConfidentialityMode);

            Assert.True(readContents.IsReadOnly);

            List <Pkcs12SafeBag> bags1 = builtContents.GetBags().ToList();
            List <Pkcs12SafeBag> bags2 = readContents.GetBags().ToList();

            Assert.Equal(bags1.Count, bags2.Count);

            for (int i = 0; i < bags2.Count; i++)
            {
                byte[] encoded1 = bags1[i].Encode();
                byte[] encoded2 = bags1[i].Encode();

                Assert.True(encoded1.AsSpan().SequenceEqual(encoded2), $"Bag {i} encodes the same");
            }
        }
Exemplo n.º 7
0
        public void EmptyPfx_NoMac_ArbitraryPassword()
        {
            Pkcs12Builder builder = new Pkcs12Builder();

            builder.SealWithoutIntegrity();
            byte[] pfxBytes = builder.Encode();

            ReadEmptyPfx(pfxBytes, "arbitrary password");
            ReadEmptyPfx(pfxBytes, "other arbitrary password");
        }
Exemplo n.º 8
0
        public static void SealAfterSeal()
        {
            Pkcs12Builder builder = new Pkcs12Builder();

            builder.SealWithMac(ReadOnlySpan <char> .Empty, HashAlgorithmName.SHA1, 2);

            Assert.Throws <InvalidOperationException>(() => builder.SealWithoutIntegrity());

            Assert.Throws <InvalidOperationException>(
                () => builder.SealWithMac(ReadOnlySpan <char> .Empty, HashAlgorithmName.SHA1, 2));
        }
Exemplo n.º 9
0
        public void OneCert_NoKeys_EncryptedEmptyPassword_NoMac()
        {
            using (X509Certificate2 cert = new X509Certificate2(TestData.MsCertificate))
            {
                Pkcs12Builder      builder      = new Pkcs12Builder();
                Pkcs12SafeContents certContents = new Pkcs12SafeContents();
                certContents.AddCertificate(cert);
                builder.AddSafeContentsEncrypted(certContents, string.Empty, s_windowsPbe);
                builder.SealWithoutIntegrity();
                byte[] pfxBytes = builder.Encode();

                ReadPfx(pfxBytes, null, cert);
                ReadPfx(pfxBytes, string.Empty, cert);
            }
        }
Exemplo n.º 10
0
        public void OneCert_MismatchedKey()
        {
            string pw = nameof(OneCert_MismatchedKey);

            // Build the PFX in the normal Windows style, except the private key doesn't match.
            using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, s_exportableImportFlags))
                using (RSA realKey = cert.GetRSAPrivateKey())
                    using (RSA key = RSA.Create(realKey.KeySize))
                    {
                        Pkcs12Builder      builder      = new Pkcs12Builder();
                        Pkcs12SafeContents keyContents  = new Pkcs12SafeContents();
                        Pkcs12SafeBag      keyBag       = keyContents.AddShroudedKey(key, pw, s_windowsPbe);
                        Pkcs12SafeContents certContents = new Pkcs12SafeContents();
                        Pkcs12SafeBag      certBag      = certContents.AddCertificate(cert);

                        keyBag.Attributes.Add(s_keyIdOne);
                        certBag.Attributes.Add(s_keyIdOne);

                        builder.AddSafeContentsUnencrypted(keyContents);
                        builder.AddSafeContentsEncrypted(certContents, pw, s_windowsPbe);
                        builder.SealWithoutIntegrity();
                        byte[] pfxBytes = builder.Encode();

                        // On macOS the cert will come back with HasPrivateKey being false.
                        if (OperatingSystem.IsMacOS())
                        {
                            using (var publicCert = new X509Certificate2(cert.RawData))
                            {
                                ReadPfx(
                                    pfxBytes,
                                    pw,
                                    publicCert);
                            }

                            return;
                        }

                        ReadPfx(
                            pfxBytes,
                            pw,
                            cert,
                            CheckKeyConsistencyFails);
                    }
        }
Exemplo n.º 11
0
        public static void BuildEmptyContents(bool withMac)
        {
            string             password;
            Pkcs12Builder      builder  = new Pkcs12Builder();
            Pkcs12SafeContents contents = new Pkcs12SafeContents();

            builder.AddSafeContentsUnencrypted(contents);

            if (withMac)
            {
                password = "******";
                builder.SealWithMac(password, HashAlgorithmName.SHA1, 2);
            }
            else
            {
                password = null;
                builder.SealWithoutIntegrity();
            }

            byte[] encoded = builder.Encode();

            const string EmptyHex =
                "3029020103302406092A864886F70D010701A01704153013301106092A864886" +
                "F70D010701A00404023000";

            if (withMac)
            {
                Assert.Equal(60 + EmptyHex.Length / 2, encoded.Length);
            }
            else
            {
                Assert.Equal(EmptyHex, encoded.ByteArrayToHex());
            }

            // [ActiveIssue(11046, TestPlatforms.AnyUnix)]
            if (withMac || RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                X509Certificate2Collection collection = new X509Certificate2Collection();
                collection.Import(encoded, password, X509KeyStorageFlags.DefaultKeySet);
                Assert.Equal(0, collection.Count);
            }
        }
Exemplo n.º 12
0
        public static void WriteCustomType()
        {
            Pkcs12SafeContents contents = new Pkcs12SafeContents();

            contents.AddSafeBag(new CustomBagType(2));

            Pkcs12Builder builder = new Pkcs12Builder();

            builder.AddSafeContentsUnencrypted(contents);
            builder.SealWithoutIntegrity();

            byte[] encoded = builder.Encode();

            const string expectedHex =
                "3033020103302E06092A864886F70D010701A021041F301D301B06092A864886" +
                "F70D010701A00E040C300A3008060100A003040102";

            Assert.Equal(
                expectedHex,
                encoded.ByteArrayToHex());
        }
Exemplo n.º 13
0
        public void OneCert_EncryptedEmptyPassword_OneKey_EncryptedNullPassword_NoMac(bool encryptKeySafe, bool associateKey)
        {
            // This test shows that while a null or empty password will result in both
            // types being tested, the PFX contents have to be the same throughout.
            using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, s_exportableImportFlags))
                using (AsymmetricAlgorithm key = cert.GetRSAPrivateKey())
                {
                    Pkcs12Builder      builder      = new Pkcs12Builder();
                    Pkcs12SafeContents keyContents  = new Pkcs12SafeContents();
                    Pkcs12SafeBag      keyBag       = keyContents.AddShroudedKey(key, (string)null, s_windowsPbe);
                    Pkcs12SafeContents certContents = new Pkcs12SafeContents();
                    Pkcs12SafeBag      certBag      = certContents.AddCertificate(cert);

                    if (associateKey)
                    {
                        keyBag.Attributes.Add(s_keyIdOne);
                        certBag.Attributes.Add(s_keyIdOne);
                    }

                    AddContents(keyContents, builder, null, encryptKeySafe);
                    AddContents(certContents, builder, string.Empty, encrypt: true);
                    builder.SealWithoutIntegrity();
                    byte[] pfxBytes = builder.Encode();

                    if (s_loaderFailsKeysEarly || associateKey || encryptKeySafe)
                    {
                        // NTE_FAIL, falling back to CRYPT_E_BAD_ENCODE if padding happened to work out.
                        ReadUnreadablePfx(pfxBytes, null, altWin32Error: -2146885630);
                        ReadUnreadablePfx(pfxBytes, string.Empty, altWin32Error: -2146885630);
                    }
                    else
                    {
                        using (var publicOnlyCert = new X509Certificate2(cert.RawData))
                        {
                            ReadPfx(pfxBytes, string.Empty, publicOnlyCert);
                        }
                    }
                }
        }
Exemplo n.º 14
0
        public static void CopyCustomType()
        {
            const string startHex =
                "3033020103302E06092A864886F70D010701A021041F301D301B06092A864886" +
                "F70D010701A00E040C300A3008060100A003040102";

            Pkcs12Info info = Pkcs12Info.Decode(startHex.HexToByteArray(), out _, skipCopy: true);
            // This next line implicitly asserts no encryption, and a couple of Single
            Pkcs12SafeBag bag = info.AuthenticatedSafe.Single().GetBags().Single();

            Pkcs12SafeContents contents = new Pkcs12SafeContents();

            contents.AddSafeBag(bag);

            Pkcs12Builder builder = new Pkcs12Builder();

            builder.AddSafeContentsUnencrypted(contents);
            builder.SealWithoutIntegrity();
            byte[] encoded = builder.Encode();

            Assert.Equal(startHex, encoded.ByteArrayToHex());
        }
Exemplo n.º 15
0
        public static void AddEncryptedNestedContents()
        {
            Pkcs12Builder      builder  = new Pkcs12Builder();
            Pkcs12SafeContents contents = new Pkcs12SafeContents();

            contents.AddSecret(s_zeroOid, s_derNull);

            builder.AddSafeContentsEncrypted(contents, "hi", s_pbeParameters);
            builder.SealWithoutIntegrity();

            byte[]     encoded = builder.Encode();
            Pkcs12Info info    = Pkcs12Info.Decode(encoded, out _, skipCopy: true);

            Assert.Equal(Pkcs12IntegrityMode.None, info.IntegrityMode);
            Assert.Equal(1, info.AuthenticatedSafe.Count);

            Pkcs12SafeContents newContents = new Pkcs12SafeContents();

            AssertExtensions.Throws <ArgumentException>(
                "safeContents",
                () => newContents.AddNestedContents(info.AuthenticatedSafe[0]));
        }
Exemplo n.º 16
0
        public static void BuildWithoutContents(bool withMac)
        {
            string        password;
            Pkcs12Builder builder = new Pkcs12Builder();

            if (withMac)
            {
                password = "******";
                builder.SealWithMac(password, HashAlgorithmName.SHA1, 2);
            }
            else
            {
                password = null;
                builder.SealWithoutIntegrity();
            }

            byte[] encoded = builder.Encode();

            const string FullyEmptyHex =
                "3016020103301106092A864886F70D010701A00404023000";

            if (withMac)
            {
                Assert.Equal(60 + FullyEmptyHex.Length / 2, encoded.Length);
            }
            else
            {
                Assert.Equal(FullyEmptyHex, encoded.ByteArrayToHex());
            }

            // [ActiveIssue("https://github.com/dotnet/corefx/issues/11046", TestPlatforms.AnyUnix)]
            if (withMac || RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                X509Certificate2Collection collection = new X509Certificate2Collection();
                collection.Import(encoded, password, X509KeyStorageFlags.DefaultKeySet);
                Assert.Equal(0, collection.Count);
            }
        }
Exemplo n.º 17
0
        public void OneCert_TwoKeys_FirstWins(bool correctKeyFirst)
        {
            string pw = nameof(OneCert_TwoKeys_FirstWins);

            // Build the PFX in the normal Windows style, except the private key doesn't match.
            using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, s_exportableImportFlags))
                using (RSA key = cert.GetRSAPrivateKey())
                    using (RSA unrelated = RSA.Create(key.KeySize))
                    {
                        Pkcs12Builder      builder      = new Pkcs12Builder();
                        Pkcs12SafeContents keyContents  = new Pkcs12SafeContents();
                        Pkcs12SafeContents certContents = new Pkcs12SafeContents();
                        Pkcs12SafeBag      keyBag;
                        Pkcs12SafeBag      keyBag2;
                        Pkcs12SafeBag      certBag = certContents.AddCertificate(cert);

                        if (correctKeyFirst)
                        {
                            keyBag  = keyContents.AddShroudedKey(key, pw, s_windowsPbe);
                            keyBag2 = keyContents.AddShroudedKey(unrelated, pw, s_windowsPbe);
                        }
                        else
                        {
                            keyBag  = keyContents.AddShroudedKey(unrelated, pw, s_windowsPbe);
                            keyBag2 = keyContents.AddShroudedKey(key, pw, s_windowsPbe);
                        }

                        keyBag.Attributes.Add(s_keyIdOne);
                        keyBag2.Attributes.Add(s_keyIdOne);
                        certBag.Attributes.Add(s_keyIdOne);

                        builder.AddSafeContentsUnencrypted(keyContents);
                        builder.AddSafeContentsEncrypted(certContents, pw, s_windowsPbe);
                        builder.SealWithoutIntegrity();
                        byte[] pfxBytes = builder.Encode();

                        // On macOS the cert will come back with HasPrivateKey being false when the
                        // incorrect key comes first
                        if (!correctKeyFirst && OperatingSystem.IsMacOS())
                        {
                            using (var publicCert = new X509Certificate2(cert.RawData))
                            {
                                ReadPfx(
                                    pfxBytes,
                                    pw,
                                    publicCert);
                            }

                            return;
                        }

                        // The RSA "self-test" should pass when the correct key is first,
                        // and fail when the unrelated key is first.
                        Action <X509Certificate2> followup = CheckKeyConsistency;

                        if (!correctKeyFirst)
                        {
                            followup = CheckKeyConsistencyFails;
                        }

                        ReadPfx(
                            pfxBytes,
                            pw,
                            cert,
                            followup);
                    }
        }
        public void OneCertWithOneKey(SingleCertOptions options)
        {
            bool   sameContainer           = (options & SingleCertOptions.KeyAndCertInSameContents) != 0;
            bool   dontShroudKey           = (options & SingleCertOptions.UnshroudedKey) != 0;
            bool   keyContainerLast        = (options & SingleCertOptions.KeyContentsLast) != 0;
            bool   encryptCertSafeContents = (options & SingleCertOptions.PlaintextCertContents) == 0;
            bool   encryptKeySafeContents  = (options & SingleCertOptions.EncryptKeyContents) != 0;
            bool   skipMac  = (options & SingleCertOptions.SkipMac) != 0;
            string password = options.ToString();

            using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, s_exportableImportFlags))
                using (RSA key = cert.GetRSAPrivateKey())
                {
                    if (dontShroudKey && OperatingSystem.IsWindows())
                    {
                        // CNG keys are only encrypted-exportable, so we need to export them encrypted.
                        // Then we can import it into a new, fully-exportable key. (Sigh.)
                        byte[] tmpPkcs8 = key.ExportEncryptedPkcs8PrivateKey(password, s_windowsPbe);
                        key.ImportEncryptedPkcs8PrivateKey(password, tmpPkcs8, out _);
                    }

                    Pkcs12Builder builder = new Pkcs12Builder();

                    Pkcs12SafeContents certContents         = new Pkcs12SafeContents();
                    Pkcs12SafeContents keyContents          = sameContainer ? null : new Pkcs12SafeContents();
                    Pkcs12SafeContents keyEffectiveContents = keyContents ?? certContents;

                    Pkcs12SafeBag certBag = certContents.AddCertificate(cert);
                    Pkcs12SafeBag keyBag;

                    if (dontShroudKey)
                    {
                        keyBag = keyEffectiveContents.AddKeyUnencrypted(key);
                    }
                    else
                    {
                        keyBag = keyEffectiveContents.AddShroudedKey(key, password, s_windowsPbe);
                    }

                    certBag.Attributes.Add(s_keyIdOne);
                    keyBag.Attributes.Add(s_keyIdOne);

                    if (sameContainer)
                    {
                        AddContents(certContents, builder, password, encryptCertSafeContents);
                    }
                    else if (keyContainerLast)
                    {
                        AddContents(certContents, builder, password, encryptCertSafeContents);
                        AddContents(keyContents, builder, password, encryptKeySafeContents);
                    }
                    else
                    {
                        AddContents(keyContents, builder, password, encryptKeySafeContents);
                        AddContents(certContents, builder, password, encryptCertSafeContents);
                    }

                    if (skipMac)
                    {
                        builder.SealWithoutIntegrity();
                    }
                    else
                    {
                        builder.SealWithMac(password, s_digestAlgorithm, MacCount);
                    }

                    ReadPfx(builder.Encode(), password, cert);
                }
        }
Exemplo n.º 19
0
        public static void EncodeAndTryEncode(Pkcs12IntegrityMode mode)
        {
            Pkcs12Builder builder1 = new Pkcs12Builder();
            Pkcs12Builder builder2 = new Pkcs12Builder();

            Pkcs12SafeContents contents = new Pkcs12SafeContents();

            contents.AddSecret(s_zeroOid, s_derNull);

            builder1.AddSafeContentsUnencrypted(contents);
            builder2.AddSafeContentsUnencrypted(contents);

            int macTrailerLength = 0;

            if (mode == Pkcs12IntegrityMode.Password)
            {
                builder1.SealWithMac(ReadOnlySpan <char> .Empty, HashAlgorithmName.SHA1, 2);
                builder2.SealWithMac(ReadOnlySpan <char> .Empty, HashAlgorithmName.SHA1, 2);

                // Two OCTET STRINGs of 20 bytes, and the INTEGER 2
                macTrailerLength = 2 + 20 + 2 + 20 + 2 + 3;
            }
            else if (mode == Pkcs12IntegrityMode.None)
            {
                builder1.SealWithoutIntegrity();
                builder2.SealWithoutIntegrity();
            }

            Assert.True(builder1.IsSealed, "builder1.IsSealed");
            Assert.True(builder2.IsSealed, "builder2.IsSealed");

            byte[]      encoded = builder1.Encode();
            byte[]      buf     = new byte[encoded.Length + 40];
            Span <byte> bufSpan = buf;

            // Span too small
            Assert.False(builder2.TryEncode(buf.AsSpan(0, encoded.Length - 1), out int bytesWritten));
            Assert.Equal(0, bytesWritten);

            // Span exactly right
            bufSpan.Fill(0xCA);
            Assert.True(builder2.TryEncode(buf.AsSpan(1, encoded.Length), out bytesWritten));
            Assert.Equal(encoded.Length, bytesWritten);
            Assert.Equal(0xCA, buf[0]);
            Assert.Equal(0xCA, buf[bytesWritten + 1]);

            if (mode == Pkcs12IntegrityMode.Password)
            {
                Assert.Equal(0x02, buf[bytesWritten]);
            }

            // The same contents except the MAC (different random salt)
            Assert.Equal(
                encoded.AsSpan(0, bytesWritten - macTrailerLength).ByteArrayToHex(),
                buf.AsSpan(1, bytesWritten - macTrailerLength).ByteArrayToHex());

            if (macTrailerLength > 0)
            {
                Assert.NotEqual(
                    encoded.AsSpan(bytesWritten - macTrailerLength).ByteArrayToHex(),
                    buf.AsSpan(1 + bytesWritten - macTrailerLength, macTrailerLength).ByteArrayToHex());
            }

            // Span larger than needed
            bufSpan.Fill(0xCA);
            Assert.True(builder2.TryEncode(buf.AsSpan(2), out bytesWritten));
            Assert.Equal(encoded.Length, bytesWritten);
            Assert.Equal(0xCA, buf[0]);
            Assert.Equal(0xCA, buf[1]);
            Assert.Equal(0xCA, buf[bytesWritten + 2]);

            if (mode == Pkcs12IntegrityMode.Password)
            {
                Assert.Equal(0x02, buf[bytesWritten + 1]);
            }

            // The same contents except the MAC (different random salt)
            Assert.Equal(
                encoded.AsSpan(0, bytesWritten - macTrailerLength).ByteArrayToHex(),
                buf.AsSpan(2, bytesWritten - macTrailerLength).ByteArrayToHex());

            if (macTrailerLength > 0)
            {
                Assert.NotEqual(
                    encoded.AsSpan(bytesWritten - macTrailerLength).ByteArrayToHex(),
                    buf.AsSpan(2 + bytesWritten - macTrailerLength, macTrailerLength).ByteArrayToHex());
            }
        }