public static void WriteTwoCertsNoKeys_NoEncryption() { Pkcs12SafeContents contents = new Pkcs12SafeContents(); byte[] rawData1; byte[] rawData2; using (X509Certificate2 cert1 = Certificates.RSAKeyTransferCapi1.GetCertificate()) using (X509Certificate2 cert2 = Certificates.RSAKeyTransfer2.GetCertificate()) { // Windows seems to treat these as a stack. (LIFO) contents.AddCertificate(cert2); contents.AddCertificate(cert1); rawData1 = cert1.RawData; rawData2 = cert2.RawData; } Pkcs12Builder builder = new Pkcs12Builder(); builder.AddSafeContentsUnencrypted(contents); const string password = nameof(WriteOneCertNoKeys_NoEncryption); builder.SealWithMac(password, HashAlgorithmName.SHA1, 1024); byte[] pfx = builder.Encode(); ImportedCollection coll = ImportedCollection.Import(pfx, password, X509KeyStorageFlags.EphemeralKeySet); using (coll) { Assert.Equal(2, coll.Collection.Count); Assert.Equal(rawData1, coll.Collection[0].RawData); Assert.Equal(rawData2, coll.Collection[1].RawData); Assert.False(coll.Collection[0].HasPrivateKey, "coll.Collection[0].HasPrivateKey"); Assert.False(coll.Collection[1].HasPrivateKey, "coll.Collection[1].HasPrivateKey"); } }
public static void AddNullContents() { Pkcs12Builder builder = new Pkcs12Builder(); AssertExtensions.Throws <ArgumentNullException>( "safeContents", () => builder.AddSafeContentsUnencrypted(null)); AssertExtensions.Throws <ArgumentNullException>( "safeContents", () => builder.AddSafeContentsEncrypted(null, Array.Empty <byte>(), s_pbkdf2Parameters)); AssertExtensions.Throws <ArgumentNullException>( "safeContents", () => builder.AddSafeContentsEncrypted(null, ReadOnlySpan <byte> .Empty, s_pbkdf2Parameters)); AssertExtensions.Throws <ArgumentNullException>( "safeContents", () => builder.AddSafeContentsEncrypted(null, string.Empty, s_pbkdf2Parameters)); AssertExtensions.Throws <ArgumentNullException>( "safeContents", () => builder.AddSafeContentsEncrypted(null, ReadOnlySpan <char> .Empty, s_pbkdf2Parameters)); }
public void CertAndKeyTwice_KeysUntagged() { string pw = nameof(CertAndKeyTwice); using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, s_exportableImportFlags)) using (RSA key = cert.GetRSAPrivateKey()) { Pkcs12Builder builder = new Pkcs12Builder(); Pkcs12SafeContents keyContents = new Pkcs12SafeContents(); Pkcs12SafeContents certContents = new Pkcs12SafeContents(); Pkcs12SafeBag key1 = keyContents.AddShroudedKey(key, pw, s_windowsPbe); Pkcs12SafeBag key2 = keyContents.AddShroudedKey(key, pw, s_windowsPbe); Pkcs12SafeBag cert1 = certContents.AddCertificate(cert); Pkcs12SafeBag cert2 = certContents.AddCertificate(cert); Pkcs9LocalKeyId id2 = new Pkcs9LocalKeyId(cert.GetCertHash()); Pkcs9LocalKeyId id3 = new Pkcs9LocalKeyId(BitConverter.GetBytes(3)); Pkcs9LocalKeyId id4 = new Pkcs9LocalKeyId(BitConverter.GetBytes(4)); cert1.Attributes.Add(s_keyIdOne); cert2.Attributes.Add(id2); key1.Attributes.Add(id3); key2.Attributes.Add(id4); AddContents(keyContents, builder, pw, encrypt: false); AddContents(certContents, builder, pw, encrypt: true); builder.SealWithMac(pw, s_digestAlgorithm, MacCount); byte[] pfxBytes = builder.Encode(); ReadUnreadablePfx( pfxBytes, pw, // NTE_BAD_DATA -2146893819); } }
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 EncryptDecryptMixBytesAndChars(bool encryptBytes, bool withSpan) { Pkcs12SafeContents contents = new Pkcs12SafeContents(); contents.AddSecret(s_zeroOid, s_derNull); string password = nameof(EncryptDecryptMixBytesAndChars); Span <byte> passwordUtf8Bytes = stackalloc byte[password.Length]; Encoding.UTF8.GetBytes(password, passwordUtf8Bytes); Pkcs12Builder builder = new Pkcs12Builder(); if (encryptBytes) { builder.AddSafeContentsEncrypted(contents, passwordUtf8Bytes, s_pbkdf2Parameters); } else { builder.AddSafeContentsEncrypted(contents, password, s_pbkdf2Parameters); } builder.SealWithMac(password, HashAlgorithmName.SHA1, 2048); byte[] encoded = builder.Encode(); Pkcs12Info info = Pkcs12Info.Decode(encoded, out _, skipCopy: true); Assert.True(info.VerifyMac(password)); ReadOnlyCollection <Pkcs12SafeContents> authSafe = info.AuthenticatedSafe; Assert.Equal(1, authSafe.Count); Pkcs12SafeContents readContents = authSafe[0]; Assert.Equal( Pkcs12ConfidentialityMode.Password, readContents.ConfidentialityMode); if (encryptBytes) { if (withSpan) { readContents.Decrypt(password.AsSpan()); } else { readContents.Decrypt(password); } } else { if (withSpan) { readContents.Decrypt(passwordUtf8Bytes); } else { readContents.Decrypt(passwordUtf8Bytes.ToArray()); } } Assert.Equal( Pkcs12ConfidentialityMode.None, readContents.ConfidentialityMode); List <Pkcs12SafeBag> bags = readContents.GetBags().ToList(); Assert.Equal(1, bags.Count); Pkcs12SecretBag secretBag = Assert.IsType <Pkcs12SecretBag>(bags[0]); Assert.Equal(s_zeroOid.Value, secretBag.GetSecretType().Value); Assert.Equal(s_derNull.ByteArrayToHex(), secretBag.SecretValue.ByteArrayToHex()); }
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()); } }
public void TwoCerts_TwoKeys_ManySafeContentsValues(bool invertCertOrder, bool invertKeyOrder) { string pw = nameof(TwoCerts_TwoKeys_ManySafeContentsValues); using (ImportedCollection ic = Cert.Import(TestData.MultiPrivateKeyPfx, null, s_exportableImportFlags)) { X509Certificate2Collection certs = ic.Collection; X509Certificate2 first = certs[0]; X509Certificate2 second = certs[1]; if (invertCertOrder) { X509Certificate2 tmp = first; first = second; second = tmp; } using (AsymmetricAlgorithm firstKey = first.GetRSAPrivateKey()) using (AsymmetricAlgorithm secondKey = second.GetRSAPrivateKey()) { AsymmetricAlgorithm firstAdd = firstKey; AsymmetricAlgorithm secondAdd = secondKey; if (invertKeyOrder != invertCertOrder) { AsymmetricAlgorithm tmp = firstKey; firstAdd = secondAdd; secondAdd = tmp; } Pkcs12Builder builder = new Pkcs12Builder(); Pkcs12SafeContents firstKeyContents = new Pkcs12SafeContents(); Pkcs12SafeContents secondKeyContents = new Pkcs12SafeContents(); Pkcs12SafeContents firstCertContents = new Pkcs12SafeContents(); Pkcs12SafeContents secondCertContents = new Pkcs12SafeContents(); Pkcs12SafeContents irrelevant = new Pkcs12SafeContents(); irrelevant.AddSecret(new Oid("0.0"), new byte[] { 0x05, 0x00 }); Pkcs12SafeBag firstAddedKeyBag = firstKeyContents.AddShroudedKey(firstAdd, pw, s_windowsPbe); Pkcs12SafeBag secondAddedKeyBag = secondKeyContents.AddShroudedKey(secondAdd, pw, s_windowsPbe); Pkcs12SafeBag firstCertBag = firstCertContents.AddCertificate(first); Pkcs12SafeBag secondCertBag = secondCertContents.AddCertificate(second); Pkcs12SafeBag firstKeyBag = firstAddedKeyBag; Pkcs12SafeBag secondKeyBag = secondAddedKeyBag; if (invertKeyOrder != invertCertOrder) { Pkcs12SafeBag tmp = firstKeyBag; firstKeyBag = secondKeyBag; secondKeyBag = tmp; } firstCertBag.Attributes.Add(s_keyIdOne); firstKeyBag.Attributes.Add(s_keyIdOne); Pkcs9LocalKeyId secondKeyId = new Pkcs9LocalKeyId(second.GetCertHash()); secondCertBag.Attributes.Add(secondKeyId); secondKeyBag.Attributes.Add(secondKeyId); // 2C, 1K, 1C, 2K // With some non-participating contents values sprinkled in for good measure. AddContents(irrelevant, builder, pw, encrypt: true); AddContents(secondCertContents, builder, pw, encrypt: true); AddContents(irrelevant, builder, pw, encrypt: false); AddContents(firstKeyContents, builder, pw, encrypt: false); AddContents(firstCertContents, builder, pw, encrypt: true); AddContents(irrelevant, builder, pw, encrypt: false); AddContents(secondKeyContents, builder, pw, encrypt: true); AddContents(irrelevant, builder, pw, encrypt: true); builder.SealWithMac(pw, s_digestAlgorithm, MacCount); byte[] pfxBytes = builder.Encode(); X509Certificate2[] expectedOrder = { first, second }; Action <X509Certificate2> followup = CheckKeyConsistency; // For unknown reasons, CheckKeyConsistency on this test fails // on Windows 7 with an Access Denied in all variations for // Collections, and in invertCertOrder: true for Single. // // Obviously this hit some sort of weird corner case in the Win7 // loader, but it's not important to the test. if (OperatingSystem.IsWindows() && !PlatformDetection.IsWindows8xOrLater) { followup = null; } ReadMultiPfx( pfxBytes, pw, first, expectedOrder, followup); } } }
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); } }