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()); }
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)); }
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); }
public void EmptyPfx_NoMac() { Pkcs12Builder builder = new Pkcs12Builder(); builder.SealWithoutIntegrity(); ReadEmptyPfx(builder.Encode(), correctPassword: null); }
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)); }
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"); } }
public void EmptyPfx_NoMac_ArbitraryPassword() { Pkcs12Builder builder = new Pkcs12Builder(); builder.SealWithoutIntegrity(); byte[] pfxBytes = builder.Encode(); ReadEmptyPfx(pfxBytes, "arbitrary password"); ReadEmptyPfx(pfxBytes, "other arbitrary password"); }
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)); }
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); } }
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); } }
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); } }
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()); }
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); } } } }
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()); }
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])); }
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); } }
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); } }
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()); } }