From 85d2991d027597b9b5c5e5af5d866869cea3d1fb Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Mon, 13 May 2024 12:19:35 -0700 Subject: [PATCH 01/19] Create new X509CertificateLoader The new certificate loader only loads one data type per method, unlike the previous loader mechanism (new X509Certiicate2(bytes, ...)). It also allows for caller configuration to control cost-of-work limits and some common usability gotchas around Windows PFX loading. This change adds the new loader, and changes the X509Certificate2 ctors to use it; a followup will mark the ctors as Obsolete and update usage in the dotnet/runtime codebase. --- .../Crypt32/Interop.CryptQueryObject.cs | 15 + .../Crypt32/Interop.PFXImportCertStore.cs | 3 + .../MemoryMappedFileMemoryManager.cs | 113 ++ .../Cryptography/Asn1/Pkcs12/PfxAsn.manual.cs | 258 ---- .../System/Security/Cryptography/Helpers.cs | 30 + .../Cryptography/IncrementalHash.netfx.cs | 20 + .../Security/Cryptography/KdfWorkLimiter.cs | 86 -- .../Cryptography/KeyFormatHelper.Encrypted.cs | 3 +- .../Cryptography/PasswordBasedEncryption.cs | 66 +- .../System/Security/Cryptography/Pkcs12Kdf.cs | 1 - .../Pkcs12LoadLimitExceededException.cs | 24 + .../X509Certificates/Pkcs12LoaderLimits.cs | 373 +++++ .../X509CertificateLoader.Pkcs12.cs | 944 +++++++++++++ .../X509Certificates/X509CertificateLoader.cs | 742 ++++++++++ .../X509IterationCountExceededException.cs | 9 - ...cateLoaderPkcs12Tests.WindowsAttributes.cs | 218 +++ .../X509CertificateLoaderPkcs12Tests.cs | 716 ++++++++++ .../X509CertificateLoaderTests.cs | 284 ++++ .../Microsoft.Bcl.Cryptography.Forwards.cs | 7 + .../ref/Microsoft.Bcl.Cryptography.cs | 48 + .../ref/Microsoft.Bcl.Cryptography.csproj | 7 +- .../src/Microsoft.Bcl.Cryptography.csproj | 237 +++- .../Win32/SafeHandles/SafePasswordHandle.cs | 96 ++ .../src/Resources/Strings.resx | 45 + .../System/Security/Cryptography/Helpers.cs | 53 + .../Security/Cryptography/NetStandardShims.cs | 142 ++ .../PbeEncryptionAlgorithm.netstandard.cs | 16 + .../Cryptography/PbeParameters.netstandard.cs | 35 + .../X509CertificateLoader.ProcessedPkcs12.cs | 132 ++ .../X509CertificateLoader.netfx.cs | 105 ++ .../X509CertificateLoader.netstandard.cs | 53 + .../Microsoft.Bcl.Cryptography.Tests.csproj | 17 + .../tests/X509Certificates/TestData.cs | 1222 +++++++++++++++++ .../tests/X509Certificates/TestFiles.cs | 43 + .../System.Security.Cryptography.Pkcs.csproj | 5 +- .../ref/System.Security.Cryptography.cs | 39 + .../src/Resources/Strings.resx | 6 + .../src/System.Security.Cryptography.csproj | 34 +- .../X509Certificates/AndroidCertificatePal.cs | 39 +- .../X509Certificates/AndroidPkcs12Reader.cs | 93 -- .../AppleCertificatePal.ImportExport.iOS.cs | 20 +- .../AppleCertificatePal.ImportExport.macOS.cs | 26 +- .../AppleCertificatePal.Keys.iOS.cs | 6 +- .../AppleCertificatePal.Pkcs12.iOS.cs | 21 +- .../AppleCertificatePal.Pkcs12.macOS.cs | 47 - .../ApplePkcs12CertLoader.iOS.cs | 41 - .../X509Certificates/ApplePkcs12Reader.iOS.cs | 68 - .../ApplePkcs12Reader.macOS.cs | 111 -- .../CertificatePal.Windows.Import.cs | 157 +-- .../CertificatePal.Windows.cs | 2 +- .../LocalAppContextSwitches.cs | 2 +- .../X509Certificates/OpenSslPkcs12Reader.cs | 107 -- .../OpenSslPkcsFormatReader.cs | 114 -- .../OpenSslX509CertificateReader.cs | 63 +- .../X509Certificates/OpenSslX509Encoder.cs | 19 +- .../X509Certificates/StorePal.Android.cs | 50 +- .../X509Certificates/StorePal.OpenSsl.cs | 59 +- .../StorePal.Windows.Import.cs | 81 +- .../Cryptography/X509Certificates/StorePal.cs | 32 + .../X509Certificates/StorePal.iOS.cs | 20 +- .../StorePal.macOS.LoaderPal.cs | 76 - .../X509Certificates/StorePal.macOS.cs | 50 +- .../X509Certificates/UnixPkcs12Reader.cs | 857 ------------ .../X509Certificate.LegacyLimits.cs | 55 + .../X509Certificates/X509Certificate.cs | 123 +- .../X509CertificateLoader.Android.cs | 111 ++ .../X509CertificateLoader.NotSupported.cs | 34 + .../X509CertificateLoader.OpenSsl.cs | 96 ++ .../X509CertificateLoader.Unix.cs | 604 ++++++++ .../X509CertificateLoader.Windows.cs | 336 +++++ .../X509CertificateLoader.iOS.cs | 123 ++ .../X509CertificateLoader.macOS.cs | 230 ++++ .../X509CertificateLoader.netcore.cs | 146 ++ .../X509Certificates/X509Pal.Android.cs | 2 +- .../System.Security.Cryptography.Tests.csproj | 9 + .../tests/X509Certificates/CertLoaderTests.cs | 139 ++ .../X509Certificates/CollectionImportTests.cs | 62 + .../tests/X509Certificates/CtorTests.cs | 45 +- .../X509Certificates/LoadFromFileTests.cs | 19 + .../tests/X509Certificates/PfxFormatTests.cs | 139 +- .../PfxFormatTests_Collection.cs | 82 +- .../PfxFormatTests_SingleCert.cs | 64 +- ...ionCountTests.CustomAppContextDataLimit.cs | 14 +- .../tests/X509Certificates/PfxTests.cs | 31 - .../tests/X509Certificates/TestData.cs | 254 ++++ 85 files changed, 8643 insertions(+), 2483 deletions(-) create mode 100644 src/libraries/Common/src/System/IO/MemoryMappedFiles/MemoryMappedFileMemoryManager.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/KdfWorkLimiter.cs create mode 100644 src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoadLimitExceededException.cs create mode 100644 src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs create mode 100644 src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs create mode 100644 src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/X509IterationCountExceededException.cs create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs create mode 100644 src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs create mode 100644 src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/Helpers.cs create mode 100644 src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeEncryptionAlgorithm.netstandard.cs create mode 100644 src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeParameters.netstandard.cs create mode 100644 src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.ProcessedPkcs12.cs create mode 100644 src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netfx.cs create mode 100644 src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netstandard.cs create mode 100644 src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestData.cs create mode 100644 src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestFiles.cs delete mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidPkcs12Reader.cs delete mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12CertLoader.iOS.cs delete mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12Reader.iOS.cs delete mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12Reader.macOS.cs delete mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslPkcs12Reader.cs delete mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/UnixPkcs12Reader.cs create mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.LegacyLimits.cs create mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Android.cs create mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.NotSupported.cs create mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs create mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Unix.cs create mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Windows.cs create mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs create mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.macOS.cs create mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netcore.cs create mode 100644 src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs diff --git a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptQueryObject.cs b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptQueryObject.cs index f7cf15985b686b..c170c89d8e0544 100644 --- a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptQueryObject.cs +++ b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptQueryObject.cs @@ -56,5 +56,20 @@ internal static unsafe partial bool CryptQueryObject( IntPtr phMsg, IntPtr ppvContext ); + + [LibraryImport(Libraries.Crypt32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static unsafe partial bool CryptQueryObject( + CertQueryObjectType dwObjectType, + void* pvObject, + ExpectedContentTypeFlags dwExpectedContentTypeFlags, + ExpectedFormatTypeFlags dwExpectedFormatTypeFlags, + int dwFlags, // reserved - always pass 0 + IntPtr pdwMsgAndCertEncodingType, + out ContentType pdwContentType, + IntPtr pdwFormatType, + IntPtr phCertStore, + IntPtr phMsg, + out SafeCertContextHandle ppvContext); } } diff --git a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.PFXImportCertStore.cs b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.PFXImportCertStore.cs index 7df9fb7e1474f0..dcc641c64f9ed1 100644 --- a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.PFXImportCertStore.cs +++ b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.PFXImportCertStore.cs @@ -10,5 +10,8 @@ internal static partial class Crypt32 { [LibraryImport(Libraries.Crypt32, SetLastError = true)] internal static partial SafeCertStoreHandle PFXImportCertStore(ref DATA_BLOB pPFX, SafePasswordHandle password, PfxCertStoreFlags dwFlags); + + [LibraryImport(Libraries.Crypt32, SetLastError = true)] + internal static unsafe partial SafeCertStoreHandle PFXImportCertStore(ref DATA_BLOB pPFX, char* password, PfxCertStoreFlags dwFlags); } } diff --git a/src/libraries/Common/src/System/IO/MemoryMappedFiles/MemoryMappedFileMemoryManager.cs b/src/libraries/Common/src/System/IO/MemoryMappedFiles/MemoryMappedFileMemoryManager.cs new file mode 100644 index 00000000000000..f30ea062aee425 --- /dev/null +++ b/src/libraries/Common/src/System/IO/MemoryMappedFiles/MemoryMappedFileMemoryManager.cs @@ -0,0 +1,113 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; + +namespace System.IO.MemoryMappedFiles +{ + internal sealed unsafe class MemoryMappedFileMemoryManager : MemoryManager + { + private byte* _pointer; + private int _length; + private MemoryMappedFile _mappedFile; + private MemoryMappedViewAccessor _accessor; + + public MemoryMappedFileMemoryManager( + byte* pointer, + int length, + MemoryMappedFile mappedFile, + MemoryMappedViewAccessor accessor) + { + _pointer = pointer; + _length = length; + _mappedFile = mappedFile; + _accessor = accessor; + } + +#if DEBUG +#pragma warning disable CA2015 + ~MemoryMappedFileMemoryManager() +#pragma warning restore CA2015 + { + Environment.FailFast("MemoryMappedFileMemoryManager was finalized."); + } +#endif + + internal static MemoryMappedFileMemoryManager CreateFromFileClamped( + FileStream fileStream, + MemoryMappedFileAccess access = MemoryMappedFileAccess.Read, + HandleInheritability inheritability = HandleInheritability.None, + bool leaveOpen = false) + { + int length = (int)Math.Min(int.MaxValue, fileStream.Length); + MemoryMappedFile mapped = MemoryMappedFile.CreateFromFile(fileStream, null, 0, access, inheritability, leaveOpen); + MemoryMappedViewAccessor? accessor = null; + byte* pointer = null; + + try + { + accessor = mapped.CreateViewAccessor(0, length, access); + accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer); + + return new MemoryMappedFileMemoryManager(pointer, length, mapped, accessor); + } + catch (Exception) + { + if (pointer != null) + { + accessor!.SafeMemoryMappedViewHandle.ReleasePointer(); + } + + accessor?.Dispose(); + mapped.Dispose(); + throw; + } + } + + protected override void Dispose(bool disposing) + { + _pointer = null; + _length = -1; + _accessor?.SafeMemoryMappedViewHandle.ReleasePointer(); + _accessor?.Dispose(); + _mappedFile.Dispose(); + _accessor = null!; + _mappedFile = null!; + + if (disposing) + { + GC.SuppressFinalize(this); + } + } + + public override Span GetSpan() + { + ThrowIfDisposed(); + return new Span(_pointer, _length); + } + + public override MemoryHandle Pin(int elementIndex = 0) + { + ThrowIfDisposed(); + return default; + } + + public override void Unpin() + { + ThrowIfDisposed(); + // nop + } + + private void ThrowIfDisposed() + { +#if NETCOREAPP + ObjectDisposedException.ThrowIf(_length < 0, this); +#else + if (_length < 0) + { + throw new ObjectDisposedException(GetType().FullName); + } +#endif + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.manual.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.manual.cs index 38e60abcebc640..67ee5593e6586b 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.manual.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/PfxAsn.manual.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Formats.Asn1; -using System.Security.Cryptography.Asn1.Pkcs7; using System.Security.Cryptography.Pkcs; -using System.Security.Cryptography.X509Certificates; -using Internal.Cryptography; #if BUILDING_PKCS using Helpers = Internal.Cryptography.PkcsHelpers; @@ -16,10 +12,6 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 { internal partial struct PfxAsn { - private const int MaxIterationWork = 300_000; - private static ReadOnlySpan EmptyPassword => ""; // don't use ReadOnlySpan.Empty because it will get confused with default. - private static ReadOnlySpan NullPassword => default; - internal bool VerifyMac( ReadOnlySpan macPassword, ReadOnlySpan authSafeContents) @@ -96,255 +88,5 @@ internal bool VerifyMac( MacData.Value.Mac.Digest.Span); } } - - internal ulong CountTotalIterations() - { - checked - { - ulong count = 0; - - // RFC 7292 section 4.1: - // the contentType field of authSafe shall be of type data - // or signedData. The content field of the authSafe shall, either - // directly (data case) or indirectly (signedData case), contain a BER- - // encoded value of type AuthenticatedSafe. - // We don't support authSafe that is signedData, so enforce that it's just data. - if (AuthSafe.ContentType != Oids.Pkcs7Data) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - ReadOnlyMemory authSafeContents = Helpers.DecodeOctetStringAsMemory(AuthSafe.Content); - AsnValueReader outerAuthSafe = new AsnValueReader(authSafeContents.Span, AsnEncodingRules.BER); // RFC 7292 PDU says BER - AsnValueReader authSafeReader = outerAuthSafe.ReadSequence(); - outerAuthSafe.ThrowIfNotEmpty(); - - bool hasSeenEncryptedInfo = false; - - while (authSafeReader.HasData) - { - ContentInfoAsn.Decode(ref authSafeReader, authSafeContents, out ContentInfoAsn contentInfo); - - ReadOnlyMemory contentData; - ArraySegment? rentedData = null; - - try - { - if (contentInfo.ContentType != Oids.Pkcs7Data) - { - if (contentInfo.ContentType == Oids.Pkcs7Encrypted) - { - if (hasSeenEncryptedInfo) - { - // We will process at most one encryptedData ContentInfo. This is the most typical scenario where - // certificates are stored in an encryptedData ContentInfo, and keys are shrouded in a data ContentInfo. - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - ArraySegment content = DecryptContentInfo(contentInfo, out uint iterations); - contentData = content; - rentedData = content; - hasSeenEncryptedInfo = true; - count += iterations; - } - else - { - // Not a common scenario. It's not data or encryptedData, so they need to go through the - // regular PKCS12 loader. - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } - else - { - contentData = Helpers.DecodeOctetStringAsMemory(contentInfo.Content); - } - - AsnValueReader outerSafeBag = new AsnValueReader(contentData.Span, AsnEncodingRules.BER); - AsnValueReader safeBagReader = outerSafeBag.ReadSequence(); - outerSafeBag.ThrowIfNotEmpty(); - - while (safeBagReader.HasData) - { - SafeBagAsn.Decode(ref safeBagReader, contentData, out SafeBagAsn bag); - - // We only need to count iterations on PKCS8ShroudedKeyBag. - // * KeyBag is PKCS#8 PrivateKeyInfo and doesn't do iterations. - // * CertBag, either for x509Certificate or sdsiCertificate don't do iterations. - // * CRLBag doesn't do iterations. - // * SecretBag doesn't do iteations. - // * Nested SafeContents _can_ do iterations, but Windows ignores it. So we will ignore it too. - if (bag.BagId == Oids.Pkcs12ShroudedKeyBag) - { - AsnValueReader pkcs8ShroudedKeyReader = new AsnValueReader(bag.BagValue.Span, AsnEncodingRules.BER); - EncryptedPrivateKeyInfoAsn.Decode( - ref pkcs8ShroudedKeyReader, - bag.BagValue, - out EncryptedPrivateKeyInfoAsn epki); - - count += IterationsFromParameters(epki.EncryptionAlgorithm); - } - } - } - finally - { - if (rentedData.HasValue) - { - CryptoPool.Return(rentedData.Value); - } - } - } - - if (MacData.HasValue) - { - if (MacData.Value.IterationCount < 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - count += (uint)MacData.Value.IterationCount; - } - - return count; - } - } - - private static ArraySegment DecryptContentInfo(ContentInfoAsn contentInfo, out uint iterations) - { - EncryptedDataAsn encryptedData = EncryptedDataAsn.Decode(contentInfo.Content, AsnEncodingRules.BER); - - if (encryptedData.Version != 0 && encryptedData.Version != 2) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - // The encrypted contentInfo can only wrap a PKCS7 data. - if (encryptedData.EncryptedContentInfo.ContentType != Oids.Pkcs7Data) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (!encryptedData.EncryptedContentInfo.EncryptedContent.HasValue) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - iterations = IterationsFromParameters(encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm); - - // This encryptData is encrypted with more rounds than we are willing to process. Bail out of the whole thing. - if (iterations > MaxIterationWork) - { - throw new X509IterationCountExceededException(); - } - - int encryptedValueLength = encryptedData.EncryptedContentInfo.EncryptedContent.Value.Length; - byte[] destination = CryptoPool.Rent(encryptedValueLength); - int written = 0; - - try - { - try - { - written = PasswordBasedEncryption.Decrypt( - in encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm, - EmptyPassword, - default, - encryptedData.EncryptedContentInfo.EncryptedContent.Value.Span, - destination); - - // When padding happens to be as expected (false-positive), we can detect gibberish and prevent unexpected failures later - // This extra check makes it so it's very unlikely we'll end up with false positive. - AsnValueReader outerSafeBag = new AsnValueReader(destination.AsSpan(0, written), AsnEncodingRules.BER); - AsnValueReader safeBagReader = outerSafeBag.ReadSequence(); - outerSafeBag.ThrowIfNotEmpty(); - } - catch - { - // If empty password didn't work, try null password. - written = PasswordBasedEncryption.Decrypt( - in encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm, - NullPassword, - default, - encryptedData.EncryptedContentInfo.EncryptedContent.Value.Span, - destination); - - AsnValueReader outerSafeBag = new AsnValueReader(destination.AsSpan(0, written), AsnEncodingRules.BER); - AsnValueReader safeBagReader = outerSafeBag.ReadSequence(); - outerSafeBag.ThrowIfNotEmpty(); - } - } - finally - { - if (written == 0) - { - // This means the decryption operation failed and destination could contain - // partial data. Clear it to be hygienic. - CryptographicOperations.ZeroMemory(destination); - } - } - - return new ArraySegment(destination, 0, written); - } - - private static uint IterationsFromParameters(in AlgorithmIdentifierAsn algorithmIdentifier) - { - switch (algorithmIdentifier.Algorithm) - { - case Oids.PasswordBasedEncryptionScheme2: - if (!algorithmIdentifier.Parameters.HasValue) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - PBES2Params pbes2Params = PBES2Params.Decode(algorithmIdentifier.Parameters.Value, AsnEncodingRules.BER); - - // PBES2 only defines PKBDF2 for now. See RFC 8018 A.4 - if (pbes2Params.KeyDerivationFunc.Algorithm != Oids.Pbkdf2) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (!pbes2Params.KeyDerivationFunc.Parameters.HasValue) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - Pbkdf2Params pbkdf2Params = Pbkdf2Params.Decode(pbes2Params.KeyDerivationFunc.Parameters.Value, AsnEncodingRules.BER); - - if (pbkdf2Params.IterationCount < 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return (uint)pbkdf2Params.IterationCount; - - // PBES1 - case Oids.PbeWithMD5AndDESCBC: - case Oids.PbeWithMD5AndRC2CBC: - case Oids.PbeWithSha1AndDESCBC: - case Oids.PbeWithSha1AndRC2CBC: - case Oids.Pkcs12PbeWithShaAnd3Key3Des: - case Oids.Pkcs12PbeWithShaAnd2Key3Des: - case Oids.Pkcs12PbeWithShaAnd128BitRC2: - case Oids.Pkcs12PbeWithShaAnd40BitRC2: - if (!algorithmIdentifier.Parameters.HasValue) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - PBEParameter pbeParameters = PBEParameter.Decode( - algorithmIdentifier.Parameters.Value, - AsnEncodingRules.BER); - - if (pbeParameters.IterationCount < 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return (uint)pbeParameters.IterationCount; - - default: - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Helpers.cs b/src/libraries/Common/src/System/Security/Cryptography/Helpers.cs index 462e4d26a8f64d..9473047a0ce84f 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Helpers.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Helpers.cs @@ -3,6 +3,8 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Cryptography; @@ -10,6 +12,10 @@ namespace Internal.Cryptography { internal static partial class Helpers { +#if NETFRAMEWORK || (NETSTANDARD && !NETSTANDARD2_1_OR_GREATER) + private static readonly RandomNumberGenerator s_rng = RandomNumberGenerator.Create(); +#endif + [UnsupportedOSPlatformGuard("browser")] internal static bool HasSymmetricEncryption { get; } = #if NET @@ -53,6 +59,30 @@ internal static partial class Helpers }; } + internal static bool ContainsNull(this ReadOnlySpan span) + { + return Unsafe.IsNullRef(ref MemoryMarshal.GetReference(span)); + } + +#if NETFRAMEWORK || (NETSTANDARD && !NETSTANDARD2_1_OR_GREATER) + internal static void RngFill(byte[] destination) + { + s_rng.GetBytes(destination); + } +#endif + + internal static void RngFill(Span destination) + { +#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER + RandomNumberGenerator.Fill(destination); +#else + byte[] temp = CryptoPool.Rent(destination.Length); + s_rng.GetBytes(temp, 0, destination.Length); + temp.AsSpan(0, destination.Length).CopyTo(destination); + CryptoPool.Return(temp, destination.Length); +#endif + } + internal static bool TryCopyToDestination(this ReadOnlySpan source, Span destination, out int bytesWritten) { if (source.TryCopyTo(destination)) diff --git a/src/libraries/Common/src/System/Security/Cryptography/IncrementalHash.netfx.cs b/src/libraries/Common/src/System/Security/Cryptography/IncrementalHash.netfx.cs index b60bac3f6ede6e..7f78979af07357 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/IncrementalHash.netfx.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/IncrementalHash.netfx.cs @@ -123,6 +123,26 @@ public byte[] GetHashAndReset() return hashValue; } + internal bool TryGetHashAndReset( + Span destination, + out int bytesWritten) + { + if (destination.Length < _hash.HashSize / 8) + { + bytesWritten = 0; + return false; + } + + _hash.TransformFinalBlock(Array.Empty(), 0, 0); + byte[] actual = _hash.Hash; + _hash.Initialize(); + + Debug.Assert(actual.Length * 8 == _hash.HashSize); + actual.AsSpan().CopyTo(destination); + bytesWritten = actual.Length; + return true; + } + /// /// Release all resources used by the current instance of the /// class. diff --git a/src/libraries/Common/src/System/Security/Cryptography/KdfWorkLimiter.cs b/src/libraries/Common/src/System/Security/Cryptography/KdfWorkLimiter.cs deleted file mode 100644 index 7500212fe27d9c..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/KdfWorkLimiter.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; - -namespace System.Security.Cryptography -{ - // Places KDF work limits on the current thread. - internal static class KdfWorkLimiter - { - [ThreadStatic] - private static State? t_state; - - // Entry point: sets the iteration limit to a new value. - internal static void SetIterationLimit(ulong workLimit) - { - Debug.Assert(t_state == null, "This method is not intended to be called recursively."); - State state = new State(); - state.RemainingAllowedWork = workLimit; - t_state = state; - } - - internal static bool WasWorkLimitExceeded() - { - Debug.Assert(t_state != null, "This method should only be called within a protected block."); - return t_state.WorkLimitWasExceeded; - } - - // Removes any iteration limit on the current thread. - internal static void ResetIterationLimit() - { - t_state = null; - } - - // Records that we're about to perform some amount of work. - // Overflows if the work count is exceeded. - internal static void RecordIterations(int workCount) - { - RecordIterations((long)workCount); - } - - // Records that we're about to perform some amount of work. - // Overflows if the work count is exceeded. - internal static void RecordIterations(long workCount) - { - State? state = t_state; - if (state == null) - { - return; - } - - bool success = false; - - if (workCount < 0) - { - throw new CryptographicException(); - } - - try - { - if (!state.WorkLimitWasExceeded) - { - state.RemainingAllowedWork = checked(state.RemainingAllowedWork - (ulong)workCount); - success = true; - } - } - finally - { - // If for any reason we failed, mark the thread as "no further work allowed" and - // normalize to CryptographicException. - if (!success) - { - state.RemainingAllowedWork = 0; - state.WorkLimitWasExceeded = true; - throw new CryptographicException(); - } - } - } - - private sealed class State - { - internal ulong RemainingAllowedWork; - internal bool WorkLimitWasExceeded; - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.Encrypted.cs b/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.Encrypted.cs index 8e96369bca9178..24b238e502b1f3 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.Encrypted.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.Encrypted.cs @@ -6,6 +6,7 @@ using System.Formats.Asn1; using System.Runtime.InteropServices; using System.Security.Cryptography.Asn1; +using Internal.Cryptography; namespace System.Security.Cryptography { @@ -202,7 +203,7 @@ private static AsnWriter WriteEncryptedPkcs8( try { - RandomNumberGenerator.Fill(salt); + Helpers.RngFill(salt); int written = PasswordBasedEncryption.Encrypt( password, diff --git a/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs b/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs index 4bf056ffd5f4f9..9193bfe4146ff5 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs @@ -393,12 +393,7 @@ internal static unsafe int Encrypt( Debug.Assert(pwdTmpBytes!.Length == 0); } - KdfWorkLimiter.RecordIterations(iterationCount); - using (var pbkdf2 = new Rfc2898DeriveBytes(pwdTmpBytes, salt.ToArray(), iterationCount, prf)) - { - derivedKey = pbkdf2.GetBytes(keySizeBytes); - } - + derivedKey = DeriveKey(pwdTmpBytes, salt, iterationCount, prf, keySizeBytes); iv.CopyTo(ivDest); } @@ -786,7 +781,7 @@ private static unsafe Rfc2898DeriveBytes OpenPbkdf2( { requestedKeyLength = pbkdf2Params.KeyLength; - return new Rfc2898DeriveBytes( + return OpenPbkdf2( tmpPassword, tmpSalt, iterationCount, @@ -1113,5 +1108,62 @@ private static RC2 CreateRC2() return RC2.Create(); } + + private static byte[] DeriveKey( + byte[] password, + ReadOnlySpan salt, + int iterationCount, + HashAlgorithmName prf, + int outputLength) + { +#if NETCOREAPP + return Rfc2898DeriveBytes.Pbkdf2(password, salt, iterationCount, prf, outputLength); +#else + using (Rfc2898DeriveBytes pbkdf2 = OpenPbkdf2(password, salt.ToArray(), iterationCount, prf)) + { + return pbkdf2.GetBytes(outputLength); + } +#endif + } + + private static Rfc2898DeriveBytes OpenPbkdf2( + byte[] password, + byte[] salt, + int iterationCount, + HashAlgorithmName prf) + { +#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER || NET472_OR_GREATER +#pragma warning disable CA5379 + return new Rfc2898DeriveBytes( + password, + salt, + iterationCount, + prf); +#pragma warning restore CA5379 +#else + if (prf == HashAlgorithmName.SHA1) + { +#pragma warning disable CA5379 + return new Rfc2898DeriveBytes(password, salt, iterationCount); +#pragma warning restore CA5379 + } + + try + { + return (Rfc2898DeriveBytes)Activator.CreateInstance( + typeof(Rfc2898DeriveBytes), + password, + salt, + iterationCount, + prf); + } + catch (MissingMethodException e) + { + throw new CryptographicException( + SR.Format(SR.Cryptography_UnknownHashAlgorithm, prf.Name), + e); + } +#endif + } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Pkcs12Kdf.cs b/src/libraries/Common/src/System/Security/Cryptography/Pkcs12Kdf.cs index 1fa1d0ee033914..8264a6586589eb 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Pkcs12Kdf.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Pkcs12Kdf.cs @@ -155,7 +155,6 @@ private static void Derive( I = IRented.AsSpan(0, ILen); } - KdfWorkLimiter.RecordIterations(iterationCount); IncrementalHash hash = IncrementalHash.CreateHash(hashAlgorithm); try diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoadLimitExceededException.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoadLimitExceededException.cs new file mode 100644 index 00000000000000..2edafaeb7db12c --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoadLimitExceededException.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Security.Cryptography.X509Certificates +{ + /// + /// The exception that is thrown when importing a PKCS#12/PFX has failed + /// due to violating a specified limit. + /// + public sealed class Pkcs12LoadLimitExceededException : CryptographicException + { + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// The name of the property representing the limit that was exceeded. + /// + public Pkcs12LoadLimitExceededException(string propertyName) + : base(SR.Format(SR.Cryptography_X509_PKCS12_LimitExceeded, propertyName)) + { + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs new file mode 100644 index 00000000000000..0ac16d4f097310 --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs @@ -0,0 +1,373 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Security.Cryptography.X509Certificates +{ + /// + /// Represents a set of constraints to apply when loading PKCS#12/PFX contents. + /// + public sealed class Pkcs12LoaderLimits + { + private bool _isReadOnly; + private int? _macIterationLimit = 300_000; + private int? _individualKdfIterationLimit = 300_000; + private int? _totalKdfIterationLimit = 1_000_000; + private int? _maxKeys = 200; + private int? _maxCertificates = 200; + private bool _preserveStorageProvider; + private bool _preserveKeyName; + private bool _preserveCertificateAlias; + private bool _preserveUnknownAttributes; + private bool _ignorePrivateKeys; + private bool _ignoreEncryptedAuthSafes; + + /// + /// Gets a shared reference to the default loader limits. + /// + /// + /// The singleton instance returned from this property is equivalent to an + /// instance produced via the default constructor, except the properties + /// prohibit reassignment. As with the default constructor, the individual + /// property values may change over time. + /// + /// A shared reference to the default loader limits. + /// + public static Pkcs12LoaderLimits Defaults { get; } = MakeReadOnly(new Pkcs12LoaderLimits()); + + /// + /// Gets a shared reference to loader limits that indicate no + /// filtering or restrictions of the contents should be applied + /// before sending them to the underlying system loader. + /// + /// + /// A shared reference to loader limits that indicate no + /// filtering or restrictions of the contents should be applied + /// before sending them to the underlying system loader. + /// + /// + /// + /// The system loader may have its own limits where only part + /// of the contents are respected, or where the load is rejected. + /// Using this set of limits only affects the .NET layer of filtering. + /// + /// + /// The class checks for reference + /// equality to this property to determine if filtering should be bypassed. + /// Making a new Pkcs12LoaderLimits value that has all of the same property + /// values may give different results for certain inputs. + /// + /// + public static Pkcs12LoaderLimits DangerousNoLimits { get; } = + MakeReadOnly( + new Pkcs12LoaderLimits + { + MacIterationLimit = null, + IndividualKdfIterationLimit = null, + TotalKdfIterationLimit = null, + MaxKeys = null, + MaxCertificates = null, + PreserveStorageProvider = true, + PreserveKeyName = true, + PreserveCertificateAlias = true, + PreserveUnknownAttributes = true, + }); + + /// + /// Initializes a new instance of the class + /// with default values. + /// + /// + /// The default values for each property on a default instance of this class + /// are chosen as a compromise between maximizing compatibility and minimizing + /// "nuisance" work. The defaults for any given property may vary over time. + /// + public Pkcs12LoaderLimits() + { + } + + /// + /// Initializes a new instance of the class + /// by copying the values from another instance. + /// + /// The instance to copy the values from. + /// + /// is . + /// + public Pkcs12LoaderLimits(Pkcs12LoaderLimits copyFrom) + { +#pragma warning disable CA1510 + if (copyFrom is null) + throw new ArgumentNullException(nameof(copyFrom)); +#pragma warning restore CA1510 + + // Do not copy _isReadOnly. + + _macIterationLimit = copyFrom._macIterationLimit; + _individualKdfIterationLimit = copyFrom._individualKdfIterationLimit; + _totalKdfIterationLimit = copyFrom._totalKdfIterationLimit; + _maxKeys = copyFrom._maxKeys; + _maxCertificates = copyFrom._maxCertificates; + _preserveStorageProvider = copyFrom._preserveStorageProvider; + _preserveKeyName = copyFrom._preserveKeyName; + _preserveCertificateAlias = copyFrom._preserveCertificateAlias; + _preserveUnknownAttributes = copyFrom._preserveUnknownAttributes; + _ignorePrivateKeys = copyFrom._ignorePrivateKeys; + _ignoreEncryptedAuthSafes = copyFrom._ignoreEncryptedAuthSafes; + } + + /// + /// Gets a value indicating whether the instance is read-only. + /// + /// + /// if the instance is read-only; otherwise, . + /// + public bool IsReadOnly => _isReadOnly; + + /// + /// Makes the instance read-only. + /// + public void MakeReadOnly() + { + _isReadOnly = true; + } + + private static Pkcs12LoaderLimits MakeReadOnly(Pkcs12LoaderLimits limits) + { + limits.MakeReadOnly(); + return limits; + } + + private void CheckReadOnly() + { + if (_isReadOnly) + { + throw new InvalidOperationException(SR.Cryptography_X509_PKCS12_LimitsReadOnly); + } + } + + /// + /// Gets or sets the iteration limit for the MAC calculation. + /// + /// The iteration limit for the MAC calculation. + public int? MacIterationLimit + { + get => _macIterationLimit; + set + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + + CheckReadOnly(); + _macIterationLimit = value; + } + } + + /// + /// Gets or sets the iteration limit for the individual Key Derivation Function (KDF) calculations. + /// + /// + /// The iteration limit for the individual Key Derivation Function (KDF) calculations. + /// + public int? IndividualKdfIterationLimit + { + get => _individualKdfIterationLimit; + set + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + + CheckReadOnly(); + _individualKdfIterationLimit = value; + } + } + + /// + /// Gets or sets the total iteration limit for the Key Derivation Function (KDF) calculations. + /// + /// + /// The total iteration limit for the Key Derivation Function (KDF) calculations. + /// + public int? TotalKdfIterationLimit + { + get => _totalKdfIterationLimit; + set + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + + CheckReadOnly(); + _totalKdfIterationLimit = value; + } + } + + /// + /// Gets or sets the maximum number of keys permitted. + /// + /// + /// The maximum number of keys permitted. + /// + public int? MaxKeys + { + get => _maxKeys; + set + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + + CheckReadOnly(); + _maxKeys = value; + } + } + + /// + /// Gets or sets the maximum number of certificates permitted. + /// + /// + /// The maximum number of certificates permitted. + /// + public int? MaxCertificates + { + get => _maxCertificates; + set + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + + CheckReadOnly(); + _maxCertificates = value; + } + } + + /// + /// Gets or sets a value indicating whether to preserve the storage provider. + /// + /// + /// to respect the storage provider identifier for a + /// private key; to ignore the storage provider + /// information and use the system defaults. + /// The default is . + /// + /// + /// Storage Provider values from the PFX are only processed on the + /// Microsoft Windows family of operating systems. + /// This property has no effect on non-Windows systems. + /// + public bool PreserveStorageProvider + { + get => _preserveStorageProvider; + set + { + CheckReadOnly(); + _preserveStorageProvider = value; + } + } + + /// + /// Gets or sets a value indicating whether to preserve the key name. + /// + /// + /// to respect the key name identifier for a + /// private key; to ignore the key name + /// information and use a randomly generated identifier. + /// The default is . + /// + /// + /// Key name identifier values from the PFX are only processed on the + /// Microsoft Windows family of operating systems. + /// This property has no effect on non-Windows systems. + /// + public bool PreserveKeyName + { + get => _preserveKeyName; + set + { + CheckReadOnly(); + _preserveKeyName = value; + } + } + + /// + /// Gets or sets a value indicating whether to preserve the certificate alias, + /// also known as the friendly name. + /// + /// + /// to respect the alias for a + /// certificate; to ignore the alias + /// information. + /// The default is . + /// + /// + /// Certificate alias values from the PFX are only processed on the + /// Microsoft Windows family of operating systems. + /// This property has no effect on non-Windows systems. + /// + /// + public bool PreserveCertificateAlias + { + get => _preserveCertificateAlias; + set + { + CheckReadOnly(); + _preserveCertificateAlias = value; + } + } + + /// + /// Gets or sets a value indicating whether to preserve unknown attributes. + /// + /// + /// to keep any attributes of a certificate or + /// private key that are not described by another property on this type intact + /// when invoking the system PKCS#12/PFX loader; + /// to remove the unknown attributes prior to invoking + /// the system loader. + /// The default is . + /// + public bool PreserveUnknownAttributes + { + get => _preserveUnknownAttributes; + set + { + CheckReadOnly(); + _preserveUnknownAttributes = value; + } + } + + /// + /// Gets or sets a value indicating whether to ignore private keys. + /// + /// + /// to skip loading private keys; + /// to load both certificates and private keys. + /// The default is . + /// + public bool IgnorePrivateKeys + { + get => _ignorePrivateKeys; + set + { + CheckReadOnly(); + _ignorePrivateKeys = value; + } + } + + /// + /// Gets or sets a value indicating whether to ignore encrypted authentication safes. + /// + /// + /// to skip over encrypted PFX AuthSafe values; + /// to decrypt encrypted PFX AuthSafe values to process their + /// contents. + /// The default is . + /// + public bool IgnoreEncryptedAuthSafes + { + get => _ignoreEncryptedAuthSafes; + set + { + CheckReadOnly(); + _ignoreEncryptedAuthSafes = value; + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs new file mode 100644 index 00000000000000..8534e699d5b55c --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs @@ -0,0 +1,944 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Formats.Asn1; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Asn1.Pkcs7; +using System.Security.Cryptography.Asn1.Pkcs12; +using System.Security.Cryptography.Pkcs; +using Internal.Cryptography; + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + private const int CRYPT_E_BAD_DECODE = unchecked((int)0x80092002); + private const int ERROR_INVALID_PASSWORD = unchecked((int)0x80070056); + +#if NETCOREAPP + private const int NTE_FAIL = unchecked((int)0x80090020); +#endif + + static partial void LoadPkcs12NoLimits( + ReadOnlyMemory data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + ref Pkcs12Return earlyReturn); + + static partial void LoadPkcs12NoLimits( + ReadOnlyMemory data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + ref X509Certificate2Collection? earlyReturn); + + private static partial Pkcs12Return LoadPkcs12( + ref BagState bagState, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags); + + private static partial X509Certificate2Collection LoadPkcs12Collection( + ref BagState bagState, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags); + + private static Pkcs12Return LoadPkcs12( + ReadOnlyMemory data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + if (ReferenceEquals(loaderLimits, Pkcs12LoaderLimits.DangerousNoLimits)) + { + Pkcs12Return earlyReturn = default; + LoadPkcs12NoLimits(data, password, keyStorageFlags, ref earlyReturn); + + if (earlyReturn.HasValue()) + { + return earlyReturn; + } + } + + BagState bagState = default; + + try + { + ReadCertsAndKeys(ref bagState, data, ref password, loaderLimits); + + if (bagState.CertCount == 0) + { + throw new CryptographicException(SR.Cryptography_Pfx_NoCertificates); + } + + bagState.UnshroudKeys(ref password); + + return LoadPkcs12(ref bagState, password, keyStorageFlags); + } + finally + { + bagState.Dispose(); + } + } + + private static X509Certificate2Collection LoadPkcs12Collection( + ReadOnlyMemory data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + if (ReferenceEquals(loaderLimits, Pkcs12LoaderLimits.DangerousNoLimits)) + { + X509Certificate2Collection? earlyReturn = null; + LoadPkcs12NoLimits(data, password, keyStorageFlags, ref earlyReturn); + + if (earlyReturn is not null) + { + return earlyReturn; + } + } + + BagState bagState = default; + + try + { + ReadCertsAndKeys(ref bagState, data, ref password, loaderLimits); + + if (bagState.CertCount == 0) + { + return new X509Certificate2Collection(); + } + + bagState.UnshroudKeys(ref password); + + return LoadPkcs12Collection(ref bagState, password, keyStorageFlags); + } + finally + { + bagState.Dispose(); + } + } + + private static void ReadCertsAndKeys( + ref BagState bagState, + ReadOnlyMemory data, + ref ReadOnlySpan password, + Pkcs12LoaderLimits loaderLimits) + { + try + { + AsnDecoder.ReadSequence(data.Span, AsnEncodingRules.BER, out _, out _, out int trimLength); + data = data.Slice(0, trimLength); + + PfxAsn pfxAsn = PfxAsn.Decode(data, AsnEncodingRules.BER); + + if (pfxAsn.AuthSafe.ContentType != Oids.Pkcs7Data) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + ReadOnlyMemory authSafeMemory = + Helpers.DecodeOctetStringAsMemory(pfxAsn.AuthSafe.Content); + ReadOnlySpan authSafeContents = authSafeMemory.Span; + + if (!password.IsEmpty) + { + bagState.LockPassword(); + } + + if (pfxAsn.MacData.HasValue) + { + if (pfxAsn.MacData.Value.IterationCount > loaderLimits.MacIterationLimit) + { + throw new Pkcs12LoadLimitExceededException(nameof(Pkcs12LoaderLimits.MacIterationLimit)); + } + + bool verified = false; + + if (!bagState.LockedPassword) + { + if (!pfxAsn.VerifyMac(password, authSafeContents)) + { + password = password.ContainsNull() ? "".AsSpan() : default; + } + else + { + verified = true; + } + } + + if (!verified && !pfxAsn.VerifyMac(password, authSafeContents)) + { + ThrowWithHResult(SR.Cryptography_Pfx_BadPassword, ERROR_INVALID_PASSWORD); + } + + bagState.ConfirmPassword(); + } + + AsnValueReader outer = new AsnValueReader(authSafeContents, AsnEncodingRules.BER); + AsnValueReader reader = outer.ReadSequence(); + outer.ThrowIfNotEmpty(); + + ReadOnlyMemory rebind = pfxAsn.AuthSafe.Content; + bagState.Init(loaderLimits); + + int? workRemaining = loaderLimits.TotalKdfIterationLimit; + + while (reader.HasData) + { + ContentInfoAsn.Decode(ref reader, rebind, out ContentInfoAsn safeContentsAsn); + + ReadOnlyMemory contentData; + + if (safeContentsAsn.ContentType == Oids.Pkcs7Data) + { + contentData = Helpers.DecodeOctetStringAsMemory(safeContentsAsn.Content); + } + else if (safeContentsAsn.ContentType == Oids.Pkcs7Encrypted) + { + if (loaderLimits.IgnoreEncryptedAuthSafes) + { + continue; + } + + bagState.PrepareDecryptBuffer(authSafeContents.Length); + + if (!bagState.LockedPassword) + { + bagState.LockPassword(); + int? workRemainingSave = workRemaining; + + try + { + contentData = DecryptSafeContents( + safeContentsAsn, + loaderLimits, + password, + ref bagState, + ref workRemaining); + } + catch (CryptographicException) + { + password = password.ContainsNull() ? "".AsSpan() : default; + workRemaining = workRemainingSave; + + contentData = DecryptSafeContents( + safeContentsAsn, + loaderLimits, + password, + ref bagState, + ref workRemaining); + } + } + else + { + contentData = DecryptSafeContents( + safeContentsAsn, + loaderLimits, + password, + ref bagState, + ref workRemaining); + } + } + else + { + // Should there be an option here to preserve this? + // Ignore this? + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + ProcessSafeContents( + contentData, + loaderLimits, + ref workRemaining, + ref bagState); + } + } + catch (AsnContentException e) + { + ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE, e); + } + } + + private static void ProcessSafeContents( + ReadOnlyMemory contentData, + Pkcs12LoaderLimits loaderLimits, + ref int? workRemaining, + ref BagState bagState) + { + AsnValueReader outer = new AsnValueReader(contentData.Span, AsnEncodingRules.BER); + AsnValueReader reader = outer.ReadSequence(); + outer.ThrowIfNotEmpty(); + + while (reader.HasData) + { + SafeBagAsn.Decode(ref reader, contentData, out SafeBagAsn bag); + + if (bag.BagId == Oids.Pkcs12CertBag) + { + CertBagAsn certBag = CertBagAsn.Decode(bag.BagValue, AsnEncodingRules.BER); + + if (certBag.CertId == Oids.Pkcs12X509CertBagType) + { + if (bagState.CertCount >= loaderLimits.MaxCertificates) + { + throw new Pkcs12LoadLimitExceededException(nameof(Pkcs12LoaderLimits.MaxCertificates)); + } + + if (bag.BagAttributes is not null) + { + FilterAttributes( + loaderLimits, + ref bag, + static (limits, oid) => + oid switch + { + Oids.LocalKeyId => true, + "1.2.840.113549.1.9.20" => limits.PreserveCertificateAlias, + _ => limits.PreserveUnknownAttributes, + }); + } + + bagState.AddCert(bag); + } + } + else if (bag.BagId is Oids.Pkcs12KeyBag or Oids.Pkcs12ShroudedKeyBag) + { + if (loaderLimits.IgnorePrivateKeys) + { + continue; + } + + if (bagState.KeyCount >= loaderLimits.MaxKeys) + { + throw new Pkcs12LoadLimitExceededException(nameof(Pkcs12LoaderLimits.MaxKeys)); + } + + if (bag.BagId == Oids.Pkcs12ShroudedKeyBag) + { + EncryptedPrivateKeyInfoAsn epki = EncryptedPrivateKeyInfoAsn.Decode( + bag.BagValue, + AsnEncodingRules.BER); + + int kdfCount = GetKdfCount(epki.EncryptionAlgorithm); + + if (kdfCount > loaderLimits.IndividualKdfIterationLimit || kdfCount > workRemaining) + { + string propertyName = kdfCount > loaderLimits.IndividualKdfIterationLimit ? + nameof(Pkcs12LoaderLimits.IndividualKdfIterationLimit) : + nameof(Pkcs12LoaderLimits.TotalKdfIterationLimit); + + throw new Pkcs12LoadLimitExceededException(propertyName); + } + + if (workRemaining.HasValue) + { + workRemaining = checked(workRemaining - kdfCount); + } + } + + if (bag.BagAttributes is not null) + { + FilterAttributes( + loaderLimits, + ref bag, + static (limits, attrType) => + attrType switch + { + Oids.LocalKeyId => true, + "1.2.840.113549.1.9.20" => limits.PreserveKeyName, + "1.3.6.1.4.1.311.17.1" => limits.PreserveStorageProvider, + _ => limits.PreserveUnknownAttributes, + }); + } + + bagState.AddKey(bag); + } + } + } + + private static void FilterAttributes( + Pkcs12LoaderLimits loaderLimits, + ref SafeBagAsn bag, + Func filter) + { + if (bag.BagAttributes is not null) + { + // Should this dedup/fail-on-dup? + int attrIdx = -1; + + for (int i = bag.BagAttributes.Length - 1; i > attrIdx; i--) + { + string attrType = bag.BagAttributes[i].AttrType; + + if (filter(loaderLimits, attrType)) + { + attrIdx++; + + if (i > attrIdx) + { + AttributeAsn attr = bag.BagAttributes[i]; + bag.BagAttributes[i] = bag.BagAttributes[attrIdx]; + + // After swapping, back up one position to check if the attribute + // swapped into this position should also be preserved. + i++; + +#if DEBUG + // In debug we'll do a full swap, just so the full set of input + // attributes can be seen under a debugger before the reducing + // Array.Resize + bag.BagAttributes[attrIdx] = attr; +#endif + } + } + } + + attrIdx++; + + if (attrIdx < bag.BagAttributes.Length) + { + if (attrIdx == 0) + { + bag.BagAttributes = null; + } + else + { + Array.Resize(ref bag.BagAttributes, attrIdx); + } + } + } + } + + private static ReadOnlyMemory DecryptSafeContents( + ContentInfoAsn safeContentsAsn, + Pkcs12LoaderLimits loaderLimits, + ReadOnlySpan passwordSpan, + ref BagState bagState, + ref int? workRemaining) + { + EncryptedDataAsn encryptedData = + EncryptedDataAsn.Decode(safeContentsAsn.Content, AsnEncodingRules.BER); + + // https://tools.ietf.org/html/rfc5652#section-8 + if (encryptedData.Version != 0 && encryptedData.Version != 2) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + // Since the contents are supposed to be the BER-encoding of an instance of + // SafeContents (https://tools.ietf.org/html/rfc7292#section-4.1) that implies the + // content type is simply "data", and that content is present. + if (encryptedData.EncryptedContentInfo.ContentType != Oids.Pkcs7Data) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + if (!encryptedData.EncryptedContentInfo.EncryptedContent.HasValue) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + ReadOnlyMemory encryptedContent = + encryptedData.EncryptedContentInfo.EncryptedContent.Value; + + int kdfCount = GetKdfCount(encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm); + + if (kdfCount > loaderLimits.IndividualKdfIterationLimit || kdfCount > workRemaining) + { + throw new Pkcs12LoadLimitExceededException( + kdfCount > loaderLimits.IndividualKdfIterationLimit ? + nameof(Pkcs12LoaderLimits.IndividualKdfIterationLimit) : + nameof(Pkcs12LoaderLimits.TotalKdfIterationLimit)); + } + + if (workRemaining.HasValue) + { + workRemaining = checked(workRemaining - kdfCount); + } + + return bagState.DecryptSafeContents( + encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm, + passwordSpan, + encryptedContent.Span); + } + + private static int GetKdfCount(in AlgorithmIdentifierAsn algorithmIdentifier) + { + int rawCount = GetRawKdfCount(in algorithmIdentifier); + + if (rawCount < 0) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return rawCount; + + static int GetRawKdfCount(in AlgorithmIdentifierAsn algorithmIdentifier) + { + if (!algorithmIdentifier.Parameters.HasValue) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + switch (algorithmIdentifier.Algorithm) + { + case Oids.PbeWithMD5AndDESCBC: + case Oids.PbeWithMD5AndRC2CBC: + case Oids.PbeWithSha1AndDESCBC: + case Oids.PbeWithSha1AndRC2CBC: + case Oids.Pkcs12PbeWithShaAnd3Key3Des: + case Oids.Pkcs12PbeWithShaAnd2Key3Des: + case Oids.Pkcs12PbeWithShaAnd128BitRC2: + case Oids.Pkcs12PbeWithShaAnd40BitRC2: + PBEParameter pbeParameter = PBEParameter.Decode( + algorithmIdentifier.Parameters.Value, + AsnEncodingRules.BER); + + return pbeParameter.IterationCount; + case Oids.PasswordBasedEncryptionScheme2: + PBES2Params pbes2Params = PBES2Params.Decode( + algorithmIdentifier.Parameters.Value, + AsnEncodingRules.BER); + + if (pbes2Params.KeyDerivationFunc.Algorithm != Oids.Pbkdf2) + { + throw new CryptographicException( + SR.Format( + SR.Cryptography_UnknownAlgorithmIdentifier, + pbes2Params.EncryptionScheme.Algorithm)); + } + + if (!pbes2Params.KeyDerivationFunc.Parameters.HasValue) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + Pbkdf2Params pbkdf2Params = Pbkdf2Params.Decode( + pbes2Params.KeyDerivationFunc.Parameters.Value, + AsnEncodingRules.BER); + + return pbkdf2Params.IterationCount; + default: + throw new CryptographicException( + SR.Format( + SR.Cryptography_UnknownAlgorithmIdentifier, + algorithmIdentifier.Algorithm)); + } + } + } + + private readonly partial struct Pkcs12Return + { + internal partial bool HasValue(); + internal partial X509Certificate2 ToCertificate(); + } + + private partial struct BagState + { + private SafeBagAsn[]? _certBags; + private SafeBagAsn[]? _keyBags; + private byte[]? _decryptBuffer; + private byte[]? _keyDecryptBuffer; + private int _certCount; + private int _keyCount; + private int _decryptBufferOffset; + private int _keyDecryptBufferOffset; + private byte _passwordState; + + internal void Init(Pkcs12LoaderLimits loaderLimits) + { + _certBags = ArrayPool.Shared.Rent(loaderLimits.MaxCertificates.GetValueOrDefault(10)); + _keyBags = ArrayPool.Shared.Rent(loaderLimits.MaxKeys.GetValueOrDefault(10)); + _certCount = 0; + _keyCount = 0; + _decryptBufferOffset = 0; + } + + public void Dispose() + { + if (_certBags is not null) + { + ArrayPool.Shared.Return(_certBags, clearArray: true); + } + + if (_keyBags is not null) + { + ArrayPool.Shared.Return(_keyBags, clearArray: true); + } + + if (_decryptBuffer is not null) + { + CryptoPool.Return(_decryptBuffer, _decryptBufferOffset); + } + + if (_keyDecryptBuffer is not null) + { + CryptoPool.Return(_keyDecryptBuffer, _keyDecryptBufferOffset); + } + + this = default; + } + + public readonly int CertCount => _certCount; + + public readonly int KeyCount => _keyCount; + + internal bool LockedPassword => (_passwordState & 1) != 0; + + internal void LockPassword() + { + _passwordState |= 1; + } + + internal bool ConfirmedPassword => (_passwordState & 2) != 0; + + internal void ConfirmPassword() + { + // Confirming it (verifying it was correct), also locks it. + _passwordState |= 3; + } + + internal void PrepareDecryptBuffer(int upperBound) + { + if (_decryptBuffer is null) + { + _decryptBuffer = CryptoPool.Rent(upperBound); + } + else + { + Debug.Assert(_decryptBuffer.Length >= upperBound); + } + } + + internal ReadOnlyMemory DecryptSafeContents( + in AlgorithmIdentifierAsn algorithmIdentifier, + ReadOnlySpan passwordSpan, + ReadOnlySpan encryptedContent) + { + Debug.Assert(_decryptBuffer is not null); + Debug.Assert(_decryptBuffer.Length - _decryptBufferOffset >= encryptedContent.Length); + + // In case anything goes wrong decrypting, clear the whole buffer in the cleanup + int saveOffset = _decryptBufferOffset; + _decryptBufferOffset = _decryptBuffer.Length; + + try + { + int written = PasswordBasedEncryption.Decrypt( + algorithmIdentifier, + passwordSpan, + default, + encryptedContent, + _decryptBuffer.AsSpan(saveOffset)); + + _decryptBufferOffset = saveOffset + written; + + try + { + AsnValueReader reader = new AsnValueReader( + _decryptBuffer.AsSpan(saveOffset, written), + AsnEncodingRules.BER); + + reader.ReadSequence(); + reader.ThrowIfNotEmpty(); + } + catch (AsnContentException) + { + ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE); + } + + ConfirmPassword(); + + return new ReadOnlyMemory( + _decryptBuffer, + saveOffset, + written); + } + catch (CryptographicException e) + { + CryptographicOperations.ZeroMemory( + _decryptBuffer.AsSpan(saveOffset, _decryptBufferOffset - saveOffset)); + + _decryptBufferOffset = saveOffset; + +#if NETCOREAPP + if (e.HResult != CRYPT_E_BAD_DECODE) + { + e.HResult = ConfirmedPassword ? NTE_FAIL : ERROR_INVALID_PASSWORD; + } +#else + Debug.Assert(e.HResult != 0); +#endif + + throw; + } + } + + internal void AddCert(SafeBagAsn bag) + { + Debug.Assert(_certBags is not null); + + GrowIfNeeded(ref _certBags, _certCount); + _certBags[_certCount] = bag; + _certCount++; + } + + internal void AddKey(SafeBagAsn bag) + { + Debug.Assert(_keyBags is not null); + + GrowIfNeeded(ref _keyBags, _keyCount); + _keyBags[_keyCount] = bag; + _keyCount++; + } + + private static void GrowIfNeeded(ref SafeBagAsn[] array, int index) + { + if (array.Length <= index) + { + SafeBagAsn[] next = ArrayPool.Shared.Rent(checked(index + 1)); + array.AsSpan().CopyTo(next); + ArrayPool.Shared.Return(array, clearArray: true); + array = next; + } + } + + internal void UnshroudKeys(ref ReadOnlySpan password) + { + Debug.Assert(_keyBags is not null); + + int spaceRequired = 0; + + for (int i = 0; i < _keyCount; i++) + { + SafeBagAsn bag = _keyBags[i]; + + if (bag.BagId == Oids.Pkcs12ShroudedKeyBag) + { + spaceRequired += bag.BagValue.Length; + } + } + + _keyDecryptBuffer = CryptoPool.Rent(spaceRequired); + + for (int i = 0; i < _keyCount; i++) + { + ref SafeBagAsn bag = ref _keyBags[i]; + + if (bag.BagId == Oids.Pkcs12ShroudedKeyBag) + { + ArraySegment decrypted = default; + int contentRead = 0; + + if (!LockedPassword) + { + try + { + decrypted = KeyFormatHelper.DecryptPkcs8( + password, + bag.BagValue, + out contentRead); + + try + { + AsnValueReader reader = new AsnValueReader(decrypted, AsnEncodingRules.BER); + reader.ReadSequence(); + reader.ThrowIfNotEmpty(); + } + catch (AsnContentException) + { + CryptoPool.Return(decrypted); + decrypted = default; + throw new CryptographicException(); + } + } + catch (CryptographicException) + { + password = password.ContainsNull() ? "".AsSpan() : default; + } + } + + if (decrypted.Array is null) + { + try + { + decrypted = KeyFormatHelper.DecryptPkcs8( + password, + bag.BagValue, + out contentRead); + + try + { + AsnValueReader reader = new AsnValueReader(decrypted, AsnEncodingRules.BER); + reader.ReadSequence(); + reader.ThrowIfNotEmpty(); + } + catch (AsnContentException) + { + CryptoPool.Return(decrypted); + decrypted = default; + throw new CryptographicException(); + } + } + catch (CryptographicException) + { + // Windows 10 compatibility: + // If anything goes wrong loading this key, just ignore it. + // If no one ended up needing it, no harm/no foul. + // If this has a LocalKeyId and something references it, then it'll fail. + + continue; + } + } + + ConfirmPassword(); + + Debug.Assert(decrypted.Array is not null); + Debug.Assert(_keyDecryptBuffer.Length - _keyDecryptBufferOffset >= decrypted.Count); + decrypted.AsSpan().CopyTo(_keyDecryptBuffer.AsSpan(_keyDecryptBufferOffset)); + + ReadOnlyMemory newBagValue = new( + _keyDecryptBuffer, + _keyDecryptBufferOffset, + decrypted.Count); + + CryptoPool.Return(decrypted); + _keyDecryptBufferOffset += newBagValue.Length; + + if (contentRead != bag.BagValue.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + bag.BagValue = newBagValue; + bag.BagId = Oids.Pkcs12KeyBag; + } + } + } + + internal ArraySegment ToPfx(ReadOnlySpan password) + { + Debug.Assert(_certBags is not null); + Debug.Assert(_keyBags is not null); + + ContentInfoAsn safeContents = new ContentInfoAsn + { + ContentType = Oids.Pkcs7Data, + }; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); + writer.PushOctetString(); + writer.PushSequence(); + + for (int i = 0; i < _certCount; i++) + { + SafeBagAsn bag = _certBags[i]; + bag.Encode(writer); + } + + for (int i = 0; i < _keyCount; i++) + { + SafeBagAsn bag = _keyBags[i]; + bag.Encode(writer); + } + + writer.PopSequence(); + writer.PopOctetString(); + safeContents.Content = writer.Encode(); + writer.Reset(); + + writer.PushSequence(); + safeContents.Encode(writer); + writer.PopSequence(); + byte[] authSafe = writer.Encode(); + writer.Reset(); + + HashAlgorithmName hashAlgorithm = HashAlgorithmName.SHA1; + byte[] macKey = new byte[20]; + Span salt = stackalloc byte[macKey.Length]; + + Helpers.RngFill(salt); + + Pkcs12Kdf.DeriveMacKey( + password, + hashAlgorithm, + 1, + salt, + macKey); + + using (IncrementalHash mac = IncrementalHash.CreateHMAC(hashAlgorithm, macKey)) + { + mac.AppendData(authSafe); + + if (!mac.TryGetHashAndReset(macKey, out int bytesWritten) || bytesWritten != macKey.Length) + { + Debug.Fail($"TryGetHashAndReset wrote {bytesWritten} of {macKey.Length} bytes"); + throw new CryptographicException(); + } + } + + // https://tools.ietf.org/html/rfc7292#section-4 + // + // PFX ::= SEQUENCE { + // version INTEGER {v3(3)}(v3,...), + // authSafe ContentInfo, + // macData MacData OPTIONAL + // } + { + writer.PushSequence(); + + writer.WriteInteger(3); + + writer.PushSequence(); + { + writer.WriteObjectIdentifierForCrypto(Oids.Pkcs7Data); + + Asn1Tag contextSpecific0 = new Asn1Tag(TagClass.ContextSpecific, 0); + + writer.PushSequence(contextSpecific0); + { + writer.WriteOctetString(authSafe); + writer.PopSequence(contextSpecific0); + } + + writer.PopSequence(); + } + + // https://tools.ietf.org/html/rfc7292#section-4 + // + // MacData ::= SEQUENCE { + // mac DigestInfo, + // macSalt OCTET STRING, + // iterations INTEGER DEFAULT 1 + // -- Note: The default is for historical reasons and its use is + // -- deprecated. + // } + writer.PushSequence(); + { + writer.PushSequence(); + { + writer.PushSequence(); + { + writer.WriteObjectIdentifierForCrypto(Oids.Sha1); + writer.PopSequence(); + } + + writer.WriteOctetString(macKey); + writer.PopSequence(); + } + + writer.WriteOctetString(salt); + writer.PopSequence(); + } + + writer.PopSequence(); + } + + byte[] ret = CryptoPool.Rent(writer.GetEncodedLength()); + int written = writer.Encode(ret); + return new ArraySegment(ret, 0, written); + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs new file mode 100644 index 00000000000000..faaa775e4de52e --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs @@ -0,0 +1,742 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; +using System.IO; +using System.IO.MemoryMappedFiles; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace System.Security.Cryptography.X509Certificates +{ + [UnsupportedOSPlatform("browser")] + public static partial class X509CertificateLoader + { + private const int MemoryMappedFileCutoff = 1_048_576; + + /// + /// Loads a single X.509 certificate from , in either the PEM + /// or DER encoding. + /// + /// The data to load. + /// + /// The certificate loaded from . + /// + /// + /// The data did not load as a valid X.509 certificate. + /// + /// + /// This method only loads plain certificates, which are identified as + /// by + /// + /// + public static partial X509Certificate2 LoadCertificate(ReadOnlySpan data); + + /// + /// Loads a single X.509 certificate from , in either the PEM + /// or DER encoding. + /// + /// The data to load. + /// + /// The certificate loaded from . + /// + /// + /// is . + /// + /// + /// The data did not load as a valid X.509 certificate. + /// + /// + /// This method only loads plain certificates, which are identified as + /// by + /// + /// + public static partial X509Certificate2 LoadCertificate(byte[] data); + + /// + /// Loads a single X.509 certificate (in either the PEM or DER encoding) + /// from the specified file. + /// + /// The path of the file to open. + /// + /// The loaded certificate. + /// + /// + /// is . + /// + /// + /// The data did not load as a valid X.509 certificate. + /// + /// + /// An error occurred while loading the specified file. + /// + /// + /// This method only loads plain certificates, which are identified as + /// by + /// + /// + public static partial X509Certificate2 LoadCertificateFromFile(string path); + + /// + /// Loads the provided data as a PKCS#12 PFX and extracts a certificate. + /// + /// The data to load. + /// The password to decrypt the contents of the PFX. + /// + /// A bitwise combination of the enumeration values that control where and how to + /// import the private key associated with the returned certificate. + /// + /// + /// Limits to apply when loading the PFX. A value, the default, + /// is equivalent to . + /// + /// The loaded certificate. + /// + /// is . + /// + /// + /// contains a value, or combination of values, + /// that is not valid. + /// + /// + /// contains a value that is not valid for the + /// current platform. + /// + /// + /// The PKCS#12/PFX violated one or more constraints of . + /// + /// + /// An error occurred while loading the PKCS#12/PFX. + /// + /// + /// A PKCS#12/PFX can contain multiple certificates. + /// Using the ordering that the certificates appear in the results of + /// , + /// this method returns the first + /// certificate where is + /// . + /// If no certificates have associated private keys, then the first + /// certificate is returned. + /// If the PKCS#12/PFX contains no certificates, a + /// is thrown. + /// + public static X509Certificate2 LoadPkcs12( + byte[] data, + string? password, + X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.DefaultKeySet, + Pkcs12LoaderLimits? loaderLimits = null) + { + ThrowIfNull(data); + ValidateKeyStorageFlagsCore(keyStorageFlags); + + return LoadPkcs12( + new ReadOnlyMemory(data), + password.AsSpan(), + keyStorageFlags, + loaderLimits ?? Pkcs12LoaderLimits.Defaults).ToCertificate(); + } + + /// + /// Loads the provided data as a PKCS#12 PFX and extracts a certificate. + /// + /// The data to load. + /// The password to decrypt the contents of the PFX. + /// + /// A bitwise combination of the enumeration values that control where and how to + /// import the private key associated with the returned certificate. + /// + /// + /// Limits to apply when loading the PFX. A value, the default, + /// is equivalent to . + /// + /// The loaded certificate. + /// + /// is . + /// + /// + /// contains a value, or combination of values, + /// that is not valid. + /// + /// + /// contains a value that is not valid for the + /// current platform. + /// + /// + /// The PKCS#12/PFX violated one or more constraints of . + /// + /// + /// An error occurred while loading the PKCS#12/PFX. + /// + /// + /// A PKCS#12/PFX can contain multiple certificates. + /// Using the ordering that the certificates appear in the results of + /// , + /// this method returns the first + /// certificate where is + /// . + /// If no certificates have associated private keys, then the first + /// certificate is returned. + /// If the PKCS#12/PFX contains no certificates, a + /// is thrown. + /// + public static X509Certificate2 LoadPkcs12( + ReadOnlySpan data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.DefaultKeySet, + Pkcs12LoaderLimits? loaderLimits = null) + { + unsafe + { + fixed (byte* pinned = data) + { + using (PointerMemoryManager manager = new(pinned, data.Length)) + { + return LoadPkcs12( + manager.Memory, + password, + keyStorageFlags, + loaderLimits ?? Pkcs12LoaderLimits.Defaults).ToCertificate(); + } + } + } + } + + /// + /// Opens the specified file, reads the contents as a PKCS#12 PFX and extracts a certificate. + /// + /// The path of the file to open. + /// + /// The loaded certificate. + /// + /// The password to decrypt the contents of the PFX. + /// + /// A bitwise combination of the enumeration values that control where and how to + /// import the private key associated with the returned certificate. + /// + /// + /// Limits to apply when loading the PFX. A value, the default, + /// is equivalent to . + /// + /// The loaded certificate. + /// + /// is . + /// + /// + /// contains a value, or combination of values, + /// that is not valid. + /// + /// + /// contains a value that is not valid for the + /// current platform. + /// + /// + /// The PKCS#12/PFX violated one or more constraints of . + /// + /// + /// An error occurred while loading the PKCS#12/PFX. + /// + /// + /// An error occurred while loading the specified file. + /// + /// + /// A PKCS#12/PFX can contain multiple certificates. + /// Using the ordering that the certificates appear in the results of + /// , + /// this method returns the first + /// certificate where is + /// . + /// If no certificates have associated private keys, then the first + /// certificate is returned. + /// If the PKCS#12/PFX contains no certificates, a + /// is thrown. + /// + public static X509Certificate2 LoadPkcs12FromFile( + string path, + string? password, + X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.DefaultKeySet, + Pkcs12LoaderLimits? loaderLimits = null) + { + return LoadPkcs12FromFile( + path, + password.AsSpan(), + keyStorageFlags, + loaderLimits); + } + + /// + /// Opens the specified file, reads the contents as a PKCS#12 PFX and extracts a certificate. + /// + /// The path of the file to open. + /// + /// The loaded certificate. + /// + /// The password to decrypt the contents of the PFX. + /// + /// A bitwise combination of the enumeration values that control where and how to + /// import the private key associated with the returned certificate. + /// + /// + /// Limits to apply when loading the PFX. A value, the default, + /// is equivalent to . + /// + /// The loaded certificate. + /// + /// is . + /// + /// + /// contains a value, or combination of values, + /// that is not valid. + /// + /// + /// contains a value that is not valid for the + /// current platform. + /// + /// + /// The PKCS#12/PFX violated one or more constraints of . + /// + /// + /// An error occurred while loading the PKCS#12/PFX. + /// + /// + /// An error occurred while loading the specified file. + /// + /// + /// A PKCS#12/PFX can contain multiple certificates. + /// Using the ordering that the certificates appear in the results of + /// , + /// this method returns the first + /// certificate where is + /// . + /// If no certificates have associated private keys, then the first + /// certificate is returned. + /// If the PKCS#12/PFX contains no certificates, a + /// is thrown. + /// + public static X509Certificate2 LoadPkcs12FromFile( + string path, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.DefaultKeySet, + Pkcs12LoaderLimits? loaderLimits = null) + { + ThrowIfNullOrEmpty(path); + ValidateKeyStorageFlagsCore(keyStorageFlags); + + return LoadFromFile( + path, + password, + keyStorageFlags, + loaderLimits ?? Pkcs12LoaderLimits.Defaults, + LoadPkcs12).ToCertificate(); + } + + /// + /// Loads the provided data as a PKCS#12 PFX and returns a collection of + /// all of the certificates therein. + /// + /// The data to load. + /// The password to decrypt the contents of the PFX. + /// + /// A bitwise combination of the enumeration values that control where and how to + /// import the private key associated with the returned certificate. + /// + /// + /// Limits to apply when loading the PFX. A value, the default, + /// is equivalent to . + /// + /// A collection of the certificates loaded from the input. + /// + /// is . + /// + /// + /// contains a value, or combination of values, + /// that is not valid. + /// + /// + /// contains a value that is not valid for the + /// current platform. + /// + /// + /// The PKCS#12/PFX violated one or more constraints of . + /// + /// + /// An error occurred while loading the PKCS#12/PFX. + /// + public static X509Certificate2Collection LoadPkcs12Collection( + byte[] data, + string? password, + X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.DefaultKeySet, + Pkcs12LoaderLimits? loaderLimits = null) + { + ThrowIfNull(data); + ValidateKeyStorageFlagsCore(keyStorageFlags); + + return LoadPkcs12Collection( + new ReadOnlyMemory(data), + password.AsSpan(), + keyStorageFlags, + loaderLimits ?? Pkcs12LoaderLimits.Defaults); + } + + /// + /// Loads the provided data as a PKCS#12 PFX and returns a collection of + /// all of the certificates therein. + /// + /// The data to load. + /// The password to decrypt the contents of the PFX. + /// + /// A bitwise combination of the enumeration values that control where and how to + /// import the private key associated with the returned certificate. + /// + /// + /// Limits to apply when loading the PFX. A value, the default, + /// is equivalent to . + /// + /// A collection of the certificates loaded from the input. + /// + /// is . + /// + /// + /// contains a value, or combination of values, + /// that is not valid. + /// + /// + /// contains a value that is not valid for the + /// current platform. + /// + /// + /// The PKCS#12/PFX violated one or more constraints of . + /// + /// + /// An error occurred while loading the PKCS#12/PFX. + /// + public static X509Certificate2Collection LoadPkcs12Collection( + ReadOnlySpan data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.DefaultKeySet, + Pkcs12LoaderLimits? loaderLimits = null) + { + ValidateKeyStorageFlagsCore(keyStorageFlags); + + unsafe + { + fixed (byte* pinned = data) + { + using (PointerMemoryManager manager = new(pinned, data.Length)) + { + return LoadPkcs12Collection( + manager.Memory, + password, + keyStorageFlags, + loaderLimits ?? Pkcs12LoaderLimits.Defaults); + } + } + } + } + + /// + /// Opens the specified file, reads the contents as a PKCS#12 PFX and extracts a certificate. + /// Loads the provided data as a PKCS#12 PFX and returns a collection of + /// all of the certificates therein. + /// + /// The path of the file to open. + /// + /// The loaded certificate. + /// + /// The password to decrypt the contents of the PFX. + /// + /// A bitwise combination of the enumeration values that control where and how to + /// import the private key associated with the returned certificate. + /// + /// + /// Limits to apply when loading the PFX. A value, the default, + /// is equivalent to . + /// + /// The loaded certificate. + /// + /// is . + /// + /// + /// contains a value, or combination of values, + /// that is not valid. + /// + /// + /// contains a value that is not valid for the + /// current platform. + /// + /// + /// The PKCS#12/PFX violated one or more constraints of . + /// + /// + /// An error occurred while loading the PKCS#12/PFX. + /// + /// + /// An error occurred while loading the specified file. + /// + public static X509Certificate2Collection LoadPkcs12CollectionFromFile( + string path, + string? password, + X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.DefaultKeySet, + Pkcs12LoaderLimits? loaderLimits = null) + { + return LoadPkcs12CollectionFromFile( + path, + password.AsSpan(), + keyStorageFlags, + loaderLimits); + } + + /// + /// Opens the specified file, reads the contents as a PKCS#12 PFX and extracts a certificate. + /// Loads the provided data as a PKCS#12 PFX and returns a collection of + /// all of the certificates therein. + /// + /// The path of the file to open. + /// + /// The loaded certificate. + /// + /// The password to decrypt the contents of the PFX. + /// + /// A bitwise combination of the enumeration values that control where and how to + /// import the private key associated with the returned certificate. + /// + /// + /// Limits to apply when loading the PFX. A value, the default, + /// is equivalent to . + /// + /// The loaded certificate. + /// + /// is . + /// + /// + /// contains a value, or combination of values, + /// that is not valid. + /// + /// + /// contains a value that is not valid for the + /// current platform. + /// + /// + /// The PKCS#12/PFX violated one or more constraints of . + /// + /// + /// An error occurred while loading the PKCS#12/PFX. + /// + /// + /// An error occurred while loading the specified file. + /// + public static X509Certificate2Collection LoadPkcs12CollectionFromFile( + string path, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.DefaultKeySet, + Pkcs12LoaderLimits? loaderLimits = null) + { + ThrowIfNull(path); + ValidateKeyStorageFlagsCore(keyStorageFlags); + + return LoadFromFile( + path, + password, + keyStorageFlags, + loaderLimits ?? Pkcs12LoaderLimits.Defaults, + LoadPkcs12Collection); + } + + private delegate T LoadFromFileFunc( + ReadOnlyMemory data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits); + + private static T LoadFromFile( + string path, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits, + LoadFromFileFunc loader) + { + (byte[]? rented, int length, MemoryManager? mapped) = ReadAllBytesIfBerSequence(path); + + try + { + Debug.Assert(rented is null != mapped is null); + ReadOnlyMemory memory = mapped?.Memory ?? new ReadOnlyMemory(rented, 0, length); + + return loader(memory, password, keyStorageFlags, loaderLimits); + } + finally + { + (mapped as IDisposable)?.Dispose(); + + if (rented is not null) + { + CryptoPool.Return(rented, length); + } + } + } + + private static (byte[]?, int, MemoryManager?) ReadAllBytesIfBerSequence(string path) + { + // The expected header in a PFX is 30 82 XX XX, but since it's BER-encoded + // it could be up to 30 FE 00 00 00 .. XX YY ZZ AA and still be within the + // bounds of what we can load into an array. 30 FE would be followed by 0x7E bytes, + // so we need 0x81 total bytes for a tag and length using a maximal BER encoding. + Span earlyBuf = stackalloc byte[0x81]; + + try + { + using (FileStream stream = File.OpenRead(path)) + { + int read = stream.ReadAtLeast(earlyBuf, 2); + + if (earlyBuf[0] != 0x30 || earlyBuf[1] is 0 or 1) + { + ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE); + } + + int totalLength; + + if (earlyBuf[1] < 0x80) + { + // The two bytes we already read, plus the interpreted length + totalLength = earlyBuf[1] + 2; + } + else if (earlyBuf[1] == 0x80) + { + // indeterminate length + long streamLength = stream.Length; + + if (streamLength < MemoryMappedFileCutoff) + { + totalLength = (int)streamLength; + } + else + { + totalLength = -1; + } + } + else + { + int lengthLength = earlyBuf[1] - 0x80; + int toRead = lengthLength - read; + + if (toRead > 0) + { + int localRead = stream.ReadAtLeast(earlyBuf.Slice(read), toRead); + read += localRead; + } + + ReadOnlySpan lengthPart = earlyBuf.Slice(1, read - 1); + + if (!AsnDecoder.TryDecodeLength(lengthPart, AsnEncodingRules.BER, out int? decoded, out int decodedLength)) + { + ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE); + } + + Debug.Assert(decoded.HasValue); + + // The interpreted value, the bytes involved in the length (which includes earlyBuf[1]), + // and the tag (earlyBuf[0]) + totalLength = decoded.GetValueOrDefault() + decodedLength + 1; + } + + if (totalLength >= 0) + { + byte[] rented = CryptoPool.Rent(totalLength); + earlyBuf.Slice(0, read).CopyTo(rented); + + stream.ReadExactly(rented.AsSpan(read, totalLength - read)); + return (rented, totalLength, null); + } + + return (null, 0, MemoryMappedFileMemoryManager.CreateFromFileClamped(stream)); + } + } + catch (IOException e) + { + throw new CryptographicException(SR.Arg_CryptographyException, e); + } + catch (UnauthorizedAccessException e) + { + throw new CryptographicException(SR.Arg_CryptographyException, e); + } + } + + [DoesNotReturn] + private static void ThrowWithHResult(string message, int hResult) + { +#if NETCOREAPP + throw new CryptographicException(message) + { + HResult = hResult, + }; +#else +#if NETSTANDARD + if (!Runtime.InteropServices.RuntimeInformation.IsOSPlatform(Runtime.InteropServices.OSPlatform.Windows)) + { + throw new CryptographicException(message); + } +#endif + throw new CryptographicException(hResult); +#endif + } + + static partial void ValidateKeyStorageFlagsCore(X509KeyStorageFlags keyStorageFlags); + + [DoesNotReturn] + private static void ThrowWithHResult(string message, int hResult, Exception innerException) + { +#if NETCOREAPP + throw new CryptographicException(message, innerException) + { + HResult = hResult, + }; +#else +#if NETSTANDARD + if (!Runtime.InteropServices.RuntimeInformation.IsOSPlatform(Runtime.InteropServices.OSPlatform.Windows)) + { + throw new CryptographicException(message, innerException); + } +#endif + + throw new CryptographicException(hResult); +#endif + } + + private static void ThrowIfNull( + [NotNull] object? argument, + [CallerArgumentExpression(nameof(argument))] string? paramName = null) + { + if (argument is null) + { + ThrowNull(paramName); + } + } + + private static void ThrowIfNullOrEmpty( + [NotNull] string? argument, + [CallerArgumentExpression(nameof(argument))] string? paramName = null) + { + if (string.IsNullOrEmpty(argument)) + { + ThrowNullOrEmpty(argument, paramName); + } + } + + [DoesNotReturn] + private static void ThrowNull(string? paramName) + { + throw new ArgumentNullException(paramName); + } + + [DoesNotReturn] + private static void ThrowNullOrEmpty(string? argument, string? paramName) + { + ThrowIfNull(argument, paramName); + throw new ArgumentException(SR.Argument_EmptyString, paramName); + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509IterationCountExceededException.cs b/src/libraries/Common/src/System/Security/Cryptography/X509IterationCountExceededException.cs deleted file mode 100644 index 60ee5d47a2af61..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/X509IterationCountExceededException.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Security.Cryptography.X509Certificates -{ - internal sealed class X509IterationCountExceededException : Exception - { - } -} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs new file mode 100644 index 00000000000000..873b8ff562225d --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs @@ -0,0 +1,218 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using Xunit; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + public abstract partial class X509CertificateLoaderPkcs12Tests + { + [Theory] + [InlineData(true)] + [InlineData(false)] + public void VerifyPreserveKeyName(bool preserveName) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + PreserveKeyName = preserveName, + }; + + string keyName = Guid.NewGuid().ToString("D"); + byte[] pfx = MakeAttributeTest(keyName: keyName, friendlyName: "Non-preserved"); + + X509Certificate2 cert = LoadPfxNoFile( + pfx, + keyStorageFlags: X509KeyStorageFlags.DefaultKeySet, + loaderLimits: loaderLimits); + + using (cert) + { + using (RSA key = cert.GetRSAPrivateKey()) + { + CngKey cngKey = Assert.IsType(key).Key; + + if (preserveName) + { + Assert.Equal(keyName, cngKey.KeyName); + } + else + { + Assert.NotEqual(keyName, cngKey.KeyName); + } + } + + // Alias was not preserved + Assert.Empty(cert.FriendlyName); + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void VerifyPreserveAlias(bool preserveAlias) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + PreserveCertificateAlias = preserveAlias, + }; + + string keyName = Guid.NewGuid().ToString("D"); + string alias = Guid.NewGuid().ToString("D"); + byte[] pfx = MakeAttributeTest(keyName: keyName, friendlyName: alias); + + X509Certificate2 cert = LoadPfxNoFile( + pfx, + keyStorageFlags: X509KeyStorageFlags.DefaultKeySet, + loaderLimits: loaderLimits); + + using (cert) + { + if (preserveAlias) + { + Assert.Equal(alias, cert.FriendlyName); + } + else + { + Assert.Empty(cert.FriendlyName); + } + + using (RSA key = cert.GetRSAPrivateKey()) + { + CngKey cngKey = Assert.IsType(key).Key; + + // Key name was not preserved + Assert.NotEqual(keyName, cngKey.KeyName); + } + } + } + + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void VerifyPreservePreserveProvider(bool preserveProvider, bool preserveName) + { + // This test forces a key creation with CAPI, and verifies that + // PreserveStorageProvider keeps the key in CAPI. Additionally, + // it shows that PreserveKeyName and PreserveStorageProvider are independent. + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + PreserveKeyName = preserveName, + PreserveStorageProvider = preserveProvider, + }; + + string keyName = Guid.NewGuid().ToString("D"); + string alias = Guid.NewGuid().ToString("D"); + byte[] pfx = MakeAttributeTest(keyName: keyName, friendlyName: alias, useCapi: true); + + X509Certificate2 cert = LoadPfxNoFile( + pfx, + keyStorageFlags: X509KeyStorageFlags.DefaultKeySet, + loaderLimits: loaderLimits); + + using (cert) + { + using (RSA key = cert.GetRSAPrivateKey()) + { + CngKey cngKey = Assert.IsType(key).Key; + + if (preserveName) + { + Assert.Equal(keyName, cngKey.KeyName); + } + else + { + Assert.NotEqual(keyName, cngKey.KeyName); + } + + const string CapiProvider = "Microsoft Enhanced RSA and AES Cryptographic Provider"; + + if (preserveProvider) + { + Assert.Equal(CapiProvider, cngKey.Provider.Provider); + } + else + { + Assert.NotEqual(CapiProvider, cngKey.Provider.Provider); + } + } + + // Alias is not preserved + Assert.Empty(cert.FriendlyName); + } + } + + private static byte[] MakeAttributeTest( + string? keyName = null, + string? friendlyName = null, + bool useCapi = false, + [CallerMemberName] string testName = null) + { + CngKey cngKey = null; + RSACryptoServiceProvider rsaCsp = null; + + try + { + RSA key; + + if (keyName is not null) + { + if (useCapi) + { + CspParameters cspParameters = new CspParameters(24) + { + KeyContainerName = keyName, + }; + + rsaCsp = new RSACryptoServiceProvider(2048, cspParameters); + key = rsaCsp; + } + else + { + CngKeyCreationParameters cngParams = new CngKeyCreationParameters + { + ExportPolicy = CngExportPolicies.AllowPlaintextExport, + }; + + cngKey = CngKey.Create(CngAlgorithm.Rsa, keyName, cngParams); + key = new RSACng(cngKey); + } + } + else + { + key = RSA.Create(2048); + } + + CertificateRequest req = new CertificateRequest( + $"CN={testName}", + key, + HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1); + + DateTimeOffset now = DateTimeOffset.UtcNow; + + using (X509Certificate2 cert = req.CreateSelfSigned(now.AddMinutes(-5), now.AddMinutes(5))) + { + if (friendlyName is not null) + { + cert.FriendlyName = friendlyName; + } + + return cert.Export(X509ContentType.Pfx); + } + } + finally + { + cngKey?.Delete(); + + if (rsaCsp is not null) + { + rsaCsp.PersistKeyInCsp = false; + rsaCsp.Dispose(); + } + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs new file mode 100644 index 00000000000000..60d8785679d5ef --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs @@ -0,0 +1,716 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.IO; +using System.IO.MemoryMappedFiles; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + public class X509CertificateLoaderPkcs12Tests_FromByteArray : X509CertificateLoaderPkcs12Tests + { + protected override void NullInputAssert(Action action) => + AssertExtensions.Throws("data", action); + + protected override void EmptyInputAssert(Action action) => + Assert.Throws(action); + + protected override X509Certificate2 LoadPfxCore( + byte[] bytes, + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadPkcs12(bytes, password, keyStorageFlags, loaderLimits); + } + + protected override X509Certificate2 LoadPfxFileOnlyCore( + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadPkcs12( + File.ReadAllBytes(path), + password, + keyStorageFlags, + loaderLimits); + } + + protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) + { + if (bytes is null) + { + contentType = X509ContentType.Unknown; + return false; + } + + contentType = X509Certificate2.GetCertContentType(bytes); + return true; + } + } + + public class X509CertificateLoaderPkcs12Tests_FromByteSpan : X509CertificateLoaderPkcs12Tests + { + protected override void NullInputAssert(Action action) => + Assert.ThrowsAny(action); + + protected override void EmptyInputAssert(Action action) => + Assert.ThrowsAny(action); + + protected override X509Certificate2 LoadPfxCore( + byte[] bytes, + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadPkcs12( + new ReadOnlySpan(bytes), + password.AsSpan(), + keyStorageFlags, + loaderLimits); + } + + protected override X509Certificate2 LoadPfxAtOffsetCore( + byte[] bytes, + int offset, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadPkcs12( + bytes.AsSpan(offset), + password.AsSpan(), + keyStorageFlags, + loaderLimits); + } + + protected override X509Certificate2 LoadPfxFileOnlyCore( + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + // Use a strategy other than File.ReadAllBytes. + + using (FileStream stream = File.OpenRead(path)) + using (MemoryManager manager = MemoryMappedFileMemoryManager.CreateFromFileClamped(stream)) + { + return X509CertificateLoader.LoadPkcs12( + manager.Memory.Span, + password.AsSpan(), + keyStorageFlags, + loaderLimits); + } + } + + protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) + { + contentType = X509ContentType.Unknown; + return false; + } + } + + public class X509CertificateLoaderPkcs12Tests_FromFile : X509CertificateLoaderPkcs12Tests + { + protected override void NullInputAssert(Action action) => + AssertExtensions.Throws("path", action); + + protected override void EmptyInputAssert(Action action) => + AssertExtensions.Throws("path", action); + + protected override X509Certificate2 LoadPfxCore( + byte[] bytes, + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadPkcs12FromFile(path, password, keyStorageFlags, loaderLimits); + } + + protected override X509Certificate2 LoadPfxFileOnlyCore( + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadCertificateFromFile(path); + } + + protected override X509Certificate2 LoadPfxNoFileCore( + byte[] bytes, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + string path = Path.GetTempFileName(); + + try + { + File.WriteAllBytes(path, bytes); + return LoadPfx(bytes, path, password, keyStorageFlags, loaderLimits); + } + finally + { + File.Delete(path); + } + } + + protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) + { + if (path is null) + { + contentType = X509ContentType.Unknown; + return false; + } + + contentType = X509Certificate2.GetCertContentType(path); + return true; + } + } + + public abstract partial class X509CertificateLoaderPkcs12Tests + { + private const int ERROR_INVALID_PASSWORD = -2147024810; + + protected static readonly X509KeyStorageFlags EphemeralIfPossible = +#if NETFRAMEWORK + X509KeyStorageFlags.DefaultKeySet; +#else + PlatformDetection.UsesAppleCrypto ? + X509KeyStorageFlags.DefaultKeySet : + X509KeyStorageFlags.EphemeralKeySet; +#endif + + protected abstract void NullInputAssert(Action action); + protected abstract void EmptyInputAssert(Action action); + + protected X509Certificate2 LoadPfx( + byte[] bytes, + string path, + string password = "", + X509KeyStorageFlags? keyStorageFlags = null, + Pkcs12LoaderLimits loaderLimits = null) + { + return LoadPfxCore( + bytes, + path, + password, + keyStorageFlags.GetValueOrDefault(EphemeralIfPossible), + loaderLimits); + } + + protected abstract X509Certificate2 LoadPfxCore( + byte[] bytes, + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits); + + protected X509Certificate2 LoadPfxFileOnly( + string path, + string password = "", + X509KeyStorageFlags? keyStorageFlags = null, + Pkcs12LoaderLimits loaderLimits = null) + { + return LoadPfxFileOnlyCore( + path, + password, + keyStorageFlags.GetValueOrDefault(EphemeralIfPossible), + loaderLimits); + } + + protected abstract X509Certificate2 LoadPfxFileOnlyCore( + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits); + + protected X509Certificate2 LoadPfxNoFile( + byte[] bytes, + string password = "", + X509KeyStorageFlags? keyStorageFlags = null, + Pkcs12LoaderLimits loaderLimits = null) + { + return LoadPfxNoFileCore( + bytes, + password, + keyStorageFlags.GetValueOrDefault(EphemeralIfPossible), + loaderLimits); + } + + protected virtual X509Certificate2 LoadPfxNoFileCore( + byte[] bytes, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return LoadPfx(bytes, null, password, keyStorageFlags, loaderLimits); + } + + protected X509Certificate2 LoadPfxAtOffset( + byte[] bytes, + int offset, + string password = "", + X509KeyStorageFlags? keyStorageFlags = null, + Pkcs12LoaderLimits loaderLimits = null) + { + return LoadPfxAtOffsetCore( + bytes, + offset, + password, + keyStorageFlags.GetValueOrDefault(EphemeralIfPossible), + loaderLimits); + } + + protected virtual X509Certificate2 LoadPfxAtOffsetCore( + byte[] bytes, + int offset, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return LoadPfxNoFile( + bytes.AsSpan(offset).ToArray(), + password, + keyStorageFlags, + loaderLimits); + } + + protected abstract bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType); + + [Fact] + public void LoadNull() + { + NullInputAssert(() => LoadPfx(null, null, null)); + } + + [Fact] + public void LoadEmpty() + { + EmptyInputAssert(() => LoadPfx(Array.Empty(), string.Empty)); + } + + private void LoadKnownFormat_Fails(byte[] data, string path, X509ContentType contentType) + { + if (PlatformDetection.IsWindows || !X509CertificateLoaderTests.IsWindowsOnlyContentType(contentType)) + { + if (TryGetContentType(data, path, out X509ContentType actualType)) + { + Assert.Equal(contentType, actualType); + } + } + + if (path is null) + { + Assert.ThrowsAny(() => LoadPfxNoFile(data)); + } + else if (data is null) + { + Assert.ThrowsAny(() => LoadPfxFileOnly(path)); + } + else + { + Assert.ThrowsAny(() => LoadPfx(data, path)); + } + } + + [Fact] + public void LoadCertificate_DER_Fails() + { + LoadKnownFormat_Fails(TestData.MsCertificate, TestFiles.MsCertificateDerFile, X509ContentType.Cert); + } + + [Fact] + public void LoadCertificate_PEM_Fails() + { + LoadKnownFormat_Fails(TestData.MsCertificatePemBytes, TestFiles.MsCertificatePemFile, X509ContentType.Cert); + } + + [Fact] + public void LoadPkcs7_BER_Fails() + { + LoadKnownFormat_Fails(TestData.Pkcs7ChainDerBytes, TestFiles.Pkcs7ChainDerFile, X509ContentType.Pkcs7); + } + + [Fact] + public void LoadPkcs7_PEM_Fails() + { + LoadKnownFormat_Fails(TestData.Pkcs7ChainPemBytes, TestFiles.Pkcs7ChainPemFile, X509ContentType.Pkcs7); + } + + [Fact] + public void LoadSerializedCert_Fails() + { + LoadKnownFormat_Fails(TestData.StoreSavedAsSerializedCerData, null, X509ContentType.SerializedCert); + } + + [Fact] + public void LoadSerializedStore_Fails() + { + LoadKnownFormat_Fails(TestData.StoreSavedAsSerializedStoreData, null, X509ContentType.SerializedStore); + } + + [Fact] + public void LoadSignedFile_Fails() + { + LoadKnownFormat_Fails(null, TestFiles.SignedMsuFile, X509ContentType.Authenticode); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoadPfx_Single_WithPassword(bool ignorePrivateKeys) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IgnorePrivateKeys = ignorePrivateKeys, + }; + + X509Certificate2 cert = LoadPfx( + TestData.PfxData, + TestFiles.PfxFile, + TestData.PfxDataPassword, + EphemeralIfPossible, + loaderLimits); + + using (cert) + { + Assert.Equal("CN=MyName", cert.Subject); + Assert.NotEqual(ignorePrivateKeys, cert.HasPrivateKey); + } + } + + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void LoadPfx_Single_NoPassword(bool ignorePrivateKeys, bool useNull) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IgnorePrivateKeys = ignorePrivateKeys, + }; + + string password = useNull ? null : ""; + + X509Certificate2 cert = LoadPfxNoFile( + TestData.PfxWithNoPassword, + password, + EphemeralIfPossible, + loaderLimits); + + using (cert) + { + Assert.Equal("CN=MyName", cert.Subject); + Assert.NotEqual(ignorePrivateKeys, cert.HasPrivateKey); + } + } + + [ConditionalTheory(typeof(PlatformSupport), nameof(PlatformSupport.IsRC2Supported))] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void LoadPfx_Single_NoPassword_AmbiguousDecrypt(bool ignorePrivateKeys, bool useNull) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IgnorePrivateKeys = ignorePrivateKeys, + }; + + string password = useNull ? null : ""; + + X509Certificate2 cert = LoadPfxNoFile( + TestData.MsCertificateExportedToPfx_NullPassword, + password, + EphemeralIfPossible, + loaderLimits); + + using (cert) + { + X509CertificateLoaderTests.AssertRawDataEquals(TestData.MsCertificate, cert); + Assert.False(cert.HasPrivateKey, "cert.HasPrivateKey"); + } + } + + [Fact] + public void LoadPfx_Single_WrongPassword() + { + CryptographicException ex = Assert.Throws( + () => LoadPfx(TestData.PfxData, TestFiles.PfxFile, "asdf")); + + Assert.Contains("password", ex.Message); + Assert.Equal(ERROR_INVALID_PASSWORD, ex.HResult); + } + + [Fact] + public void LoadPfx_Single_EmptyPassword_WithWrongPassword() + { + CryptographicException ex = Assert.Throws( + () => LoadPfxNoFile(TestData.PfxWithNoPassword, "asdf")); + + Assert.Contains("password", ex.Message); + Assert.Equal(ERROR_INVALID_PASSWORD, ex.HResult); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoadPfx_Single_EmptyPassword_NoMac(bool useEmpty) + { + string password = useEmpty ? "" : null; + + X509Certificate2 cert = LoadPfxNoFile( + TestData.Pkcs12OpenSslOneCertDefaultNoMac, + password, + EphemeralIfPossible); + + using (cert) + { + Assert.Equal("CN=test", cert.Subject); + } + } + + [Fact] + public void LoadPfx_WithTrailingData() + { + byte[] data = TestData.PfxWithNoPassword; + Array.Resize(ref data, data.Length + 10); + + using (X509Certificate2 cert = LoadPfxNoFile(data)) + { + Assert.Equal("CN=MyName", cert.Subject); + } + } + + [Fact] + public void LoadPfx_Empty() + { + AssertExtensions.Throws( + () => LoadPfxNoFile(TestData.EmptyPfx), + "The provided PFX data contains no certificates."); + } + + private void LoadPfx_VerifyLimit( + string propertyTested, + bool fail, + byte[] bytes, + string path, + string password, + Pkcs12LoaderLimits loaderLimits) + { + Func test; + + if (bytes is null) + { + test = () => LoadPfxFileOnly(path, password, EphemeralIfPossible, loaderLimits); + } + else if (path is null) + { + test = () => LoadPfxNoFile(bytes, password, EphemeralIfPossible, loaderLimits); + } + else + { + test = () => LoadPfx(bytes, path, password, EphemeralIfPossible, loaderLimits); + } + + if (fail) + { + Pkcs12LoadLimitExceededException ex = + AssertExtensions.Throws(() => test()); + + Assert.Contains(propertyTested, ex.Message); + } + else + { + // Assert.NoThrow + test().Dispose(); + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoadPfx_VerifyMacIterationLimit(bool failLimit) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + MacIterationLimit = failLimit ? 1999 : 2000, + }; + + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.MacIterationLimit), + failLimit, + TestData.PfxData, + TestFiles.PfxFile, + TestData.PfxDataPassword, + loaderLimits); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoadPfx_VerifyKdfIterationLimit(bool failLimit) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IndividualKdfIterationLimit = failLimit ? 1999 : 2000, + }; + + // Both 1999 and 2000 will fail, because the key uses 2001. + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.IndividualKdfIterationLimit), + fail: true, + TestData.MixedIterationsPfx, + null, + TestData.PlaceholderPw, + loaderLimits); + + loaderLimits.IgnorePrivateKeys = true; + + // Now that we're ignoring the key, 1999 will fail, 2000 will pass. + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.IndividualKdfIterationLimit), + failLimit, + TestData.MixedIterationsPfx, + null, + TestData.PlaceholderPw, + loaderLimits); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoadPfx_VerifyTotalKdfIterationLimit(bool failLimit) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + TotalKdfIterationLimit = failLimit ? 3999 : 4000, + }; + + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.TotalKdfIterationLimit), + failLimit, + TestData.PfxData, + TestFiles.PfxFile, + TestData.PfxDataPassword, + loaderLimits); + } + + [Theory] + [InlineData(null)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + public void LoadPfx_VerifyCertificateLimit(int? certLimit) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + MaxCertificates = certLimit, + }; + + bool expectFailure = certLimit.GetValueOrDefault(int.MaxValue) < 3; + + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.MaxCertificates), + expectFailure, + TestData.ChainPfxBytes, + TestFiles.ChainPfxFile, + TestData.ChainPfxPassword, + loaderLimits); + } + + [Theory] + [InlineData(null)] + [InlineData(0)] + [InlineData(1)] + [InlineData(4)] + public void LoadPfx_VerifyKeysLimit(int? keysLimit) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + MaxKeys = keysLimit, + }; + + bool expectFailure = keysLimit.GetValueOrDefault(int.MaxValue) < 1; + + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.MaxKeys), + expectFailure, + TestData.ChainPfxBytes, + TestFiles.ChainPfxFile, + TestData.ChainPfxPassword, + loaderLimits); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void LoadPfx_VerifyIgnoreEncryptedSafes(bool ignoreEncrypted) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IgnoreEncryptedAuthSafes = ignoreEncrypted, + }; + + string expectedSubject = ignoreEncrypted ? + "CN=Plaintext Test Certificate, OU=.NET Libraries, O=Microsoft Corporation" : + "CN=Encrypted Test Certificate, OU=.NET Libraries, O=Microsoft Corporation"; + + X509Certificate2 cert = LoadPfxNoFile( + TestData.TwoCertsPfx_OneEncrypted, + TestData.PlaceholderPw, + default, + loaderLimits); + + using (cert) + { + Assert.Equal(expectedSubject, cert.Subject); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void LoadPfx_VerifyIgnoreEncryptedSafes_EmptyIfIgnored(bool ignoreEncrypted) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IgnoreEncryptedAuthSafes = ignoreEncrypted, + }; + + if (ignoreEncrypted) + { + AssertExtensions.Throws( + () => LoadPfx( + TestData.PfxData, + TestFiles.PfxFile, + TestData.PfxDataPassword, + default, + loaderLimits), + "The provided PFX data contains no certificates."); + } + else + { + X509Certificate2 cert = LoadPfx( + TestData.PfxData, + TestFiles.PfxFile, + TestData.PfxDataPassword, + default, + loaderLimits); + + using (cert) + { + Assert.Equal("CN=MyName", cert.Subject); + } + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs new file mode 100644 index 00000000000000..3fef8720365fa1 --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs @@ -0,0 +1,284 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.IO; +using System.IO.MemoryMappedFiles; +using Xunit; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + public class X509CertificateLoaderTests_FromByteArray : X509CertificateLoaderTests + { + protected override void NullInputAssert(Action action) => + AssertExtensions.Throws("data", action); + + protected override void EmptyInputAssert(Action action) => + Assert.ThrowsAny(action); + + protected override X509Certificate2 LoadCertificate(byte[] bytes, string path) => + X509CertificateLoader.LoadCertificate(bytes); + + protected override X509Certificate2 LoadCertificateFileOnly(string path) => + X509CertificateLoader.LoadCertificate(File.ReadAllBytes(path)); + + protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) + { + if (bytes is null) + { + contentType = X509ContentType.Unknown; + return false; + } + + contentType = X509Certificate2.GetCertContentType(bytes); + return true; + } + } + + public class X509CertificateLoaderTests_FromByteSpan : X509CertificateLoaderTests + { + protected override void NullInputAssert(Action action) => + Assert.ThrowsAny(action); + + protected override void EmptyInputAssert(Action action) => + Assert.ThrowsAny(action); + + protected override X509Certificate2 LoadCertificate(byte[] bytes, string path) => + X509CertificateLoader.LoadCertificate(new ReadOnlySpan(bytes)); + + protected override X509Certificate2 LoadCertificateAtOffset(byte[] bytes, int offset) => + X509CertificateLoader.LoadCertificate(bytes.AsSpan(offset)); + + protected override X509Certificate2 LoadCertificateFileOnly(string path) + { + // Use a strategy other than File.ReadAllBytes. + using (FileStream stream = File.OpenRead(path)) + using (MemoryManager manager = MemoryMappedFileMemoryManager.CreateFromFileClamped(stream)) + { + return X509CertificateLoader.LoadCertificate(manager.Memory.Span); + } + } + + protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) + { + contentType = X509ContentType.Unknown; + return false; + } + } + + public class X509CertificateLoaderTests_FromFile : X509CertificateLoaderTests + { + protected override void NullInputAssert(Action action) => + AssertExtensions.Throws("path", action); + + protected override void EmptyInputAssert(Action action) => + AssertExtensions.Throws("path", action); + + protected override X509Certificate2 LoadCertificate(byte[] bytes, string path) => + X509CertificateLoader.LoadCertificateFromFile(path); + + protected override X509Certificate2 LoadCertificateFileOnly(string path) => + X509CertificateLoader.LoadCertificateFromFile(path); + + protected override X509Certificate2 LoadCertificateNoFile(byte[] bytes) + { + string path = Path.GetTempFileName(); + + try + { + File.WriteAllBytes(path, bytes); + return LoadCertificate(bytes, path); + } + finally + { + File.Delete(path); + } + } + + protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) + { + if (path is null) + { + contentType = X509ContentType.Unknown; + return false; + } + + contentType = X509Certificate2.GetCertContentType(path); + return true; + } + } + + public abstract class X509CertificateLoaderTests + { + protected abstract void NullInputAssert(Action action); + protected abstract void EmptyInputAssert(Action action); + protected abstract X509Certificate2 LoadCertificate(byte[] bytes, string path); + protected abstract X509Certificate2 LoadCertificateFileOnly(string path); + + protected virtual X509Certificate2 LoadCertificateNoFile(byte[] bytes) => + LoadCertificate(bytes, null); + + protected virtual X509Certificate2 LoadCertificateAtOffset(byte[] bytes, int offset) => + LoadCertificateNoFile(bytes.AsSpan(offset).ToArray()); + + protected abstract bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType); + + [Fact] + public void LoadNull() + { + NullInputAssert(() => LoadCertificate(null, null)); + } + + [Fact] + public void LoadEmpty() + { + EmptyInputAssert(() => LoadCertificate(Array.Empty(), string.Empty)); + } + + private void LoadKnownFormat_Fails(byte[] data, string path, X509ContentType contentType) + { + if (PlatformDetection.IsWindows || !IsWindowsOnlyContentType(contentType)) + { + if (TryGetContentType(data, path, out X509ContentType actualType)) + { + Assert.Equal(contentType, actualType); + } + } + + if (path is null) + { + Assert.ThrowsAny(() => LoadCertificateNoFile(data)); + } + else if (data is null) + { + Assert.ThrowsAny(() => LoadCertificateFileOnly(path)); + } + else + { + Assert.ThrowsAny(() => LoadCertificate(data, path)); + } + } + + [Fact] + public void LoadCertificate_DER() + { + using (X509Certificate2 cert = LoadCertificate(TestData.MsCertificate, TestFiles.MsCertificateDerFile)) + { + Assert.NotNull(cert); + AssertRawDataEquals(TestData.MsCertificate, cert); + } + } + + [Fact] + public void LoadCertificate_PEM() + { + using (X509Certificate2 cert = LoadCertificate(TestData.MsCertificatePemBytes, TestFiles.MsCertificatePemFile)) + { + Assert.NotNull(cert); + AssertRawDataEquals(TestData.MsCertificate, cert); + } + } + + [Fact] + public void LoadPkcs7_BER_Fails() + { + LoadKnownFormat_Fails(TestData.Pkcs7ChainDerBytes, TestFiles.Pkcs7ChainDerFile, X509ContentType.Pkcs7); + } + + [Fact] + public void LoadPkcs7_PEM_Fails() + { + LoadKnownFormat_Fails(TestData.Pkcs7ChainPemBytes, TestFiles.Pkcs7ChainPemFile, X509ContentType.Pkcs7); + } + + [Fact] + public void LoadPfx_NeedsPassword_Fails() + { + LoadKnownFormat_Fails(TestData.PfxData, TestFiles.PfxFile, X509ContentType.Pfx); + } + + [Fact] + public void LoadPfx_NoPasswordNeeded_Fails() + { + LoadKnownFormat_Fails(TestData.PfxWithNoPassword, null, X509ContentType.Pfx); + } + + [Fact] + public void LoadSignedFile_Fails() + { + LoadKnownFormat_Fails(null, TestFiles.SignedMsuFile, X509ContentType.Authenticode); + } + + [Fact] + public void LoadSerializedCert_Fails() + { + LoadKnownFormat_Fails(TestData.StoreSavedAsSerializedCerData, null, X509ContentType.SerializedCert); + } + + [Fact] + public void LoadSerializedStore_Fails() + { + LoadKnownFormat_Fails(TestData.StoreSavedAsSerializedStoreData, null, X509ContentType.SerializedStore); + } + + [Fact] + public void LoadNestedCertificates() + { + using (X509Certificate2 cert = LoadCertificateNoFile(TestData.NestedCertificates)) + { + Assert.Equal("CN=outer", cert.Subject); + + X509Extension ext = cert.Extensions["0.0.1"]; + + using (X509Certificate2 inner = LoadCertificateNoFile(ext.RawData)) + { + Assert.Equal("CN=inner", inner.Subject); + } + } + } + + [Fact] + [ActiveIssue("macOS seems to not like the PEM post-EB not followed by a newline or EOF", TestPlatforms.OSX)] + public void LoadCertificate_WithTrailingData() + { + // Find the PEM-encoded certificate embedded within NestedCertificates, and + // load only that portion of the data. + byte[] data = TestData.NestedCertificates; + + // The offset could be hard-coded, but it's not expensive to do the find and saves on test maintenance. + Span needle = stackalloc byte[] { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D }; + int offset = data.AsSpan().IndexOf(needle); + + using (X509Certificate2 cert = LoadCertificateAtOffset(data, offset)) + { + Assert.Equal("CN=inner", cert.Subject); + } + } + + [Fact] + public void LoadCertificate_DER_WithTrailingData() + { + byte[] data = TestData.MsCertificate; + Array.Resize(ref data, data.Length + 21); + + using (X509Certificate2 cert = LoadCertificateNoFile(data)) + { + AssertRawDataEquals(TestData.MsCertificate, cert); + } + } + + internal static void AssertRawDataEquals(byte[] expected, X509Certificate2 cert) + { +#if NETCOREAPP + AssertExtensions.SequenceEqual(TestData.MsCertificate, cert.RawDataMemory.Span); +#else + AssertExtensions.SequenceEqual(TestData.MsCertificate, cert.RawData); +#endif + } + + internal static bool IsWindowsOnlyContentType(X509ContentType contentType) + { + return contentType is X509ContentType.Authenticode or X509ContentType.SerializedStore or X509ContentType.SerializedCert; + } + } +} diff --git a/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.Forwards.cs b/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.Forwards.cs index 5f6e0fbf895762..b7cb4dae61d56a 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.Forwards.cs +++ b/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.Forwards.cs @@ -1,4 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.SP800108HmacCounterKdf))] +#endif +#if NET9_0_OR_GREATER +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.X509Certificates.Pkcs12LoadLimitExceededException))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.X509Certificates.X509CertificateLoader))] +#endif diff --git a/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.cs b/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.cs index 83dc7a9f3fa30d..a3b9cba6a80087 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.cs +++ b/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.cs @@ -4,6 +4,7 @@ // Changes to this file must follow the https://aka.ms/api-review process. // ------------------------------------------------------------------------------ +#if NETFRAMEWORK || NETSTANDARD namespace System.Security.Cryptography { public sealed partial class SP800108HmacCounterKdf : System.IDisposable @@ -25,3 +26,50 @@ public void DeriveKey(System.ReadOnlySpan label, System.ReadOnlySpan public void Dispose() { } } } +#endif +#if NETFRAMEWORK || NETSTANDARD || NET8_0 +namespace System.Security.Cryptography.X509Certificates +{ + public sealed partial class Pkcs12LoaderLimits + { + public Pkcs12LoaderLimits() { } + public Pkcs12LoaderLimits(System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits copyFrom) { } + public static System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits DangerousNoLimits { get { throw null; } } + public static System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits Defaults { get { throw null; } } + public bool IgnoreEncryptedAuthSafes { get { throw null; } set { } } + public bool IgnorePrivateKeys { get { throw null; } set { } } + public int? IndividualKdfIterationLimit { get { throw null; } set { } } + public bool IsReadOnly { get { throw null; } } + public int? MacIterationLimit { get { throw null; } set { } } + public int? MaxCertificates { get { throw null; } set { } } + public int? MaxKeys { get { throw null; } set { } } + public bool PreserveCertificateAlias { get { throw null; } set { } } + public bool PreserveKeyName { get { throw null; } set { } } + public bool PreserveStorageProvider { get { throw null; } set { } } + public bool PreserveUnknownAttributes { get { throw null; } set { } } + public int? TotalKdfIterationLimit { get { throw null; } set { } } + public void MakeReadOnly() { } + } + public sealed partial class Pkcs12LoadLimitExceededException : System.Security.Cryptography.CryptographicException + { + public Pkcs12LoadLimitExceededException(string propertyName) { } + } +#if NETCOREAPP + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif + public static partial class X509CertificateLoader + { + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadCertificate(byte[] data) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadCertificate(System.ReadOnlySpan data) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadCertificateFromFile(string path) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadPkcs12(byte[] data, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadPkcs12(System.ReadOnlySpan data, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2Collection LoadPkcs12Collection(byte[] data, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2Collection LoadPkcs12Collection(System.ReadOnlySpan data, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2Collection LoadPkcs12CollectionFromFile(string path, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2Collection LoadPkcs12CollectionFromFile(string path, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadPkcs12FromFile(string path, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadPkcs12FromFile(string path, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + } +} +#endif diff --git a/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.csproj b/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.csproj index ef8ae599f15b4c..d6c00334523640 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.csproj +++ b/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.csproj @@ -1,14 +1,11 @@ - netstandard2.0;$(NetFrameworkMinimum);$(NetCoreAppMinimum) + netstandard2.0;$(NetFrameworkMinimum);$(NetCoreAppMinimum);$(NetCoreAppCurrent) - + - - - diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj b/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj index 2c71f52147943a..88c60b8f42ede1 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj @@ -1,7 +1,7 @@ - netstandard2.0;$(NetFrameworkMinimum);$(NetCoreAppMinimum) + netstandard2.0;$(NetFrameworkMinimum);$(NetCoreAppMinimum);$(NetCoreAppCurrent) true false true @@ -14,8 +14,20 @@ System.Security.Cryptography.SP800108HmacCounterKdf true - true + true + + true + false + true + + + + + + + Link="Common\Interop\Windows\BCrypt\Interop.Blobs.cs" /> - + + + Common\System\Security\Cryptography\Asn1\AlgorithmIdentifierAsn.xml + + + Common\System\Security\Cryptography\Asn1\AlgorithmIdentifierAsn.xml.cs + Common\System\Security\Cryptography\Asn1\AlgorithmIdentifierAsn.xml + + + Common\System\Security\Cryptography\Asn1\AlgorithmIdentifierAsn.manual.cs + Common\System\Security\Cryptography\Asn1\AlgorithmIdentifierAsn.xml + + + Common\System\Security\Cryptography\Asn1\AttributeAsn.xml + + + Common\System\Security\Cryptography\Asn1\AttributeAsn.xml.cs + Common\System\Security\Cryptography\Asn1\AttributeAsn.xml + + + Common\System\Security\Cryptography\Asn1\AttributeAsn.manual.cs + Common\System\Security\Cryptography\Asn1\AttributeAsn.xml + + + Common\System\Security\Cryptography\Asn1\DigestInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\DigestInfoAsn.xml.cs + Common\System\Security\Cryptography\Asn1\DigestInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\EncryptedPrivateKeyInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\EncryptedPrivateKeyInfoAsn.xml.cs + Common\System\Security\Cryptography\Asn1\EncryptedPrivateKeyInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\PBEParameter.xml + + + Common\System\Security\Cryptography\Asn1\PBEParameter.xml.cs + Common\System\Security\Cryptography\Asn1\PBEParameter.xml + + + Common\System\Security\Cryptography\Asn1\PBES2Params.xml + + + Common\System\Security\Cryptography\Asn1\PBES2Params.xml.cs + Common\System\Security\Cryptography\Asn1\PBES2Params.xml + + + Common\System\Security\Cryptography\Asn1\Pbkdf2Params.xml + + + Common\System\Security\Cryptography\Asn1\Pbkdf2Params.xml.cs + Common\System\Security\Cryptography\Asn1\Pbkdf2Params.xml + + + Common\System\Security\Cryptography\Asn1\Pbkdf2SaltChoice.xml + + + Common\System\Security\Cryptography\Asn1\Pbkdf2SaltChoice.xml.cs + Common\System\Security\Cryptography\Asn1\Pbkdf2SaltChoice.xml + + + Common\System\Security\Cryptography\Asn1\PrivateKeyInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\PrivateKeyInfoAsn.xml.cs + Common\System\Security\Cryptography\Asn1\PrivateKeyInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\Rc2CbcParameters.xml + + + Common\System\Security\Cryptography\Asn1\Rc2CbcParameters.xml.cs + Common\System\Security\Cryptography\Asn1\Rc2CbcParameters.xml + + + Common\System\Security\Cryptography\Asn1\Rc2CbcParameters.manual.cs + Common\System\Security\Cryptography\Asn1\Rc2CbcParameters.xml + + + Common\System\Security\Cryptography\Asn1\SubjectPublicKeyInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\SubjectPublicKeyInfoAsn.xml.cs + Common\System\Security\Cryptography\Asn1\SubjectPublicKeyInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs12\CertBagAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs12\CertBagAsn.xml.cs + Common\System\Security\Cryptography\Asn1\Pkcs12\CertBagAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs12\MacData.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs12\MacData.xml.cs + Common\System\Security\Cryptography\Asn1\Pkcs12\MacData.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs12\PfxAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs12\PfxAsn.manual.cs + Common\System\Security\Cryptography\Asn1\Pkcs12\PfxAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs12\PfxAsn.xml.cs + Common\System\Security\Cryptography\Asn1\Pkcs12\PfxAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs12\SafeBagAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs12\SafeBagAsn.xml.cs + Common\System\Security\Cryptography\Asn1\Pkcs12\SafeBagAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs7\ContentInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs7\ContentInfoAsn.xml.cs + Common\System\Security\Cryptography\Asn1\Pkcs7\ContentInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs7\EncryptedContentInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs7\EncryptedContentInfoAsn.xml.cs + Common\System\Security\Cryptography\Asn1\Pkcs7\EncryptedContentInfoAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs7\EncryptedDataAsn.xml + + + Common\System\Security\Cryptography\Asn1\Pkcs7\EncryptedDataAsn.xml.cs + Common\System\Security\Cryptography\Asn1\Pkcs7\EncryptedDataAsn.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs new file mode 100644 index 00000000000000..918c1cbd4ea507 --- /dev/null +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Security; +using Internal.Cryptography; + +namespace Microsoft.Win32.SafeHandles +{ + /// + /// Wrap a string- or SecureString-based object. A null value indicates IntPtr.Zero should be used. + /// + internal sealed partial class SafePasswordHandle : SafeHandleZeroOrMinusOneIsInvalid + { + internal int Length { get; private set; } + + /// + /// This is used to track if a password was explicitly provided. + /// A null/empty password is a valid password. + /// + internal bool PasswordProvided { get; } + + public SafePasswordHandle(string? password, bool passwordProvided) + : base(ownsHandle: true) + { + if (password != null) + { + handle = Marshal.StringToHGlobalUni(password); + Length = password.Length; + } + + PasswordProvided = passwordProvided; + } + + public SafePasswordHandle(ReadOnlySpan password, bool passwordProvided) + : base(ownsHandle: true) + { + // "".AsSpan() is not default, so this is compat for "null tries NULL first". + if (!password.ContainsNull()) + { + int spanLen; + + checked + { + spanLen = password.Length + 1; + handle = Marshal.AllocHGlobal(spanLen * sizeof(char)); + } + + unsafe + { + Span dest = new Span((void*)handle, spanLen); + password.CopyTo(dest); + dest[password.Length] = '\0'; + } + + Length = password.Length; + } + + PasswordProvided = passwordProvided; + } + + public SafePasswordHandle(SecureString? password, bool passwordProvided) + : base(ownsHandle: true) + { + if (password != null) + { + handle = Marshal.SecureStringToGlobalAllocUnicode(password); + Length = password.Length; + } + + PasswordProvided = passwordProvided; + } + + protected override bool ReleaseHandle() + { + Marshal.ZeroFreeGlobalAllocUnicode(handle); + SetHandle((IntPtr)(-1)); + Length = 0; + return true; + } + + internal ReadOnlySpan DangerousGetSpan() + { + if (IsInvalid) + { + return default; + } + + unsafe + { + return new ReadOnlySpan((char*)handle, Length); + } + } + } +} diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/Resources/Strings.resx b/src/libraries/Microsoft.Bcl.Cryptography/src/Resources/Strings.resx index 85b7fab4f321a6..02e47cb969f047 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/Resources/Strings.resx +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/Resources/Strings.resx @@ -57,6 +57,18 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Error occurred during a cryptographic operation. + + + Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. + + + Value was invalid. + + + {0} ('{1}') must be a non-negative and non-zero value. + Non-negative number required. @@ -66,7 +78,40 @@ The value cannot be an empty string. + + The KDF for algorithm '{0}' requires a char-based password input. + + + Algorithm '{0}' is not supported on this platform. + + + ASN1 corrupted data. + + + The hash algorithm name cannot be null or empty. + + + Key is not a valid public or private key. + + + The certificate data cannot be read with the provided password, the password may be incorrect. + + + The provided PFX data contains no certificates. + + + The EncryptedPrivateKeyInfo structure was decoded but was not successfully interpreted, the password may be incorrect. + + + The algorithm identified by '{0}' is unknown, not valid for the requested usage, or was not handled. + '{0}' is not a known hash algorithm. + + The PKCS#12/PFX violated the '{0}' limit. + + + This Pkcs12LoaderLimits object has been made read-only and can no longer be modified. + diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/Helpers.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/Helpers.cs new file mode 100644 index 00000000000000..0d2af61b8d6347 --- /dev/null +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/Helpers.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Formats.Asn1; +using System.Security.Cryptography; + +namespace Internal.Cryptography +{ + internal static partial class Helpers + { + internal static ReadOnlyMemory DecodeOctetStringAsMemory(ReadOnlyMemory encodedOctetString) + { + try + { + ReadOnlySpan input = encodedOctetString.Span; + + if (AsnDecoder.TryReadPrimitiveOctetString( + input, + AsnEncodingRules.BER, + out ReadOnlySpan primitive, + out int consumed)) + { + if (consumed != input.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + if (input.Overlaps(primitive, out int offset)) + { + return encodedOctetString.Slice(offset, primitive.Length); + } + + Debug.Fail("input.Overlaps(primitive) failed after TryReadPrimitiveOctetString succeeded"); + } + + byte[] ret = AsnDecoder.ReadOctetString(input, AsnEncodingRules.BER, out consumed); + + if (consumed != input.Length) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return ret; + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + } +} diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/NetStandardShims.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/NetStandardShims.cs index 4dc6e7b867d7e9..9766bcdeadb65b 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/NetStandardShims.cs +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/NetStandardShims.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; @@ -8,6 +9,19 @@ namespace System.Security.Cryptography { internal static class NetStandardShims { + internal static unsafe int GetByteCount(this Encoding encoding, ReadOnlySpan str) + { + if (str.IsEmpty) + { + return 0; + } + + fixed (char* pStr = str) + { + return encoding.GetByteCount(pStr, str.Length); + } + } + internal static unsafe int GetBytes(this Encoding encoding, ReadOnlySpan str, Span destination) { if (str.IsEmpty) @@ -21,6 +35,96 @@ internal static unsafe int GetBytes(this Encoding encoding, ReadOnlySpan s return encoding.GetBytes(pStr, str.Length, pDestination, destination.Length); } } + + internal static void ReadExactly(this System.IO.Stream stream, Span buffer) => + ReadAtLeast(stream, buffer, buffer.Length, throwOnEndOfStream: true); + + internal static int ReadAtLeast( + this System.IO.Stream stream, + Span buffer, + int minimumBytes, + bool throwOnEndOfStream = true) + { + if (minimumBytes > buffer.Length) + throw new ArgumentOutOfRangeException(nameof(minimumBytes)); + + byte[] rented = CryptoPool.Rent(Math.Min(minimumBytes, 32768)); + int max = 0; + int spaceRemaining = buffer.Length; + int totalRead = 0; + + while (totalRead < minimumBytes) + { + int read = stream.Read(rented, 0, Math.Min(spaceRemaining, rented.Length)); + max = Math.Max(read, max); + + if (read == 0) + { + CryptoPool.Return(rented, max); + + if (throwOnEndOfStream) + { + throw new System.IO.EndOfStreamException(); + } + + return totalRead; + } + + spaceRemaining -= read; + totalRead += read; + rented.AsSpan(0, read).CopyTo(buffer); + buffer = buffer.Slice(read); + } + + CryptoPool.Return(rented, max); + return totalRead; + } + + internal static void AppendData(this IncrementalHash hash, ReadOnlySpan data) + { + byte[] rented = CryptoPool.Rent(data.Length); + + try + { + data.CopyTo(rented); + hash.AppendData(rented, 0, data.Length); + } + finally + { + CryptoPool.Return(rented, data.Length); + } + } + +#if NETSTANDARD2_0 + internal static bool TryGetHashAndReset( + this IncrementalHash hash, + Span destination, + out int bytesWritten) + { + int hashSize = hash.AlgorithmName.Name switch + { + nameof(HashAlgorithmName.MD5) => 128 >> 3, + nameof(HashAlgorithmName.SHA1) => 160 >> 3, + nameof(HashAlgorithmName.SHA256) => 256 >> 3, + nameof(HashAlgorithmName.SHA384) => 384 >> 3, + nameof(HashAlgorithmName.SHA512) => 512 >> 3, + _ => throw new CryptographicException(), + }; + + if (destination.Length < hashSize) + { + bytesWritten = 0; + return false; + } + + byte[] actual = hash.GetHashAndReset(); + Debug.Assert(actual.Length == hashSize); + + actual.AsSpan().CopyTo(destination); + bytesWritten = actual.Length; + return true; + } +#endif } internal static class CryptographicOperations @@ -30,5 +134,43 @@ internal static void ZeroMemory(Span buffer) { buffer.Clear(); } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + internal static bool FixedTimeEquals(ReadOnlySpan left, ReadOnlySpan right) + { + // NoOptimization because we want this method to be exactly as non-short-circuiting + // as written. + // + // NoInlining because the NoOptimization would get lost if the method got inlined. + + if (left.Length != right.Length) + { + return false; + } + + int length = left.Length; + int accum = 0; + + for (int i = 0; i < length; i++) + { + accum |= left[i] - right[i]; + } + + return accum == 0; + } + } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] + internal sealed class CallerArgumentExpressionAttribute : Attribute + { + public CallerArgumentExpressionAttribute(string parameterName) + { + ParameterName = parameterName; + } + + public string ParameterName { get; } } } diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeEncryptionAlgorithm.netstandard.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeEncryptionAlgorithm.netstandard.cs new file mode 100644 index 00000000000000..2669c9378f0677 --- /dev/null +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeEncryptionAlgorithm.netstandard.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Security.Cryptography +{ +#if !NETCOREAPP + internal enum PbeEncryptionAlgorithm + { + Unknown = 0, + Aes128Cbc = 1, + Aes192Cbc = 2, + Aes256Cbc = 3, + TripleDes3KeyPkcs12 = 4, + } +#endif +} diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeParameters.netstandard.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeParameters.netstandard.cs new file mode 100644 index 00000000000000..197baaafb9ed99 --- /dev/null +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeParameters.netstandard.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Security.Cryptography +{ +#if !NETCOREAPP + internal sealed class PbeParameters + { + public PbeEncryptionAlgorithm EncryptionAlgorithm { get; } + public HashAlgorithmName HashAlgorithm { get; } + public int IterationCount { get; } + + public PbeParameters( + PbeEncryptionAlgorithm encryptionAlgorithm, + HashAlgorithmName hashAlgorithm, + int iterationCount) + { + if (iterationCount <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(iterationCount), + iterationCount, + SR.Format( + SR.ArgumentOutOfRange_Generic_MustBeNonNegativeNonZero, + nameof(iterationCount), + iterationCount)); + } + + EncryptionAlgorithm = encryptionAlgorithm; + HashAlgorithm = hashAlgorithm; + IterationCount = iterationCount; + } + } +#endif +} diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.ProcessedPkcs12.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.ProcessedPkcs12.cs new file mode 100644 index 00000000000000..25a778d09a194b --- /dev/null +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.ProcessedPkcs12.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + static partial void LoadPkcs12NoLimits( + ReadOnlyMemory data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + ref Pkcs12Return earlyReturn) + { + string hydrated = password.ToString(); + + if (MemoryMarshal.TryGetArray(data, out ArraySegment segment) && segment.Offset == 0) + { + Debug.Assert(segment.Array is not null); + earlyReturn = new X509Certificate2(segment.Array, hydrated, keyStorageFlags); + } + else + { + byte[] rented = CryptoPool.Rent(data.Length); + data.Span.CopyTo(rented); + + try + { + earlyReturn = new X509Certificate2(rented, hydrated, keyStorageFlags); + } + finally + { + CryptoPool.Return(rented, data.Length); + } + } + } + + static partial void LoadPkcs12NoLimits( + ReadOnlyMemory data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + ref X509Certificate2Collection? earlyReturn) + { + string hydrated = password.ToString(); + X509Certificate2Collection coll = new X509Certificate2Collection(); + + if (MemoryMarshal.TryGetArray(data, out ArraySegment segment) && segment.Offset == 0) + { + Debug.Assert(segment.Array is not null); + coll.Import(segment.Array, hydrated, keyStorageFlags); + } + else + { + byte[] rented = CryptoPool.Rent(data.Length); + data.Span.CopyTo(rented); + + try + { + coll.Import(rented, hydrated, keyStorageFlags); + } + finally + { + CryptoPool.Return(rented, data.Length); + } + } + + earlyReturn = coll; + } + + private static partial Pkcs12Return LoadPkcs12( + ref BagState bagState, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags) + { + ArraySegment reassembled = bagState.ToPfx(password); + + try + { + Debug.Assert(reassembled.Array is not null); + Debug.Assert(reassembled.Offset == 0); + + return new X509Certificate2(reassembled.Array, password.ToString(), keyStorageFlags); + } + finally + { + CryptoPool.Return(reassembled); + } + } + + private static partial X509Certificate2Collection LoadPkcs12Collection( + ref BagState bagState, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags) + { + ArraySegment reassembled = bagState.ToPfx(password); + X509Certificate2Collection coll = new X509Certificate2Collection(); + + try + { + Debug.Assert(reassembled.Array is not null); + Debug.Assert(reassembled.Offset == 0); + + coll.Import(reassembled.Array, password.ToString(), keyStorageFlags); + return coll; + } + finally + { + CryptoPool.Return(reassembled); + } + } + + private readonly partial struct Pkcs12Return + { + private readonly X509Certificate2 _cert; + + internal Pkcs12Return(X509Certificate2 cert) + { + _cert = cert; + } + + internal partial bool HasValue() => _cert is not null; + internal partial X509Certificate2 ToCertificate() => _cert; + + public static implicit operator Pkcs12Return(X509Certificate2 cert) + { + return new Pkcs12Return(cert); + } + } + } +} diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netfx.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netfx.cs new file mode 100644 index 00000000000000..649979d0186bed --- /dev/null +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netfx.cs @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using Internal.Cryptography; + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + public static partial X509Certificate2 LoadCertificate(byte[] data) + { + ThrowIfNull(data); + + return LoadCertificate(new ReadOnlySpan(data)); + } + + public static partial X509Certificate2 LoadCertificate(ReadOnlySpan data) + { + unsafe + { + fixed (byte* dataPtr = data) + { + Interop.Crypt32.DATA_BLOB blob = new Interop.Crypt32.DATA_BLOB( + (IntPtr)dataPtr, + (uint)data.Length); + + return LoadCertificate( + Interop.Crypt32.CertQueryObjectType.CERT_QUERY_OBJECT_BLOB, + &blob); + } + } + } + + public static partial X509Certificate2 LoadCertificateFromFile(string path) + { + ThrowIfNullOrEmpty(path); + + unsafe + { + fixed (char* pathPtr = path) + { + return LoadCertificate( + Interop.Crypt32.CertQueryObjectType.CERT_QUERY_OBJECT_FILE, + pathPtr); + } + } + } + + private static unsafe X509Certificate2 LoadCertificate( + Interop.Crypt32.CertQueryObjectType objectType, + void* pvObject) + { + Debug.Assert(objectType != 0); + Debug.Assert(pvObject != (void*)0); + + const Interop.Crypt32.ContentType ContentType = + Interop.Crypt32.ContentType.CERT_QUERY_CONTENT_CERT; + const Interop.Crypt32.ExpectedContentTypeFlags ExpectedContentType = + Interop.Crypt32.ExpectedContentTypeFlags.CERT_QUERY_CONTENT_FLAG_CERT; + + IntPtr certHandle = IntPtr.Zero; + + try + { + bool loaded = Interop.Crypt32.CryptQueryObject( + objectType, + pvObject, + ExpectedContentType, + Interop.Crypt32.ExpectedFormatTypeFlags.CERT_QUERY_FORMAT_FLAG_ALL, + dwFlags: 0, + pdwMsgAndCertEncodingType: IntPtr.Zero, + out Interop.Crypt32.ContentType actualType, + pdwFormatType: IntPtr.Zero, + phCertStore: IntPtr.Zero, + phMsg: IntPtr.Zero, + out certHandle); + + if (!loaded) + { + throw Marshal.GetHRForLastWin32Error().ToCryptographicException(); + } + + // Since contentType is an input filter, actualType should not be possible to disagree. + // + // Since contentType is only CERT, singleContext should either be valid, or the + // function should have returned false. + if (actualType != ContentType) + { + throw new CryptographicException(); + } + + return new X509Certificate2(certHandle); + } + finally + { + if (certHandle != IntPtr.Zero) + { + Interop.Crypt32.CertFreeCertificateContext(certHandle); + } + } + } + } +} diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netstandard.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netstandard.cs new file mode 100644 index 00000000000000..c867617e66b154 --- /dev/null +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netstandard.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + public static partial X509Certificate2 LoadCertificate(byte[] data) + { + X509ContentType contentType = X509Certificate2.GetCertContentType(data); + + if (contentType != X509ContentType.Cert) + { + ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE); + } + + return new X509Certificate2(data); + } + + public static partial X509Certificate2 LoadCertificate(ReadOnlySpan data) + { + if (data.IsEmpty) + { + ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE); + } + + byte[] rented = CryptoPool.Rent(data.Length); + + try + { + data.CopyTo(rented); + + return LoadCertificate(rented); + } + finally + { + CryptoPool.Return(rented, data.Length); + } + } + + public static partial X509Certificate2 LoadCertificateFromFile(string path) + { + X509ContentType contentType = X509Certificate2.GetCertContentType(path); + + if (contentType != X509ContentType.Cert) + { + ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE); + } + + return new X509Certificate2(path); + } + } +} diff --git a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj index 02ee9aae4af719..525c3135297907 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj +++ b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj @@ -4,6 +4,14 @@ $(NetCoreAppCurrent);$(NetFrameworkMinimum) + + + + + + + + @@ -19,4 +33,7 @@ + + + diff --git a/src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestData.cs b/src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestData.cs new file mode 100644 index 00000000000000..2826ce1468eb81 --- /dev/null +++ b/src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestData.cs @@ -0,0 +1,1222 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Test.Cryptography; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + internal static class TestData + { + internal const string PlaceholderPw = "Placeholder"; + + public static byte[] MsCertificate = ( + "308204ec308203d4a003020102021333000000b011af0a8bd03b9fdd00010000" + + "00b0300d06092a864886f70d01010505003079310b3009060355040613025553" + + "311330110603550408130a57617368696e67746f6e3110300e06035504071307" + + "5265646d6f6e64311e301c060355040a13154d6963726f736f667420436f7270" + + "6f726174696f6e312330210603550403131a4d6963726f736f667420436f6465" + + "205369676e696e6720504341301e170d3133303132343232333333395a170d31" + + "34303432343232333333395a308183310b300906035504061302555331133011" + + "0603550408130a57617368696e67746f6e3110300e060355040713075265646d" + + "6f6e64311e301c060355040a13154d6963726f736f667420436f72706f726174" + + "696f6e310d300b060355040b13044d4f5052311e301c060355040313154d6963" + + "726f736f667420436f72706f726174696f6e30820122300d06092a864886f70d" + + "01010105000382010f003082010a0282010100e8af5ca2200df8287cbc057b7f" + + "adeeeb76ac28533f3adb407db38e33e6573fa551153454a5cfb48ba93fa837e1" + + "2d50ed35164eef4d7adb137688b02cf0595ca9ebe1d72975e41b85279bf3f82d" + + "9e41362b0b40fbbe3bbab95c759316524bca33c537b0f3eb7ea8f541155c0865" + + "1d2137f02cba220b10b1109d772285847c4fb91b90b0f5a3fe8bf40c9a4ea0f5" + + "c90a21e2aae3013647fd2f826a8103f5a935dc94579dfb4bd40e82db388f12fe" + + "e3d67a748864e162c4252e2aae9d181f0e1eb6c2af24b40e50bcde1c935c49a6" + + "79b5b6dbcef9707b280184b82a29cfbfa90505e1e00f714dfdad5c238329ebc7" + + "c54ac8e82784d37ec6430b950005b14f6571c50203010001a38201603082015c" + + "30130603551d25040c300a06082b06010505070303301d0603551d0e04160414" + + "5971a65a334dda980780ff841ebe87f9723241f230510603551d11044a3048a4" + + "463044310d300b060355040b13044d4f5052313330310603550405132a333135" + + "39352b34666166306237312d616433372d346161332d613637312d3736626330" + + "35323334346164301f0603551d23041830168014cb11e8cad2b4165801c9372e" + + "331616b94c9a0a1f30560603551d1f044f304d304ba049a0478645687474703a" + + "2f2f63726c2e6d6963726f736f66742e636f6d2f706b692f63726c2f70726f64" + + "756374732f4d6963436f645369675043415f30382d33312d323031302e63726c" + + "305a06082b06010505070101044e304c304a06082b06010505073002863e6874" + + "74703a2f2f7777772e6d6963726f736f66742e636f6d2f706b692f6365727473" + + "2f4d6963436f645369675043415f30382d33312d323031302e637274300d0609" + + "2a864886f70d0101050500038201010031d76e2a12573381d59dc6ebf93ad444" + + "4d089eee5edf6a5bb779cf029cbc76689e90a19c0bc37fa28cf14dba9539fb0d" + + "e0e19bf45d240f1b8d88153a7cdbadceb3c96cba392c457d24115426300d0dff" + + "47ea0307e5e4665d2c7b9d1da910fa1cb074f24f696b9ea92484daed96a0df73" + + "a4ef6a1aac4b629ef17cc0147f48cd4db244f9f03c936d42d8e87ce617a09b68" + + "680928f90297ef1103ba6752adc1e9b373a6d263cd4ae23ee4f34efdffa1e0bb" + + "02133b5d20de553fa3ae9040313875285e04a9466de6f57a7940bd1fcde845d5" + + "aee25d3ef575c7e6666360ccd59a84878d2430f7ef34d0631db142674a0e4bbf" + + "3a0eefb6953aa738e4259208a6886682").HexToByteArray(); + + // This pfx was generated by new X509Certificate(MsCertificate).Export(X509ContentType.Pfx) + // and was choosen when the padding was 01 and caused a false-positive on decryption. + public static byte[] MsCertificateExportedToPfx_NullPassword = Convert.FromBase64String( + /* [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="This PKCS#12 blob only contains public info.")] */ @" +MIIFxAIBAzCCBYoGCSqGSIb3DQEHAaCCBXsEggV3MIIFczCCBW8GCSqGSIb3DQEH +BqCCBWAwggVcAgEAMIIFVQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIKpCU +u5nlxAACAggAgIIFKG/SLlS1TJmxGUiXBPJ1r4yV+JMehwo6RYPMkCSnpKGaiLyA +TLo/LPv2pwbna9R6eWMJqHUUhYKidx7Wh2gRVdDQ53FbKdbAdBPOKC+bZAQpjnHq +YRIEpnkfsee4of0O3hUCC2FPqrtnwGvyrcjLCzKZGzdLAtT4DGs7AJ3Y9YXDMy1E +MYjJLzR8Gg0teHXVgx2/QFNWclO9MYxRHzCYQkDCB3A67/gH8qrApks+3gmPTrFd +IyIjvuUmEbqeLRh/4JTIJZp4sQEgiyfk7eM5kffvWYGzoTgkYayRQYmrgRL1vkWg +hr/AXBvYzB5WY/7thp6WYilK7njVwbu3cEbGHchzhJj9OpQb5mEu9Tmx9fgFSc4l +a7lizHUwaHqH74/KEykDrlezaKp3xp0SO9b/rL7t4mkbbBe5QTJVgrgtGATBEBA9 +h4/gGiO8VWQSc0+nRzsiqkw63xLPb4dXK+ILg9NoFvIJ3xq6/HriwuGs8cyndflL +o12M4I7uzdeCTuJbIcxzI/4jF8U07n62hj39+n5IgtGPc63cSmHwKynnRLXgljSS +nBlU4q2LpXnjbCgrczyLDoRn1p1NHq0xaslFSKiV0yqO5aZ5cf6k0y+uUD84kHoK +uxbRFrcBtFIDIN0cHLKpGThiRSTBAZXLBWO+VVtZrE3yJtIjVIDhdyItt+F57rJV +7zFrzvytf1nnh8tZwLMOdwoi/X0F3WScWCscHtd6FUYDKCBgCauXW14dDX+qZE1j +CJE/zyJQMd3VOxd1m83tFJy+yC7pd+6m3CeTY8gnFAhuDECwOIhDSiROIDI7YUIh +PNOoF7/10WWgINGnKJdsPVWTP8TAMK9ePgQXyrzMpzHxko5GmLcEe5f55YqXaTwZ +JGL/MdS88YoL6GNbNpCm5mSbM3fcg72YE5c328sPDkGh4EBvWBFqu1pTYM0bm8sJ +qGjoFzVcl16O45saaVCApPihYflIxO5f48P+XcUq9RMEY11wQxLEjWJLm0EnSV0Y +GTuFT0bs8AwPJC/DJroTx/A259KkU96WpNC6icczdmGAB4MRJ8O22o7tsATYXPbU +69sLxbSK0tZGG01T0QwPwxq7vnFP5oHQDRvopQsmFOKy8j1wrABs9z646QIhs1fe +hQUPmyD3T1DvEe7p5agbksAspzlHz0rr9aP6VQPOg2M+548iTdNVaNRYxgZAL8Ew +W4x/2LANFs/wFlO6ecmlyeHzBSvMpwbYuWS5buW1iXnwHywpZt0Zv3auafGDQOqx +KeMYn+FJzvvkX8n34oWiVnJTrt+uX2Z3Q7BmZ4hRUdE/uSApoFejVTs67W8LDnL7 +VKOajQpN0IRMQv3ab34hFMmnxhs0b1B66atDlybEBsJl9xj3lzSZs+LX7tPVd+Ac +/2bRUxGgwJBK6mk7v8JikgqYCGrzT6kLvBnCkEeQa8uEPohe8aOxe4+juqjz6GqD +95YoDfEXCdF7G0rAS6pFD1YnI3DyBhm33POjeQGCverS+D8clDRzBa4ei56lzaUx +VqbMLxcxARYZw8L+fSMnhpYQgUuEfq8177USSETpUvnpD/qnMwfHwblmEaW2vsHO +AG4Sj4DcWxhRg5MnIA9fkwo6n/aQWAouv0ErNuzR1TjBM3qxrTrVM2bYoyBD9kf0 +OZ0b9sU+hiJ0LYif2/CczJwouCt58nDeYWzS4w9U/QqsaVALqMiMAgVfpIF5PYNx +9z45Sobpl+L5m5OVOe6mhKIJ7A7CnPNzKfF+mvb2Sg1kcPgwUDAxMCEwCQYFKw4D +AhoFAAQUUW5vGmwJT06SIGY9OIMn5kaZfy4ECDQi49YQi3auAgIIAA=="); + + public static readonly byte[] MsCertificatePemBytes = ByteUtils.AsciiBytes( + @"-----BEGIN CERTIFICATE----- +MIIE7DCCA9SgAwIBAgITMwAAALARrwqL0Duf3QABAAAAsDANBgkqhkiG9w0BAQUF +ADB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH +UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSMwIQYDVQQD +ExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQTAeFw0xMzAxMjQyMjMzMzlaFw0x +NDA0MjQyMjMzMzlaMIGDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv +bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 +aW9uMQ0wCwYDVQQLEwRNT1BSMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRp +b24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDor1yiIA34KHy8BXt/ +re7rdqwoUz8620B9s44z5lc/pVEVNFSlz7SLqT+oN+EtUO01Fk7vTXrbE3aIsCzw +WVyp6+HXKXXkG4Unm/P4LZ5BNisLQPu+O7q5XHWTFlJLyjPFN7Dz636o9UEVXAhl +HSE38Cy6IgsQsRCddyKFhHxPuRuQsPWj/ov0DJpOoPXJCiHiquMBNkf9L4JqgQP1 +qTXclFed+0vUDoLbOI8S/uPWenSIZOFixCUuKq6dGB8OHrbCryS0DlC83hyTXEmm +ebW22875cHsoAYS4KinPv6kFBeHgD3FN/a1cI4Mp68fFSsjoJ4TTfsZDC5UABbFP +ZXHFAgMBAAGjggFgMIIBXDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU +WXGmWjNN2pgHgP+EHr6H+XIyQfIwUQYDVR0RBEowSKRGMEQxDTALBgNVBAsTBE1P +UFIxMzAxBgNVBAUTKjMxNTk1KzRmYWYwYjcxLWFkMzctNGFhMy1hNjcxLTc2YmMw +NTIzNDRhZDAfBgNVHSMEGDAWgBTLEejK0rQWWAHJNy4zFha5TJoKHzBWBgNVHR8E +TzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9k +dWN0cy9NaWNDb2RTaWdQQ0FfMDgtMzEtMjAxMC5jcmwwWgYIKwYBBQUHAQEETjBM +MEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRz +L01pY0NvZFNpZ1BDQV8wOC0zMS0yMDEwLmNydDANBgkqhkiG9w0BAQUFAAOCAQEA +MdduKhJXM4HVncbr+TrURE0Inu5e32pbt3nPApy8dmiekKGcC8N/oozxTbqVOfsN +4OGb9F0kDxuNiBU6fNutzrPJbLo5LEV9JBFUJjANDf9H6gMH5eRmXSx7nR2pEPoc +sHTyT2lrnqkkhNrtlqDfc6TvahqsS2Ke8XzAFH9IzU2yRPnwPJNtQtjofOYXoJto +aAko+QKX7xEDumdSrcHps3Om0mPNSuI+5PNO/f+h4LsCEztdIN5VP6OukEAxOHUo +XgSpRm3m9Xp5QL0fzehF1a7iXT71dcfmZmNgzNWahIeNJDD37zTQYx2xQmdKDku/ +Og7vtpU6pzjkJZIIpohmgg== +-----END CERTIFICATE----- +"); + + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test password.")] + public const string PfxDataPassword = "12345"; + + private static readonly byte[] PfxData_RC2ContentEncryption = ( + "3082063A020103308205F606092A864886F70D010701A08205E7048205E33082" + + "05DF3082035806092A864886F70D010701A08203490482034530820341308203" + + "3D060B2A864886F70D010C0A0102A08202B6308202B2301C060A2A864886F70D" + + "010C0103300E04085052002C7DA2C2A6020207D0048202907D485E3BFC6E6457" + + "C811394C145D0E8A18325646854E4FF0097BC5A98547F5AD616C8EFDA8505AA8" + + "7564ED4800A3139759497C60C6688B51F376ACAE906429C8771CB1428226B68A" + + "6297207BCC9DD7F9563478DD83880AAB2304B545759B2275305DF4EFF9FAC24A" + + "3CC9D3B2D672EFE45D8F48E24A16506C1D7566FC6D1B269FBF201B3AC3309D3E" + + "BC6FD606257A7A707AA2F790EA3FE7A94A51138540C5319010CBA6DE9FB9D85F" + + "CDC78DA60E33DF2F21C46FB9A8554B4F82E0A6EDBA4DB5585D77D331D35DAAED" + + "51B6A5A3E000A299880FB799182C8CA3004B7837A9FEB8BFC76778089993F3D1" + + "1D70233608AF7C50722D680623D2BF54BD4B1E7A604184D9F44E0AF8099FFA47" + + "1E5536E7902793829DB9902DDB61264A62962950AD274EA516B2D44BE9036530" + + "016E607B73F341AEEFED2211F6330364738B435B0D2ED6C57747F6C8230A053F" + + "78C4DD65DB83B26C6A47836A6CBBAB92CBB262C6FB6D08632B4457F5FA8EABFA" + + "65DB34157E1D301E9085CC443582CDD15404314872748545EB3FC3C574882655" + + "8C9A85F966E315775BBE9DA34D1E8B6DADC3C9E120C6D6A2E1CFFE4EB014C3CE" + + "FBC19356CE33DAC60F93D67A4DE247B0DAE13CD8B8C9F15604CC0EC9968E3AD7" + + "F57C9F53C45E2ECB0A0945EC0BA04BAA15B48D8596EDC9F5FE9165A5D21949FB" + + "5FE30A920AD2C0F78799F6443C300629B8CA4DCA19B9DBF1E27AAB7B12271228" + + "119A95C9822BE6439414BEEAE24002B46EB97E030E18BD810ADE0BCF4213A355" + + "038B56584B2FBCC3F5EA215D0CF667FFD823EA03AB62C3B193DFB4450AABB50B" + + "AF306E8088EE7384FA2FDFF03E0DD7ACD61832223E806A94D46E196462522808" + + "3163F1CAF333FDBBE2D54CA86968867CE0B6DD5E5B7F0633C6FAB4A19CC14F64" + + "5EC14D0B1436F7623174301306092A864886F70D010915310604040100000030" + + "5D06092B060104018237110131501E4E004D006900630072006F0073006F0066" + + "00740020005300740072006F006E0067002000430072007900700074006F0067" + + "007200610070006800690063002000500072006F007600690064006500723082" + + "027F06092A864886F70D010706A08202703082026C0201003082026506092A86" + + "4886F70D010701301C060A2A864886F70D010C0106300E0408E0C117E67A75D8" + + "EB020207D080820238292882408B31826F0DC635F9BBE7C199A48A3B4FEFC729" + + "DBF95508D6A7D04805A8DD612427F93124F522AC7D3C6F4DDB74D937F57823B5" + + "B1E8CFAE4ECE4A1FFFD801558D77BA31985AA7F747D834CBE84464EF777718C9" + + "865C819D6C9DAA0FA25E2A2A80B3F2AAA67D40E382EB084CCA85E314EA40C3EF" + + "3ED1593904D7A16F37807C99AF06C917093F6C5AAEBB12A6C58C9956D4FBBDDE" + + "1F1E389989C36E19DD38D4B978D6F47131E458AB68E237E40CB6A87F21C8773D" + + "E845780B50995A51F041106F47C740B3BD946038984F1AC9E91230616480962F" + + "11B0683F8802173C596C4BD554642F51A76F9DFFF9053DEF7B3C3F759FC7EEAC" + + "3F2386106C4B8CB669589E004FB235F0357EA5CF0B5A6FC78A6D941A3AE44AF7" + + "B601B59D15CD1EC61BCCC481FBB83EAE2F83153B41E71EF76A2814AB59347F11" + + "6AB3E9C1621668A573013D34D13D3854E604286733C6BAD0F511D7F8FD6356F7" + + "C3198D0CB771AF27F4B5A3C3B571FDD083FD68A9A1EEA783152C436F7513613A" + + "7E399A1DA48D7E55DB7504DC47D1145DF8D7B6D32EAA4CCEE06F98BB3DDA2CC0" + + "D0564A962F86DFB122E4F7E2ED6F1B509C58D4A3B2D0A68788F7E313AECFBDEF" + + "456C31B96FC13586E02AEB65807ED83BB0CB7C28F157BC95C9C593C919469153" + + "9AE3C620ED1D4D4AF0177F6B9483A5341D7B084BC5B425AFB658168EE2D8FB2B" + + "FAB07A3BA061687A5ECD1F8DA9001DD3E7BE793923094ABB0F2CF4D24CB071B9" + + "E568B18336BB4DC541352C9785C48D0F0E53066EB2009EFCB3E5644ED12252C1" + + "BC303B301F300706052B0E03021A04144DEAB829B57A3156AEBC8239C0E7E884" + + "EFD96E680414E147930B932899741C92D7652268938770254A2B020207D0").HexToByteArray(); + + private static readonly byte[] PfxData_TripleDESContentEncryption = ( + "308206980201033082065406092A864886F70D010701A0820645048206413082" + + "063D308203B606092A864886F70D010701A08203A7048203A33082039F308203" + + "9B060B2A864886F70D010C0A0102A08202B6308202B2301C060A2A864886F70D" + + "010C0103300E04083A55E19A8875CF7E020207D004820290C39707B6E15BC075" + + "62B296E37D44A4C3654F398528F51988932D4E78C96AC7E783A21DD306AEB4BB" + + "7490551F5CCB21DBF883B3D01D3CCFEB0148CC79C8C15AD7266BC0C0D23563FF" + + "A879DDB05ACC5B1F9A9BA5F8E32ECE05458A55DC91882750B0573315D843524D" + + "72E400C14E24B530A228B477DF60F01A1CBA22085350C72B8E871AEF219D1457" + + "3A660D71064D4226814B8D360A84FDF9B24578E61D62119C893DF1CB97C733BC" + + "D9CCA8C3FBA40B3E35F305AFCF1E24E720379FC94FB7103A27F817D98AF70D12" + + "0A357EB5840720F48AE6ED59990E80541CCA08B2B3F9FAE0F7554F491616E72B" + + "E2383478709947F5BD5AC8D2F74550A2C4260DAA789C66559DBC25D0D5657DEF" + + "FF491287E9E982C870732E6C1494F25FED513D2DF246EDABA1DEF8FE05A1C283" + + "676A0722961FBAD4B47E8D27D08E4129FACE86CAAB657A1899C7F9286DD534AC" + + "3AE6B1A100C90207B9A39857C1D9B7A061A5E8496F0E099F1323FA56DE85BF34" + + "96DBBE6FFEAAC0321F65A40BAE63503BB704553F1027700D15D12A20EC3A8FB9" + + "8D62D902FBDBD1BA9984242A6413CF243FE1C5A5A42083EA6E821D302CC4BE4D" + + "23C0D92247C027A6D5AE3D645F378916ACBD068F52A7772571F51F4AF46652E6" + + "D9821A24267F6A8CF9D69B3EF5FF33702B71CCDA87A3704DB815685F83E22C64" + + "B60D9B659EEA517BCACDD638DB2EF171692AA17176891901F2BB97F574908CCA" + + "943743C1E8D2520FE7B5E04C10E748A666EAAE81EF5DB3577F669E049BE57CCF" + + "7FFCF7583A9ABE0BC44F0D33EBE7F3BFC2F1F4012F588D0FC8CB0FFDF3BF1A43" + + "A962B0F9059FAAC0BD68B23275584180EDDE1F1703BFE977456079FF0890BFE5" + + "ED9CA93ABECF335C02DEBB7DB1A1E60B5B7746F6F152299A1DB983ACE6D9D5B9" + + "DBAF68A3DEDCCC013181D1301306092A864886F70D0109153106040401000000" + + "305B06092A864886F70D010914314E1E4C007B00430035004600430030003800" + + "350032002D0038004200410033002D0034003700460041002D00410045003400" + + "33002D004200440039003300420044003200350041004300440042007D305D06" + + "092B060104018237110131501E4E004D006900630072006F0073006F00660074" + + "0020005300740072006F006E0067002000430072007900700074006F00670072" + + "00610070006800690063002000500072006F007600690064006500723082027F" + + "06092A864886F70D010706A08202703082026C0201003082026506092A864886" + + "F70D010701301C060A2A864886F70D010C0103300E0408812EAA0D26B0151B02" + + "0207D080820238A471B31EBE90A6B540889297C09BFC73185C2C2676665DBAD2" + + "BC5759464079976B4C82DCB425289BCFABF9CD11AABF18B3CFA1879A958919E5" + + "05465E5B2DBCA5039990650F7D7490D322F2CCCD444ACE4625BE3C361FA5C096" + + "615ACDA3123A478340332961655C973D74EE64F32015194558F4B8148DED4671" + + "7EE0381E8A53B582F176DB914475FB57C2648DCC709D6153825F33F082B91EAB" + + "BB2236343CA919065B9D9112D301E6700CE8104035469EDF7874246373351E13" + + "DC1DAB74DF1DE1E076FE03869F494C0427DE55CA7C17536DEF40D0E3F1B7E5FF" + + "5A2050175624A1F2D9EF4069186424CC956B84997768E589E38D3D836592CF27" + + "881DB2EE51AB682B4F4C58ED14585C51B72CCF156BE3CD2EEC538AD15A598E84" + + "0CFCDF6CD4A151B7EA4BE5388EA903298B4D1A86E7371300A6242DD7A654E430" + + "2B4F849650230F467042CD328A4FD4B33803213C807C37F98024A718B40AD0BD" + + "3AC6279E47FBD27E50D743BC8F595B78F8805D8143EBB3119B919DFFBFAF606F" + + "A858C77C5E0526646B6AACC536A3B2FEA1E0A67CBAE166A8F85913F3600C55F7" + + "850EF48619B9E93FB15F21994BC9ABA8FF7156B68B1DAD472ED2FA3917E1F84D" + + "AD4EB34992493B7084F6490FB326697A0A52BA08BF7F537B3BDC09B421A0D47F" + + "DCB8BD0271963EC4CE4F9C21A45AC382E8AD7ED8D69824BD5C00BCEAF56D88B3" + + "F2D0D5A81CF1C14884040A8E522428154A9C752C0E64204FA849689739E5138F" + + "0F96C471C5FE026EA7EA68AA42DF0A693213ECF06555804A191637A8F7858A30" + + "3B301F300706052B0E03021A04146B4EDA4227EAFC85EDA331AC88761DD06D54" + + "60FD0414C8A2B10C67EAECB3D48BEC69616133185721613D020207D0").HexToByteArray(); + + public static readonly byte[] PfxData = + PlatformSupport.IsRC2Supported + ? PfxData_RC2ContentEncryption + : PfxData_TripleDESContentEncryption; + + public static byte[] StoreSavedAsSerializedCerData = ( + "0200000001000000bc0000001c0000006c000000010000000000000000000000" + + "00000000020000007b00370037004500420044003000320044002d0044003800" + + "440045002d0034003700350041002d0038003800360037002d00440032003000" + + "4200300030003600340045003400390046007d00000000004d00690063007200" + + "6f0073006f006600740020005300740072006f006e0067002000430072007900" + + "700074006f0067007200610070006800690063002000500072006f0076006900" + + "64006500720000002000000001000000e9010000308201e530820152a0030201" + + "020210d5b5bc1c458a558845bff51cb4dff31c300906052b0e03021d05003011" + + "310f300d060355040313064d794e616d65301e170d3130303430313038303030" + + "305a170d3131303430313038303030305a3011310f300d060355040313064d79" + + "4e616d6530819f300d06092a864886f70d010101050003818d00308189028181" + + "00b11e30ea87424a371e30227e933ce6be0e65ff1c189d0d888ec8ff13aa7b42" + + "b68056128322b21f2b6976609b62b6bc4cf2e55ff5ae64e9b68c78a3c2dacc91" + + "6a1bc7322dd353b32898675cfb5b298b176d978b1f12313e3d865bc53465a11c" + + "ca106870a4b5d50a2c410938240e92b64902baea23eb093d9599e9e372e48336" + + "730203010001a346304430420603551d01043b3039801024859ebf125e76af3f" + + "0d7979b4ac7a96a1133011310f300d060355040313064d794e616d658210d5b5" + + "bc1c458a558845bff51cb4dff31c300906052b0e03021d0500038181009bf6e2" + + "cf830ed485b86d6b9e8dffdcd65efc7ec145cb9348923710666791fcfa3ab59d" + + "689ffd7234b7872611c5c23e5e0714531abadb5de492d2c736e1c929e648a65c" + + "c9eb63cd84e57b5909dd5ddf5dbbba4a6498b9ca225b6e368b94913bfc24de6b" + + "2bd9a26b192b957304b89531e902ffc91b54b237bb228be8afcda26476").HexToByteArray(); + + public static byte[] StoreSavedAsSerializedStoreData = ( + "00000000434552540200000001000000bc0000001c0000006c00000001000000" + + "000000000000000000000000020000007b003700370045004200440030003200" + + "44002d0044003800440045002d0034003700350041002d003800380036003700" + + "2d004400320030004200300030003600340045003400390046007d0000000000" + + "4d006900630072006f0073006f006600740020005300740072006f006e006700" + + "2000430072007900700074006f00670072006100700068006900630020005000" + + "72006f007600690064006500720000002000000001000000e9010000308201e5" + + "30820152a0030201020210d5b5bc1c458a558845bff51cb4dff31c300906052b" + + "0e03021d05003011310f300d060355040313064d794e616d65301e170d313030" + + "3430313038303030305a170d3131303430313038303030305a3011310f300d06" + + "0355040313064d794e616d6530819f300d06092a864886f70d01010105000381" + + "8d0030818902818100b11e30ea87424a371e30227e933ce6be0e65ff1c189d0d" + + "888ec8ff13aa7b42b68056128322b21f2b6976609b62b6bc4cf2e55ff5ae64e9" + + "b68c78a3c2dacc916a1bc7322dd353b32898675cfb5b298b176d978b1f12313e" + + "3d865bc53465a11cca106870a4b5d50a2c410938240e92b64902baea23eb093d" + + "9599e9e372e48336730203010001a346304430420603551d01043b3039801024" + + "859ebf125e76af3f0d7979b4ac7a96a1133011310f300d060355040313064d79" + + "4e616d658210d5b5bc1c458a558845bff51cb4dff31c300906052b0e03021d05" + + "00038181009bf6e2cf830ed485b86d6b9e8dffdcd65efc7ec145cb9348923710" + + "666791fcfa3ab59d689ffd7234b7872611c5c23e5e0714531abadb5de492d2c7" + + "36e1c929e648a65cc9eb63cd84e57b5909dd5ddf5dbbba4a6498b9ca225b6e36" + + "8b94913bfc24de6b2bd9a26b192b957304b89531e902ffc91b54b237bb228be8" + + "afcda264762000000001000000f0040000308204ec308203d4a0030201020213" + + "33000000b011af0a8bd03b9fdd0001000000b0300d06092a864886f70d010105" + + "05003079310b3009060355040613025553311330110603550408130a57617368" + + "696e67746f6e3110300e060355040713075265646d6f6e64311e301c06035504" + + "0a13154d6963726f736f667420436f72706f726174696f6e3123302106035504" + + "03131a4d6963726f736f667420436f6465205369676e696e6720504341301e17" + + "0d3133303132343232333333395a170d3134303432343232333333395a308183" + + "310b3009060355040613025553311330110603550408130a57617368696e6774" + + "6f6e3110300e060355040713075265646d6f6e64311e301c060355040a13154d" + + "6963726f736f667420436f72706f726174696f6e310d300b060355040b13044d" + + "4f5052311e301c060355040313154d6963726f736f667420436f72706f726174" + + "696f6e30820122300d06092a864886f70d01010105000382010f003082010a02" + + "82010100e8af5ca2200df8287cbc057b7fadeeeb76ac28533f3adb407db38e33" + + "e6573fa551153454a5cfb48ba93fa837e12d50ed35164eef4d7adb137688b02c" + + "f0595ca9ebe1d72975e41b85279bf3f82d9e41362b0b40fbbe3bbab95c759316" + + "524bca33c537b0f3eb7ea8f541155c08651d2137f02cba220b10b1109d772285" + + "847c4fb91b90b0f5a3fe8bf40c9a4ea0f5c90a21e2aae3013647fd2f826a8103" + + "f5a935dc94579dfb4bd40e82db388f12fee3d67a748864e162c4252e2aae9d18" + + "1f0e1eb6c2af24b40e50bcde1c935c49a679b5b6dbcef9707b280184b82a29cf" + + "bfa90505e1e00f714dfdad5c238329ebc7c54ac8e82784d37ec6430b950005b1" + + "4f6571c50203010001a38201603082015c30130603551d25040c300a06082b06" + + "010505070303301d0603551d0e041604145971a65a334dda980780ff841ebe87" + + "f9723241f230510603551d11044a3048a4463044310d300b060355040b13044d" + + "4f5052313330310603550405132a33313539352b34666166306237312d616433" + + "372d346161332d613637312d373662633035323334346164301f0603551d2304" + + "1830168014cb11e8cad2b4165801c9372e331616b94c9a0a1f30560603551d1f" + + "044f304d304ba049a0478645687474703a2f2f63726c2e6d6963726f736f6674" + + "2e636f6d2f706b692f63726c2f70726f64756374732f4d6963436f6453696750" + + "43415f30382d33312d323031302e63726c305a06082b06010505070101044e30" + + "4c304a06082b06010505073002863e687474703a2f2f7777772e6d6963726f73" + + "6f66742e636f6d2f706b692f63657274732f4d6963436f645369675043415f30" + + "382d33312d323031302e637274300d06092a864886f70d010105050003820101" + + "0031d76e2a12573381d59dc6ebf93ad4444d089eee5edf6a5bb779cf029cbc76" + + "689e90a19c0bc37fa28cf14dba9539fb0de0e19bf45d240f1b8d88153a7cdbad" + + "ceb3c96cba392c457d24115426300d0dff47ea0307e5e4665d2c7b9d1da910fa" + + "1cb074f24f696b9ea92484daed96a0df73a4ef6a1aac4b629ef17cc0147f48cd" + + "4db244f9f03c936d42d8e87ce617a09b68680928f90297ef1103ba6752adc1e9" + + "b373a6d263cd4ae23ee4f34efdffa1e0bb02133b5d20de553fa3ae9040313875" + + "285e04a9466de6f57a7940bd1fcde845d5aee25d3ef575c7e6666360ccd59a84" + + "878d2430f7ef34d0631db142674a0e4bbf3a0eefb6953aa738e4259208a68866" + + "82000000000000000000000000").HexToByteArray(); + + internal static readonly byte[] EmptyPfx = ( + "304F020103301106092A864886F70D010701A004040230003037301F30070605" + + "2B0E03021A0414822078BC83E955E314BDA908D76D4C5177CC94EB0414711018" + + "F2897A44A90E92779CB655EA11814EC598").HexToByteArray(); + + internal const string ChainPfxPassword = "test"; + + private static readonly byte[] ChainPfxBytes_RC2ContentEncryption = ( + "308213790201033082133506092A864886F70D010701A0821326048213223082" + + "131E3082036706092A864886F70D010701A08203580482035430820350308203" + + "4C060B2A864886F70D010C0A0102A08202B6308202B2301C060A2A864886F70D" + + "010C0103300E040811E8B9808BA6E96C020207D004820290D11DA8713602105C" + + "95792D65BCDFC1B7E3708483BF6CD83008082F89DAE4D003F86081B153BD4D4A" + + "C122E802752DEA29F07D0B7E8F0FB8A762B4CAA63360F9F72CA5846771980A6F" + + "AE2643CD412E6E4A101625371BBD48CC6E2D25191D256B531B06DB7CDAC04DF3" + + "E10C6DC556D5FE907ABF32F2966A561C988A544C19B46DF1BE531906F2CC2263" + + "A301302A857075C7A9C48A395241925C6A369B60D176419D75E320008D5EFD91" + + "5257B160F6CD643953E85F19EBE4E4F72B9B787CF93E95F819D1E43EF01CCFA7" + + "48F0E7260734EA9BC6039BA7557BE6328C0149718A1D9ECF3355082DE697B6CD" + + "630A9C224D831B7786C7E904F1EF2D9D004E0E825DD74AC4A576CDFCA7CECD14" + + "D8E2E6CCAA3A302871AE0BA979BB25559215D771FAE647905878E797BBA9FC62" + + "50F30F518A8008F5A12B35CE526E31032B56EFE5A4121E1E39DC7339A0CE8023" + + "24CDDB7E9497BA37D8B9F8D826F901C52708935B4CA5B0D4D760A9FB33B0442D" + + "008444D5AEB16E5C32187C7038F29160DD1A2D4DB1F9E9A6C035CF5BCED45287" + + "C5DEBAB18743AAF90E77201FEA67485BA3BBCE90CEA4180C447EE588AC19C855" + + "638B9552D47933D2760351174D9C3493DCCE9708B3EFE4BE398BA64051BF52B7" + + "C1DCA44D2D0ED5A6CFB116DDA41995FA99373C254F3F3EBF0F0049F1159A8A76" + + "4CFE9F9CC56C5489DD0F4E924158C9B1B626030CB492489F6AD0A9DCAF3E141D" + + "B4D4821B2D8A384110B6B0B522F62A9DC0C1315A2A73A7F25F96C530E2F700F9" + + "86829A839B944AE6758B8DD1A1E9257F91C160878A255E299C18424EB9983EDE" + + "6DD1C5F4D5453DD5A56AC87DB1EFA0806E3DBFF10A9623FBAA0BAF352F50AB5D" + + "B16AB1171145860D21E2AB20B45C8865B48390A66057DE3A1ABE45EA65376EF6" + + "A96FE36285C2328C318182301306092A864886F70D0109153106040401000000" + + "306B06092B0601040182371101315E1E5C004D006900630072006F0073006F00" + + "66007400200045006E00680061006E0063006500640020004300720079007000" + + "74006F0067007200610070006800690063002000500072006F00760069006400" + + "650072002000760031002E003030820FAF06092A864886F70D010706A0820FA0" + + "30820F9C02010030820F9506092A864886F70D010701301C060A2A864886F70D" + + "010C0106300E0408FFCC41FD8C8414F6020207D080820F68092C6010873CF9EC" + + "54D4676BCFB5FA5F523D03C981CB4A3DC096074E7D04365DDD1E80BF366B8F9E" + + "C4BC056E8CE0CAB516B9C28D17B55E1EB744C43829D0E06217852FA99CCF5496" + + "176DEF9A48967C1EEB4A384DB7783E643E35B5B9A50533B76B8D53581F02086B" + + "782895097860D6CA512514E10D004165C85E561DF5F9AEFD2D89B64F178A7385" + + "C7FA40ECCA899B4B09AE40EE60DAE65B31FF2D1EE204669EFF309A1C7C8D7B07" + + "51AE57276D1D0FB3E8344A801AC5226EA4ED97FCD9399A4EB2E778918B81B17F" + + "E4F65B502595195C79E6B0E37EB8BA36DB12435587E10037D31173285D45304F" + + "6B0056512B3E147D7B5C397709A64E1D74F505D2BD72ED99055161BC57B6200F" + + "2F48CF128229EFBEBFC2707678C0A8C51E3C373271CB4FD8EF34A1345696BF39" + + "50E8CE9831F667D68184F67FE4D30332E24E5C429957694AF23620EA7742F08A" + + "38C9A517A7491083A367B31C60748D697DFA29635548C605F898B64551A48311" + + "CB2A05B1ACA8033128D48E4A5AA263D970FE59FBA49017F29049CF80FFDBD192" + + "95B421FEFF6036B37D2F8DC8A6E36C4F5D707FB05274CC0D8D94AFCC8C6AF546" + + "A0CF49FBD3A67FB6D20B9FE6FDA6321E8ABF5F7CC794CFCC46005DC57A7BAFA8" + + "9954E43230402C8100789F11277D9F05C78DF0509ECFBF3A85114FD35F4F17E7" + + "98D60C0008064E2557BA7BF0B6F8663A6C014E0220693AE29E2AB4BDE5418B61" + + "0889EC02FF5480BD1B344C87D73E6E4DB98C73F881B22C7D298059FE9D7ADA21" + + "92BB6C87F8D25F323A70D234E382F6C332FEF31BB11C37E41903B9A59ADEA5E0" + + "CBAB06DFB835257ABC179A897DEAD9F19B7DF861BE94C655DC73F628E065F921" + + "E5DE98FFCBDF2A54AC01E677E365DD8B932B5BDA761A0032CE2127AB2A2B9DCB" + + "63F1EA8A51FC360AB5BC0AD435F21F9B6842980D795A6734FDB27A4FA8209F73" + + "62DD632FC5FB1F6DE762473D6EA68BFC4BCF983865E66E6D93159EFACC40AB31" + + "AA178806CF893A76CAAA3279C988824A33AF734FAF8E21020D988640FAB6DB10" + + "DF21D93D01776EEA5DAECF695E0C690ED27AD386E6F2D9C9482EA38946008CCB" + + "8F0BD08F9D5058CF8057CA3AD50BB537116A110F3B3ACD9360322DB4D242CC1A" + + "6E15FA2A95192FC65886BE2672031D04A4FB0B1F43AE8476CF82638B61B416AA" + + "97925A0110B736B4D83D7977456F35D947B3D6C9571D8E2DA0E9DEE1E665A844" + + "259C17E01E044FAB898AA170F99157F7B525D524B01BD0710D23A7689A615703" + + "8A0697BD48FFE0253ABD6F862093574B2FC9BA38E1A6EC60AF187F10D79FF71F" + + "7C50E87A07CC0A51099899F7336FE742ADEF25E720B8E0F8781EC7957D414CF5" + + "D44D6998E7E35D2433AFD86442CCA637A1513BE3020B5334614277B3101ED7AD" + + "22AFE50DE99A2AD0E690596C93B881E2962D7E52EE0A770FAF6917106A8FF029" + + "8DF38D6DE926C30834C5D96854FFD053BDB020F7827FB81AD04C8BC2C773B2A5" + + "9FDD6DDF7298A052B3486E03FECA5AA909479DDC7FED972192792888F49C40F3" + + "910140C5BE264D3D07BEBF3275117AF51A80C9F66C7028A2C3155414CF939997" + + "268A1F0AA9059CC3AA7C8BBEF880187E3D1BA8978CBB046E43289A020CAE11B2" + + "5140E2247C15A32CF70C7AA186CBB68B258CF2397D2971F1632F6EBC4846444D" + + "E445673B942F1F110C7D586B6728ECA5B0A62D77696BF25E21ED9196226E5BDA" + + "5A80ECCC785BEEDE917EBC6FFDC2F7124FE8F719B0A937E35E9A720BB9ED72D2" + + "1213E68F058D80E9F8D7162625B35CEC4863BD47BC2D8D80E9B9048811BDD8CB" + + "B70AB215962CD9C40D56AE50B7003630AE26341C6E243B3D12D5933F73F78F15" + + "B014C5B1C36B6C9F410A77CA997931C8BD5CCB94C332F6723D53A4CCC630BFC9" + + "DE96EFA7FDB66FA519F967D6A2DB1B4898BB188DEB98A41FFA7907AE7601DDE2" + + "30E241779A0FDF551FB84D80AAEE3D979F0510CD026D4AE2ED2EFB7468418CCD" + + "B3BD2A29CD7C7DC6419B4637412304D5DA2DC178C0B4669CA8330B9713A812E6" + + "52E812135D807E361167F2A6814CEF2A8A9591EFE2C18216A517473B9C3BF2B7" + + "51E47844893DA30F7DCD4222D1A55D570C1B6F6A99AD1F9213BA8F84C0B14A6D" + + "ED6A26EAFF8F89DF733EEB44117DF0FD357186BA4A15BD5C669F60D6D4C34028" + + "322D4DDF035302131AB6FD08683804CC90C1791182F1AE3281EE69DDBBCC12B8" + + "1E60942FD082286B16BE27DC11E3BB0F18C281E02F3BA66E48C5FD8E8EA3B731" + + "BDB12A4A3F2D9E1F833DD204372003532E1BB11298BDF5092F2959FC439E6BD2" + + "DC6C37E3E775DCBE821B9CBB02E95D84C15E736CEA2FDDAD63F5CD47115B4AD5" + + "5227C2A02886CD2700540EBFD5BF18DC5F94C5874972FD5424FE62B30500B1A8" + + "7521EA3798D11970220B2BE7EFC915FCB7A6B8962F09ABA005861E839813EDA3" + + "E59F70D1F9C277B73928DFFC84A1B7B0F78A8B001164EB0824F2510885CA269F" + + "DCBB2C3AE91BDE91A8BBC648299A3EB626E6F4236CCE79E14C803498562BAD60" + + "28F5B619125F80925A2D3B1A56790795D04F417003A8E9E53320B89D3A3109B1" + + "9BB17B34CC9700DA138FABB5997EC34D0A44A26553153DBCFF8F6A1B5432B150" + + "58F7AD87C6B37537796C95369DAD53BE5543D86D940892F93983153B4031D4FA" + + "B25DAB02C1091ACC1DAE2118ABD26D19435CD4F1A02BDE1896236C174743BCA6" + + "A33FB5429E627EB3FD9F513E81F7BD205B81AAE627C69CF227B043722FA05141" + + "39347D202C9B7B4E55612FC27164F3B5F287F29C443793E22F6ED6D2F353ED82" + + "A9F33EDBA8F5F1B2958F1D6A3943A9614E7411FDBCA597965CD08A8042307081" + + "BAC5A070B467E52D5B91CA58F986C5A33502236B5BAE6DB613B1A408D16B29D3" + + "560F1E94AD840CFA93E83412937A115ABF68322538DA8082F0192D19EAAA41C9" + + "299729D487A9404ECDB6396DDA1534841EAE1E7884FA43574E213AE656116D9E" + + "F7591AA7BDE2B44733DFE27AA59949E5DC0EE00FDF42130A748DDD0FB0053C1A" + + "55986983C8B9CEAC023CAD7EDFFA1C20D3C437C0EF0FC9868D845484D8BE6538" + + "EAADA6365D48BA776EE239ED045667B101E3798FE53E1D4B9A2ACBBE6AF1E5C8" + + "8A3FB03AD616404013E249EC34458F3A7C9363E7772151119FE058BD0939BAB7" + + "64A2E545B0B2FDAA650B7E849C8DD4033922B2CAE46D0461C04A2C87657CB4C0" + + "FFBA23DED69D097109EC8BFDC25BB64417FEEB32842DE3EFEF2BF4A47F08B9FC" + + "D1907BC899CA9DA604F5132FB420C8D142D132E7E7B5A4BD0EF4A56D9E9B0ACD" + + "88F0E862D3F8F0440954879FFE3AA7AA90573C6BFDC6D6474C606ACA1CD94C1C" + + "3404349DD83A639B786AFCDEA1779860C05400E0479708F4A9A0DD51429A3F35" + + "FBD5FB9B68CECC1D585F3E35B7BBFC469F3EAEEB8020A6F0C8E4D1804A3EB32E" + + "B3909E80B0A41571B23931E164E0E1D0D05379F9FD3BF51AF04D2BE78BDB84BD" + + "787D419E85626297CB35FCFB6ED64042EAD2EBC17BB65677A1A33A5C48ADD280" + + "237FB2451D0EFB3A3C32354222C7AB77A3C92F7A45B5FB10092698D88725864A" + + "3685FBDD0DC741424FCCD8A00B928F3638150892CAAB535CC2813D13026615B9" + + "9977F7B8240E914ACA0FF2DCB1A9274BA1F55DF0D24CCD2BAB7741C9EA8B1ECD" + + "E97477C45F88F034FDF73023502944AEE1FF370260C576992826C4B2E5CE9924" + + "84E3B85170FCCAC3413DC0FF6F093593219E637F699A98BD29E8EE4550C128CA" + + "182680FDA3B10BC07625734EE8A8274B43B170FC3AEC9AA58CD92709D388E166" + + "AB4ADFD5A4876DC47C17DE51FDD42A32AF672515B6A81E7ABECFE748912B321A" + + "FD0CBF4880298DD79403900A4002B5B436230EB6E49192DF49FAE0F6B60EBA75" + + "A54592587C141AD3B319129006367E9532861C2893E7A2D0D2832DF4377C3184" + + "5CB02A1D020282C3D2B7F77221F71FEA7FF0A988FEF15C4B2F6637159EEC5752" + + "D8A7F4AB971117666A977370E754A4EB0DC52D6E8901DC60FCD87B5B6EF9A91A" + + "F8D9A4E11E2FFDAB55FC11AF6EEB5B36557FC8945A1E291B7FF8931BE4A57B8E" + + "68F04B9D4A9A02FC61AE913F2E2DDBEE42C065F4D30F568834D5BB15FDAF691F" + + "197EF6C25AE87D8E968C6D15351093AAC4813A8E7B191F77E6B19146F839A43E" + + "2F40DE8BE28EB22C0272545BADF3BD396D383B8DA8388147100B347999DDC412" + + "5AB0AA1159BC6776BD2BF51534C1B40522D41466F414BDE333226973BAD1E6D5" + + "7639D30AD94BEA1F6A98C047F1CE1294F0067B771778D59E7C722C73C2FF100E" + + "13603206A694BF0ED07303BE0655DC984CA29893FD0A088B122B67AABDC803E7" + + "3E5729E868B1CA26F5D05C818D9832C70F5992E7D15E14F9775C6AD24907CF2F" + + "211CF87167861F94DCF9E3D365CB600B336D93AD44B8B89CA24E59C1F7812C84" + + "DBE3EE57A536ED0D4BF948F7662E5BCBBB388C72243CFCEB720852D5A4A52F01" + + "8C2C087E4DB43410FE9ABA3A8EF737B6E8FFDB1AB9832EBF606ED5E4BD62A86B" + + "BCAE115C67682EDEA93E7845D0D6962C146B411F7784545851D2F327BEC7E434" + + "4D68F137CDA217A3F0FF3B752A34C3B5339C79CB8E1AC690C038E85D6FC13379" + + "090198D3555394D7A2159A23BD5EEF06EB0BCC729BB29B5BE911D02DA78FDA56" + + "F035E508C722139AD6F25A6C84BED0E98893370164B033A2B52BC40D9BF5163A" + + "F9650AB55EABB23370492A7D3A87E17C11B4D07A7296273F33069C835FD208BA" + + "8F989A3CF8659054E2CCCFB0C983531DC6590F27C4A1D2C3A780FE945F7E52BB" + + "9FFD2E324640E3E348541A620CD62605BBDB284AF97C621A00D5D1D2C31D6BD6" + + "1149137B8A0250BC426417A92445A52574E999FB9102C16671914A1542E92DDE" + + "541B2A0457112AF936DA84707CADFEA43BFEDAE5F58859908640420948086E57" + + "FFD1B867C241D40197CB0D4AD58BB69B3724772E0079406A1272858AAA620668" + + "F696955102639F3E95CFFC637EAF8AB54F0B5B2131AB292438D06E15F3826352" + + "DEDC653DA5A4AACE2BB97061A498F3B6789A2310471B32F91A6B7A9944DDBB70" + + "31525B3AE387214DC85A1C7749E9168F41272680D0B3C331D61175F23B623EEC" + + "40F984C35C831268036680DE0821E5DEE5BB250C6984775D49B7AF94057371DB" + + "72F81D2B0295FC6A51BCD00A697649D4346FDD59AC0DFAF21BFCC942C23C6134" + + "FFBA2ABABC141FF700B52C5B26496BF3F42665A5B71BAC7F0C19870BD9873890" + + "239C578CDDD8E08A1B0A429312FB24F151A11E4D180359A7FA043E8155453F67" + + "265CB2812B1C98C144E7675CFC86413B40E35445AE7710227D13DC0B5550C870" + + "10B363C492DA316FB40D3928570BF71BF47638F1401549369B1255DB080E5DFA" + + "18EA666B9ECBE5C9768C06B3FF125D0E94B98BB24B4FD44E770B78D7B336E021" + + "4FD72E77C1D0BE9F313EDCD147957E3463C62E753C10BB98584C85871AAEA9D1" + + "F397FE9F1A639ADE31D40EAB391B03B588B8B031BCAC6C837C61B06E4B745052" + + "474D33531086519C39EDD6310F3079EB5AC83289A6EDCBA3DC97E36E837134F7" + + "303B301F300706052B0E03021A0414725663844329F8BF6DECA5873DDD8C96AA" + + "8CA5D40414DF1D90CD18B3FBC72226B3C66EC2CB1AB351D4D2020207D0").HexToByteArray(); + + private static readonly byte[] ChainPfxBytes_TripleDESContentEncryption = ( + "30821306020103308212C206092A864886F70D010701A08212B3048212AF3082" + + "12AB308203C406092A864886F70D010701A08203B5048203B1308203AD308203" + + "A9060B2A864886F70D010C0A0102A08202B6308202B2301C060A2A864886F70D" + + "010C0103300E0408B43CDBD7536FD7E2020207D0048202904EC18175B7B959AD" + + "8BF52CCEE41D754471CD84C99404E07672BA2F1BF60C59AFE7D32B64EF1EDB60" + + "326F06991D20797C598F2BD192C258C0ED726382C1B7B8A12781CA64AB72320F" + + "CAFD864D9E3CA580BA961DEF71477BA4463F8E56C9FDE471B7E0D252988256CE" + + "8780A6E843B46D9D829EF20321F8B207C1410D85E04F0CDD9CA2E7C25ED661F2" + + "820C76495A900A83D5485D0CA93E6B89D3088C727EF6A702F63C648C9271C292" + + "FCF716E755F23EB65376E1F413E12946E3A43B6AB419E5076ED523CE4DC6DF9B" + + "2C8E461A3243708764B8655AA2763A55011A558778E2A31072B33DDD4D04A86E" + + "89C416F4CFA65E6FADF390F2EE7DC8764428F85C3BB7553478A03F4807949722" + + "77077B65A799E862C7E67A4EEF0AAEC1C36ED7F1408E3B0B53DB24A76CE80966" + + "2DD472121B64740EDC3306AED1CD9E200011999B31A3EEB56F7B34880FD5918C" + + "7116607366E6C2514BAD741BBF25B10124B01F848162F405572C3781049E9AED" + + "A5294FB5AC6171E885311E7CB7001A8B83A86964B6D407096309322051ED60AF" + + "1E2D76A534D44ECC0895BF4984FA2F05625F2AF2B05B82794007705DE256914E" + + "F66AB1DD8726CE09E7950B8CE37D5138951620EEE7DF1F6957B62610BBCC53CA" + + "DE6FB33DBA5709DAC3D83E92028391132535B2C8A1B96E153FBF18D008A9F495" + + "E022B8B1DD77FE3456388D450E7026246F0246408B96F995C60930F912463E34" + + "45B6803F5BDBAC95C6CFF02893C8D57FFE091E7C520B971D24F6050D0ADA8E52" + + "F9F64D68DA38891A36AAF0231B35873CD90894393835623D6D3E898361AD17E3" + + "BC99FDBBDEF9F82763740468DE692A3FA094E1683E651DAEAD3267DBD030C930" + + "5C3A4E4AF5AB61B483BFFA4998C5F708FE4CF8D3435EA38B1512D56310ED58CD" + + "65AAADB6A64C7C623181DF301306092A864886F70D0109153106040401000000" + + "305B06092A864886F70D010914314E1E4C007B00340031003700380032003500" + + "340041002D0036003300440042002D0034003400380045002D00410030003700" + + "41002D003200320033003600420038003900450041003800350045007D306B06" + + "092B0601040182371101315E1E5C004D006900630072006F0073006F00660074" + + "00200045006E00680061006E006300650064002000430072007900700074006F" + + "0067007200610070006800690063002000500072006F00760069006400650072" + + "002000760031002E003030820EDF06092A864886F70D010706A0820ED030820E" + + "CC02010030820EC506092A864886F70D010701301C060A2A864886F70D010C01" + + "03300E040894BE093240AADB8A020207D080820E989C5F83126495753056D861" + + "A825CCB15575B04D71869B2A3685B26E535C4748F62EE23ED1C6ADC3D35DC39D" + + "0EB75C11742BBB7F36A7BA52D63EEA6F1398BBF1361EB98CC06C9EF405040888" + + "35B4063525FB2AA0715E88E62D44746404681304F38E5E2CD27BBB14571885CA" + + "DEAE50AB9F3956B3332B0C4CED90F0C8A7421532D938015861AC6EDF0F3007E7" + + "FFB9786DC56E299D5D9277BB7A29902863CD606CED8494932607AA202ECACB5F" + + "BB23AB7E824FEBED60CD76B462417ED099CAF56CE946FA5B5906341618ADA2EB" + + "3C739E332EE43ED6D16A971D2F0AF47941BABB3251250108BA6E4C0909B27808" + + "6CAA4509DFE267F9ED352330576EC22D28F11607B9805D5A82CF5A4B45C702F1" + + "A55B52D9A0E1A70543746ED457E39CAA42D66BF1EA8EE7C4DEE500E8B35849F4" + + "797583651465B5088C8F83665D05087DB9E68A5D6AFB0C489BA255A1E639E540" + + "977C53CA9F250E2C5F2B5822A7F706BD3C45DC8BCB546CB1184D7CB088701D0B" + + "53845736CCDC4BAD654108459415CC5DA8718E15E319473E5EF8107709B4EF68" + + "395E164987D467D39EAD4A049EB314F30505A072D52FB9902AF3FDC76F607D4C" + + "5068F4AD2CDB0E734E5EC18B72A44DD009EB02AB3C9531CF2BFBD6E3B40B3A4C" + + "2AE0AF8B04F6D2038102869A3952FEB7F4B82A03BC180D201FAC040785E02DA7" + + "261E1A2D74BBE95D13DA0C3F07744D2A159E4BBACB8FF4D7CC6306D0DE3E1D12" + + "6A9BDE0DFB9ED71D2C0B3827B0087D1FFBAE639AFD6EBB3C3C7138602C582F01" + + "404D997BB66EDC324CF43EB0315C13C8917EB88C318351E77CD5168EB8D05F04" + + "1F375ED6149E797497DDB6B549CDC7E24375E90746143F95474FC5A72E1B097C" + + "85C61BA7C5CCFC573B0DC308EA08BC1E21930DE314934CC4A7BBCB6C83165B1A" + + "2D7A34C94B165879FABCEA245919A9CE9681DB0FA6B4278B6E757047661C6604" + + "CB66E95BE6C5BE1F43CD6CCBC6CD9687310DB42C64E6BAE6224A219AD34E4918" + + "81007A7A69BCD4FC7077D0B469E90F4FE7FA584D8F7384C2013C94502AB0D61E" + + "24341FFC2894FBB0C807D8F9228E15DDE47E20C909B6BA802C1C3C4FF4E6FFE6" + + "7EAAC94290681A250FD681305C77FF881142C04FA1262D0B29E233AEB0DBAF7A" + + "70C8EAB8257BF553CDCCB5E3D06617410E6CABF19B24F6943597FA353D8861BE" + + "BF728864E17A54B37BE81FBF25FA5C3F5EDDADBD633274D226A6DD9BB2D427DC" + + "B283A29A691BC89BADFF80A5B852EA64521704974CDDC0599E94EF1BC259EC23" + + "3A1310F791878294DB5F8D7B2C890B6EBAE3E517EB1138D0A00AC762BD27D273" + + "2D66BDF971C8E87ED10CE4880D0460C9EFEBF58DE7C65AE59BF6FBAD9B5B32A2" + + "49DD6B9B3F9821F584004EA9DFB54C94B54B7A756D531F565A84A60747A47E6D" + + "D2A5A0F2F16BA15D4F5AD0CA110D26E629AD7CF4E6812948138FEB7DF3104AD1" + + "CFDC9F6FD6F64EB6A009D27E4D63C9A212ABF61CABEE2E290691913AE20E4966" + + "D0B8FF6E97342A6297A07A72796D7466AAA8E9336F8F7796EEB4CE624E83335C" + + "F3DC78C97A67BD868C9EE8C8CA3B756ACF93B7798DF470FAB7DE524C751631BC" + + "4040483C2C53CC691E13122C1BFDC36D83F0FEFD1DDC1ED9042613406FA476A0" + + "F97969213DE68568FA15F04F1AED1481BBDAC0BD9FDFF6FAB9C2E130024CEB6F" + + "358809FBCB94EC1454E46D2E369923D1B93F18F8B584B6CC2820E26C605C7BB1" + + "F50595DD9F0E724DB23359E9B7045DE35E46ACFE07AF27E1D111551689CC0352" + + "AD474F0159B8645408512B03E5EEDE3302513FD665666FB7D1D210F426E18E1E" + + "2B5616EBAB322A306338C784CAECA267F3592D217CE17D318A0A68A6D19CE60D" + + "90BEAC809F8F3DCEDB427B51CE604CBE814597A71311C9E650D64FA1BFECB801" + + "D8EE3C1607E02F641C31212AC6ABA56922A912A6E0C5284C547CA80A94B59C46" + + "443510EDEAB9388D422014C472394EAE7DC125C3A6C8B707AC3552EE52153B65" + + "F7FC989093721329910D4B5C92398FB13644A8C784380E82E73AF03A478C7875" + + "1A7E8E9B6E0D1ED589190ADF388C7A82BC451BAC4AD363C5B6889EED91DF2FB8" + + "A8DCC6810D188570CADDBA85BA935E272026AF23611D1FEE694E4475BF9E68E0" + + "8E8A88F2202A153259206FADAE4346D3BAEB59387715FC5D05E7D5F915184A38" + + "81609998068E7EB6A843A719A5ACD41A82364358F4B6867F6B9EA0175703A431" + + "00742F88AC0769B03071D7C3BF345C95806EC951F2B7C55AF7FE52174E37219B" + + "59309BAA09EEBA299F78E53F375D549C560E5D3F1848113A2E2C8B6CE65892C1" + + "F55E3822864E6D95E055BA5CBB0098BB922C3188E3EB56B9EAA93030B2E8587E" + + "9EF411F725D57F1A12D6F911413D91C78334963679083C308BCAD4C8DBE43116" + + "BCD5C74AA6B22FE9904329DF6E8EFF458BA7659ED9A043302009D6BBED2EC674" + + "EE661AB7789058CE56918A7F31A4827667338C7B12A958C239300FEE3D151F46" + + "D595707B76707B247BC37457DC1498818F6B56D9CABB72682F65BE500F74D00F" + + "A7B1144FD05FBAEB12DD83014F1A01367543761DA8FFF0B78072E5DCC14A561C" + + "5A08F509FEFF2DA20C5F342BC7E7C498417BF758B5DB26732DE45034FE952AC7" + + "6FDB80A526066505610E8753BC5521BB47333C2EBBD42B959010EA18FB5F8194" + + "A8BCD6FC6E5B8059F8C14DE002E3F78AC453C4E14FCA4DF09EFDBC992ABAEA34" + + "A7E6648B6E7F4B7D7676B2DFDFB780A275456ABC2E0C6409FBD017A10E08D3D7" + + "D8F37CF3C451BC73CD28C77F3BE2E1812D5FA6DBB842B60FBA1A5B718BC43A18" + + "2D9BAFC7C2899D8BCBBED51B2B252AC90EAADEFEA04E801F3AEB7766D92C70A5" + + "D4D05546F698B191B080135CE040A626F91589088E14E4AC494B2C4EB5CE6D1C" + + "0B827E412001F6C366A9FCD29C1663D4003AF2BE77733DA806787E4AC39DBE07" + + "9F13344B958A38EB6A0D7DB28F51241D1820316D33A23233766D57ADC6C7383A" + + "16FDC152C4100CFB459D37915478DE03A6BCF64CF513EB650D8390077C7EE12E" + + "0CC2EC447B1DECC5C675F0A0912FAD96727942E3F76490A319B65689D25212E6" + + "574AFE8AC6E558FA17D324E8ACAE80DA5803CC17C10BEB887F5B8783C37D1EF8" + + "4382D5DEEBCF3D2CBCD4420C1B5B304C56F60EF06A58C632CBB9435932E0C605" + + "C46291952EC08261C5847B2785E5E3FCCE6756BCE869E5E564FFA6D92F494099" + + "BCC08F2BB84ADAE91F000D134E918DE947DBB217EE5D24A42E68C1A6F14B3C37" + + "0F0E0E829FE29F91DF01B205BA209736A979D06DA905E52569D19F460CD3C9FB" + + "AC7D428554D7BE5766D246DAF927F32EEE76D88B29D24EBB8F20C0EE01661579" + + "568B20F08523893DA596F0418EF2565CFC90A2AE97860D77F43806B1493FB1B9" + + "873AF46DDCE8112CA98141AEB2A3CDCE6FB90545CDA7215CB5AFD0E1FA1CFA01" + + "9B8A95E552F7B4C174F4B5635430500DFF11851F5084233AE8D9CBB3416C7AF6" + + "128DAFA764C2032E4E6FD45C2C8ED83436CA673C7F0A1310B50A59E99A4DA328" + + "A179D29128C74B14FEACF2C6D0B0E079B7A700CD4D5D92E50CFEBB5633055C87" + + "5F57D2063CE4A5A4BDE5043F9F069C8051A5F2803D242A67F61FAAE19CB01827" + + "7132484137ED45B02FA919C399AA6F7DC4EC9D116C7A12803F8A6589AD801FD0" + + "154387C09072D48D4D9F4DD70E3319B61AE35F6B8BDF77C796C12FCFCA7D3F5B" + + "AB3C639D79AF8CA579944D2EA9F1D3F8B1E872C48384FA089EE74E044255BEA7" + + "79C79FE7826D7BAADD6CCA0B53912762CCE890A419E94B9CEC3152A46D1D1158" + + "84671CAF82DB53B3E1D23934C58AADC97E84589F2E0745DB62B757C4061416BB" + + "5DA650CAF0D687BAE96DF8521AE185B3122C13E342F7FFC985B4ADB2BE8E412E" + + "A4ABF9D39DDFAB7FACE1BC319F725F9982944F8B34B4F563BC4E138C99B7EA49" + + "05960529F79E4124B52508E7F20547FDBE7DE03692A5B249AD0E0739E34CE987" + + "9423CEEECBAFF243EA4EECAF7E7DA782306FA771AC2D4CF3EDB33255382CA410" + + "F8DA4B467012CF4322BBA86138A75D1252C1C08A5E24C8BFB57DF48C860B93A4" + + "55C6A428DEEA8EDB91272BFA03C73E80987BB94F8C0CB504552C8FB0C7A3003A" + + "62F678A4424C0B8AA1A9AAEC747786C051FCD407F1CE56A28F3ED28B2BCDADE0" + + "5724FE94FBF67EA6BA4030832D4B8DB97DEA9496CFEDC85E71AA35524DB4331E" + + "742BE36CC7EE1DB92AA57467F23AB8AA73FC23B32E5749C8A43915A8E5A1AC3D" + + "3E031CB786B4070195D88C98E0075B713BB71D77D6F9E50FB65F18B0552B1F83" + + "B4158B304014C94B266C52781521388E3469FF78E8128EB26D8FDFFF75FEF45B" + + "C0D2241851E976F3B0A75E6A9BED401095A093AEB7F6F8EFC8338F3CEC40B71B" + + "DB86433E7E0617FBB354D37ABBC4C998E8E4E3E9514C2BE06D65109772DCE808" + + "CDB9AE319F35E681B83D29E01BACFB0E02619DC8C1D390C35C87299D40D8396C" + + "0344242A963987ABAFA357634E8398C0CAFFCB5AB5F5EAF395B38CBBF87F6AB1" + + "0331837BB08FEEC14A16B22B1B8DECF5C79DB8F27D4DCE805FB15E8823E35529" + + "136BDB216A3E48EA042DF59237C65048DA7CA3E06FDE9EA8EF04124A8DBF4ED2" + + "720A4B9F99B93659B220072A4B88EC093E3E69CE964837E413A145EF08E9265A" + + "946653112C46CBA62D2425AF9A56B6F88A30802BF1174E854EEA5585AC4CAC24" + + "2A5BFDF5585659C2FF9EDFAF71594DB4A9D81188350AD5F30CBA9FED3B023ACC" + + "9424D1FBAB4FE8B1562544C90EA2599E8FAB028A244EAF3FC6FF72EE0029B277" + + "6E12786BD3783E51CF2AFC8AE484C205576F6E4EB216F7D4573D0EBCFA67BC3E" + + "999DE7390EE74F5E73B570DF80D8145DE6FE96A2D4D12D758A6B134303E7F361" + + "500377765CE0F5D3ACDB9ED3FEEC304CD10C0C19872A426A077B0D57D4CC8969" + + "81EAD835C2EAABDA8A6537E19B034638F1B57F544D8B5CAF9AB8754E45CBCCFB" + + "C1AC47A4AF3E225D96FCC828979B4A6923294E1E9F75C3F35354C9BE8C9FF95C" + + "805FF2E108FDC596965FC4837D0EC02C40F1C343B21C96110FB4AAB46A840984" + + "8AF62B81FA593FD49DA006B3B44CB16F7DAF32603742990FEE02569EEC8CC042" + + "EAEBAFC01F28DF096A3CD961D8F4975FD19CD8FE4F64D46D14A53BEFC60B3CC4" + + "8CD8DBADEDE41EFBF9DDDC5C583F0E66F577192D3B31D2254B6A6991B1BD684B" + + "6DECD4E8F232767A46BBBD83E9BA5CDE1EAA09BBEF497FAC94BE5DD435E364AA" + + "8A19E9413C47214C2D285A6B20303B301F300706052B0E03021A04147E4F196F" + + "6D0A4EF84A2C08674CDDF6899F7672A50414206C1AAAFFB1E282745C91F6BF5F" + + "0E87036AC61E020207D0").HexToByteArray(); + + public static readonly byte[] ChainPfxBytes = + PlatformSupport.IsRC2Supported + ? ChainPfxBytes_RC2ContentEncryption + : ChainPfxBytes_TripleDESContentEncryption; + + internal static readonly byte[] Pkcs7ChainDerBytes = ( + "30820E1606092A864886F70D010702A0820E0730820E030201013100300B0609" + + "2A864886F70D010701A0820DEB3082050B30820474A003020102020A15EAA83A" + + "000100009291300D06092A864886F70D010105050030818131133011060A0992" + + "268993F22C6401191603636F6D31193017060A0992268993F22C64011916096D" + + "6963726F736F667431143012060A0992268993F22C6401191604636F72703117" + + "3015060A0992268993F22C64011916077265646D6F6E643120301E0603550403" + + "13174D532050617373706F7274205465737420537562204341301E170D313330" + + "3131303231333931325A170D3331313231333232323630375A308185310B3009" + + "060355040613025553310B30090603550408130257413110300E060355040713" + + "075265646D6F6E64310D300B060355040A130454455354310D300B060355040B" + + "130454455354311330110603550403130A746573742E6C6F63616C3124302206" + + "092A864886F70D010901161563726973706F70406D6963726F736F66742E636F" + + "6D30819F300D06092A864886F70D010101050003818D0030818902818100B406" + + "851089E9CF7CDB438DD77BEBD819197BEEFF579C35EF9C4652DF9E6330AA7E2E" + + "24B181C59DA4AF10E97220C1DF99F66CE6E97247E9126A016AC647BD2EFD136C" + + "31470C7BE01A20E381243BEEC8530B7F6466C50A051DCE37274ED7FF2AFFF4E5" + + "8AABA61D5A448F4A8A9B3765D1D769F627ED2F2DE9EE67B1A7ECA3D288C90203" + + "010001A38202823082027E300E0603551D0F0101FF0404030204F0301D060355" + + "1D250416301406082B0601050507030106082B06010505070302301D0603551D" + + "0E04160414FB3485708CBF6188F720EF948489405C8D0413A7301F0603551D23" + + "0418301680146A6678620A4FF49CA8B75FD566348F3371E42B133081D0060355" + + "1D1F0481C83081C53081C2A081BFA081BC865F687474703A2F2F707074657374" + + "73756263612E7265646D6F6E642E636F72702E6D6963726F736F66742E636F6D" + + "2F43657274456E726F6C6C2F4D5325323050617373706F727425323054657374" + + "25323053756225323043412831292E63726C865966696C653A2F2F5C5C707074" + + "65737473756263612E7265646D6F6E642E636F72702E6D6963726F736F66742E" + + "636F6D5C43657274456E726F6C6C5C4D532050617373706F7274205465737420" + + "5375622043412831292E63726C3082013806082B060105050701010482012A30" + + "82012630819306082B06010505073002868186687474703A2F2F707074657374" + + "73756263612E7265646D6F6E642E636F72702E6D6963726F736F66742E636F6D" + + "2F43657274456E726F6C6C2F70707465737473756263612E7265646D6F6E642E" + + "636F72702E6D6963726F736F66742E636F6D5F4D5325323050617373706F7274" + + "2532305465737425323053756225323043412831292E63727430818D06082B06" + + "01050507300286818066696C653A2F2F5C5C70707465737473756263612E7265" + + "646D6F6E642E636F72702E6D6963726F736F66742E636F6D5C43657274456E72" + + "6F6C6C5C70707465737473756263612E7265646D6F6E642E636F72702E6D6963" + + "726F736F66742E636F6D5F4D532050617373706F727420546573742053756220" + + "43412831292E637274300D06092A864886F70D0101050500038181009DEBB8B5" + + "A41ED54859795F68EF767A98A61EF7B07AAC190FCC0275228E4CAD360C9BA98B" + + "0AE153C75522EEF42D400E813B4E49E7ACEB963EEE7B61D3C8DA05C183471544" + + "725B2EBD1889877F62134827FB5993B8FDF618BD421ABA18D70D1C5B41ECDD11" + + "695A48CB42EB501F96DA905471830C612B609126559120F6E18EA44830820358" + + "308202C1A00302010202101B9671A4BC128B8341B0E314EAD9A191300D06092A" + + "864886F70D01010505003081A13124302206092A864886F70D01090116156173" + + "6D656D6F6E406D6963726F736F66742E636F6D310B3009060355040613025553" + + "310B30090603550408130257413110300E060355040713075265646D6F6E6431" + + "123010060355040A13094D6963726F736F667431163014060355040B130D5061" + + "7373706F727420546573743121301F060355040313184D532050617373706F72" + + "74205465737420526F6F74204341301E170D3035303132363031333933325A17" + + "0D3331313231333232323630375A3081A13124302206092A864886F70D010901" + + "161561736D656D6F6E406D6963726F736F66742E636F6D310B30090603550406" + + "13025553310B30090603550408130257413110300E060355040713075265646D" + + "6F6E6431123010060355040A13094D6963726F736F667431163014060355040B" + + "130D50617373706F727420546573743121301F060355040313184D5320506173" + + "73706F7274205465737420526F6F7420434130819F300D06092A864886F70D01" + + "0101050003818D0030818902818100C4673C1226254F6BBD01B01D21BB05264A" + + "9AA5B77AC51748EAC52048706DA6B890DCE043C6426FC44E76D70F9FE3A4AC85" + + "5F533E3D08E140853DB769EE24DBDB7269FABEC0FDFF6ADE0AA85F0085B78864" + + "58E7585E433B0924E81600433CB1177CE6AD5F2477B2A0E2D1A34B41F6C6F5AD" + + "E4A9DD7D565C65F02C2AAA01C8E0C10203010001A3818E30818B301306092B06" + + "0104018237140204061E0400430041300B0603551D0F040403020186300F0603" + + "551D130101FF040530030101FF301D0603551D0E04160414F509C1D6267FC39F" + + "CA1DE648C969C74FB111FE10301206092B060104018237150104050203010002" + + "302306092B0601040182371502041604147F7A5208411D4607C0057C98F0C473" + + "07010CB3DE300D06092A864886F70D0101050500038181004A8EAC73D8EA6D7E" + + "893D5880945E0E3ABFC79C40BFA60A680CF8A8BF63EDC3AD9C11C081F1F44408" + + "9581F5C8DCB23C0AEFA27571D971DBEB2AA9A1B3F7B9B0877E9311D36098A65B" + + "7D03FC69A835F6C3096DEE135A864065F9779C82DEB0C777B9C4DB49F0DD11A0" + + "EAB287B6E352F7ECA467D0D3CA2A8081119388BAFCDD25573082057C308204E5" + + "A003020102020A6187C7F200020000001B300D06092A864886F70D0101050500" + + "3081A13124302206092A864886F70D010901161561736D656D6F6E406D696372" + + "6F736F66742E636F6D310B3009060355040613025553310B3009060355040813" + + "0257413110300E060355040713075265646D6F6E6431123010060355040A1309" + + "4D6963726F736F667431163014060355040B130D50617373706F727420546573" + + "743121301F060355040313184D532050617373706F7274205465737420526F6F" + + "74204341301E170D3039313032373231333133395A170D333131323133323232" + + "3630375A30818131133011060A0992268993F22C6401191603636F6D31193017" + + "060A0992268993F22C64011916096D6963726F736F667431143012060A099226" + + "8993F22C6401191604636F727031173015060A0992268993F22C640119160772" + + "65646D6F6E643120301E060355040313174D532050617373706F727420546573" + + "742053756220434130819F300D06092A864886F70D010101050003818D003081" + + "8902818100A6A4918F93C5D23B3C3A325AD8EC77043D207A0DDC294AD3F5BDE0" + + "4033FADD4097BB1DB042B1D3B2F26A42CC3CB88FA9357710147AB4E1020A0DFB" + + "2597AB8031DB62ABDC48398067EB79E4E2BBE5762F6B4C5EA7629BAC23F70269" + + "06D46EC106CC6FBB4D143F7D5ADADEDE19B021EEF4A6BCB9D01DAEBB9A947703" + + "40B748A3490203010001A38202D7308202D3300F0603551D130101FF04053003" + + "0101FF301D0603551D0E041604146A6678620A4FF49CA8B75FD566348F3371E4" + + "2B13300B0603551D0F040403020186301206092B060104018237150104050203" + + "010001302306092B060104018237150204160414A0A485AE8296EA4944C6F6F3" + + "886A8603FD07472C301906092B0601040182371402040C1E0A00530075006200" + + "430041301F0603551D23041830168014F509C1D6267FC39FCA1DE648C969C74F" + + "B111FE103081D60603551D1F0481CE3081CB3081C8A081C5A081C28663687474" + + "703A2F2F70617373706F72747465737463612E7265646D6F6E642E636F72702E" + + "6D6963726F736F66742E636F6D2F43657274456E726F6C6C2F4D532532305061" + + "7373706F727425323054657374253230526F6F7425323043412831292E63726C" + + "865B66696C653A2F2F50415353504F52545445535443412E7265646D6F6E642E" + + "636F72702E6D6963726F736F66742E636F6D2F43657274456E726F6C6C2F4D53" + + "2050617373706F7274205465737420526F6F742043412831292E63726C308201" + + "4406082B06010505070101048201363082013230819A06082B06010505073002" + + "86818D687474703A2F2F70617373706F72747465737463612E7265646D6F6E64" + + "2E636F72702E6D6963726F736F66742E636F6D2F43657274456E726F6C6C2F50" + + "415353504F52545445535443412E7265646D6F6E642E636F72702E6D6963726F" + + "736F66742E636F6D5F4D5325323050617373706F727425323054657374253230" + + "526F6F7425323043412832292E63727430819206082B06010505073002868185" + + "66696C653A2F2F50415353504F52545445535443412E7265646D6F6E642E636F" + + "72702E6D6963726F736F66742E636F6D2F43657274456E726F6C6C2F50415353" + + "504F52545445535443412E7265646D6F6E642E636F72702E6D6963726F736F66" + + "742E636F6D5F4D532050617373706F7274205465737420526F6F742043412832" + + "292E637274300D06092A864886F70D010105050003818100C44788F8C4F5C2DC" + + "84976F66417CBAE19FBFA82C257DA4C7FED6267BC711D113C78B1C097154A62A" + + "B462ADC84A434AEBAE38DEB9605FAB534A3CAF7B72C199448E58640388911296" + + "115ED6B3478D0E741D990F2D59D66F12E58669D8983489AB0406E37462164B56" + + "6AA1D9B273C406FA694A2556D1D3ACE723382C19871B8C143100").HexToByteArray(); + + internal static readonly byte[] Pkcs7ChainPemBytes = ByteUtils.AsciiBytes( + @"-----BEGIN PKCS7----- +MIIOFgYJKoZIhvcNAQcCoIIOBzCCDgMCAQExADALBgkqhkiG9w0BBwGggg3rMIIF +CzCCBHSgAwIBAgIKFeqoOgABAACSkTANBgkqhkiG9w0BAQUFADCBgTETMBEGCgmS +JomT8ixkARkWA2NvbTEZMBcGCgmSJomT8ixkARkWCW1pY3Jvc29mdDEUMBIGCgmS +JomT8ixkARkWBGNvcnAxFzAVBgoJkiaJk/IsZAEZFgdyZWRtb25kMSAwHgYDVQQD +ExdNUyBQYXNzcG9ydCBUZXN0IFN1YiBDQTAeFw0xMzAxMTAyMTM5MTJaFw0zMTEy +MTMyMjI2MDdaMIGFMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcT +B1JlZG1vbmQxDTALBgNVBAoTBFRFU1QxDTALBgNVBAsTBFRFU1QxEzARBgNVBAMT +CnRlc3QubG9jYWwxJDAiBgkqhkiG9w0BCQEWFWNyaXNwb3BAbWljcm9zb2Z0LmNv +bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtAaFEInpz3zbQ43Xe+vYGRl7 +7v9XnDXvnEZS355jMKp+LiSxgcWdpK8Q6XIgwd+Z9mzm6XJH6RJqAWrGR70u/RNs +MUcMe+AaIOOBJDvuyFMLf2RmxQoFHc43J07X/yr/9OWKq6YdWkSPSoqbN2XR12n2 +J+0vLenuZ7Gn7KPSiMkCAwEAAaOCAoIwggJ+MA4GA1UdDwEB/wQEAwIE8DAdBgNV +HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFPs0hXCMv2GI9yDv +lISJQFyNBBOnMB8GA1UdIwQYMBaAFGpmeGIKT/ScqLdf1WY0jzNx5CsTMIHQBgNV +HR8EgcgwgcUwgcKggb+ggbyGX2h0dHA6Ly9wcHRlc3RzdWJjYS5yZWRtb25kLmNv +cnAubWljcm9zb2Z0LmNvbS9DZXJ0RW5yb2xsL01TJTIwUGFzc3BvcnQlMjBUZXN0 +JTIwU3ViJTIwQ0EoMSkuY3JshllmaWxlOi8vXFxwcHRlc3RzdWJjYS5yZWRtb25k +LmNvcnAubWljcm9zb2Z0LmNvbVxDZXJ0RW5yb2xsXE1TIFBhc3Nwb3J0IFRlc3Qg +U3ViIENBKDEpLmNybDCCATgGCCsGAQUFBwEBBIIBKjCCASYwgZMGCCsGAQUFBzAC +hoGGaHR0cDovL3BwdGVzdHN1YmNhLnJlZG1vbmQuY29ycC5taWNyb3NvZnQuY29t +L0NlcnRFbnJvbGwvcHB0ZXN0c3ViY2EucmVkbW9uZC5jb3JwLm1pY3Jvc29mdC5j +b21fTVMlMjBQYXNzcG9ydCUyMFRlc3QlMjBTdWIlMjBDQSgxKS5jcnQwgY0GCCsG +AQUFBzAChoGAZmlsZTovL1xccHB0ZXN0c3ViY2EucmVkbW9uZC5jb3JwLm1pY3Jv +c29mdC5jb21cQ2VydEVucm9sbFxwcHRlc3RzdWJjYS5yZWRtb25kLmNvcnAubWlj +cm9zb2Z0LmNvbV9NUyBQYXNzcG9ydCBUZXN0IFN1YiBDQSgxKS5jcnQwDQYJKoZI +hvcNAQEFBQADgYEAneu4taQe1UhZeV9o73Z6mKYe97B6rBkPzAJ1Io5MrTYMm6mL +CuFTx1Ui7vQtQA6BO05J56zrlj7ue2HTyNoFwYNHFURyWy69GImHf2ITSCf7WZO4 +/fYYvUIauhjXDRxbQezdEWlaSMtC61AfltqQVHGDDGErYJEmVZEg9uGOpEgwggNY +MIICwaADAgECAhAblnGkvBKLg0Gw4xTq2aGRMA0GCSqGSIb3DQEBBQUAMIGhMSQw +IgYJKoZIhvcNAQkBFhVhc21lbW9uQG1pY3Jvc29mdC5jb20xCzAJBgNVBAYTAlVT +MQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDESMBAGA1UEChMJTWljcm9z +b2Z0MRYwFAYDVQQLEw1QYXNzcG9ydCBUZXN0MSEwHwYDVQQDExhNUyBQYXNzcG9y +dCBUZXN0IFJvb3QgQ0EwHhcNMDUwMTI2MDEzOTMyWhcNMzExMjEzMjIyNjA3WjCB +oTEkMCIGCSqGSIb3DQEJARYVYXNtZW1vbkBtaWNyb3NvZnQuY29tMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQxEjAQBgNVBAoTCU1p +Y3Jvc29mdDEWMBQGA1UECxMNUGFzc3BvcnQgVGVzdDEhMB8GA1UEAxMYTVMgUGFz +c3BvcnQgVGVzdCBSb290IENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDE +ZzwSJiVPa70BsB0huwUmSpqlt3rFF0jqxSBIcG2muJDc4EPGQm/ETnbXD5/jpKyF +X1M+PQjhQIU9t2nuJNvbcmn6vsD9/2reCqhfAIW3iGRY51heQzsJJOgWAEM8sRd8 +5q1fJHeyoOLRo0tB9sb1reSp3X1WXGXwLCqqAcjgwQIDAQABo4GOMIGLMBMGCSsG +AQQBgjcUAgQGHgQAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G +A1UdDgQWBBT1CcHWJn/Dn8od5kjJacdPsRH+EDASBgkrBgEEAYI3FQEEBQIDAQAC +MCMGCSsGAQQBgjcVAgQWBBR/elIIQR1GB8AFfJjwxHMHAQyz3jANBgkqhkiG9w0B +AQUFAAOBgQBKjqxz2Optfok9WICUXg46v8ecQL+mCmgM+Ki/Y+3DrZwRwIHx9EQI +lYH1yNyyPArvonVx2XHb6yqpobP3ubCHfpMR02CYplt9A/xpqDX2wwlt7hNahkBl ++Xecgt6wx3e5xNtJ8N0RoOqyh7bjUvfspGfQ08oqgIERk4i6/N0lVzCCBXwwggTl +oAMCAQICCmGHx/IAAgAAABswDQYJKoZIhvcNAQEFBQAwgaExJDAiBgkqhkiG9w0B +CQEWFWFzbWVtb25AbWljcm9zb2Z0LmNvbTELMAkGA1UEBhMCVVMxCzAJBgNVBAgT +AldBMRAwDgYDVQQHEwdSZWRtb25kMRIwEAYDVQQKEwlNaWNyb3NvZnQxFjAUBgNV +BAsTDVBhc3Nwb3J0IFRlc3QxITAfBgNVBAMTGE1TIFBhc3Nwb3J0IFRlc3QgUm9v +dCBDQTAeFw0wOTEwMjcyMTMxMzlaFw0zMTEyMTMyMjI2MDdaMIGBMRMwEQYKCZIm +iZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MRQwEgYKCZIm +iZPyLGQBGRYEY29ycDEXMBUGCgmSJomT8ixkARkWB3JlZG1vbmQxIDAeBgNVBAMT +F01TIFBhc3Nwb3J0IFRlc3QgU3ViIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCmpJGPk8XSOzw6MlrY7HcEPSB6DdwpStP1veBAM/rdQJe7HbBCsdOy8mpC +zDy4j6k1dxAUerThAgoN+yWXq4Ax22Kr3Eg5gGfreeTiu+V2L2tMXqdim6wj9wJp +BtRuwQbMb7tNFD99Wtre3hmwIe70pry50B2uu5qUdwNAt0ijSQIDAQABo4IC1zCC +AtMwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUamZ4YgpP9Jyot1/VZjSPM3Hk +KxMwCwYDVR0PBAQDAgGGMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUC +BBYEFKCkha6ClupJRMb284hqhgP9B0csMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA +QwBBMB8GA1UdIwQYMBaAFPUJwdYmf8Ofyh3mSMlpx0+xEf4QMIHWBgNVHR8Egc4w +gcswgciggcWggcKGY2h0dHA6Ly9wYXNzcG9ydHRlc3RjYS5yZWRtb25kLmNvcnAu +bWljcm9zb2Z0LmNvbS9DZXJ0RW5yb2xsL01TJTIwUGFzc3BvcnQlMjBUZXN0JTIw +Um9vdCUyMENBKDEpLmNybIZbZmlsZTovL1BBU1NQT1JUVEVTVENBLnJlZG1vbmQu +Y29ycC5taWNyb3NvZnQuY29tL0NlcnRFbnJvbGwvTVMgUGFzc3BvcnQgVGVzdCBS +b290IENBKDEpLmNybDCCAUQGCCsGAQUFBwEBBIIBNjCCATIwgZoGCCsGAQUFBzAC +hoGNaHR0cDovL3Bhc3Nwb3J0dGVzdGNhLnJlZG1vbmQuY29ycC5taWNyb3NvZnQu +Y29tL0NlcnRFbnJvbGwvUEFTU1BPUlRURVNUQ0EucmVkbW9uZC5jb3JwLm1pY3Jv +c29mdC5jb21fTVMlMjBQYXNzcG9ydCUyMFRlc3QlMjBSb290JTIwQ0EoMikuY3J0 +MIGSBggrBgEFBQcwAoaBhWZpbGU6Ly9QQVNTUE9SVFRFU1RDQS5yZWRtb25kLmNv +cnAubWljcm9zb2Z0LmNvbS9DZXJ0RW5yb2xsL1BBU1NQT1JUVEVTVENBLnJlZG1v +bmQuY29ycC5taWNyb3NvZnQuY29tX01TIFBhc3Nwb3J0IFRlc3QgUm9vdCBDQSgy +KS5jcnQwDQYJKoZIhvcNAQEFBQADgYEAxEeI+MT1wtyEl29mQXy64Z+/qCwlfaTH +/tYme8cR0RPHixwJcVSmKrRirchKQ0rrrjjeuWBfq1NKPK97csGZRI5YZAOIkRKW +EV7Ws0eNDnQdmQ8tWdZvEuWGadiYNImrBAbjdGIWS1Zqodmyc8QG+mlKJVbR06zn +IzgsGYcbjBQxAA== +-----END PKCS7-----"); + + public static readonly byte[] PfxWithNoPassword = ( + "308205DB0201033082059706092A864886F70D010701A0820588048205843082" + + "0580308202F106092A864886F70D010701A08202E2048202DE308202DA308202" + + "D6060B2A864886F70D010C0A0102A08202AE308202AA3024060A2A864886F70D" + + "010C010330160410286B4EFF0202AFE16583E50C3EB8F38F020207D004820280" + + "C3452B3850AE69FCC0AC426FF3A0421477813259C128D643452219EEF71EBCAB" + + "5E7054B7A195E3F945222864CA37D8F67DDFA9136A93CD7FEAD86F00D4179F1C" + + "557253253C6235295499729C564DE2CE30E131C0D9B3E1BDBB211F8FA9E78B7B" + + "088C63137DF44CF50C293E082E7C57A8D0CB0404D1F5B9D1491F4EF9045181D3" + + "8D528C61F49EB3F1CF11ABB60270CBC10AC4BAF115A5AB52EA22FE4406743695" + + "7DDC1BFEE0C6BDC097BDF092AC6D11CABAE497FC10564E7E7797BC6028CBD75B" + + "1A2339329D439F6557B3CEB77489467FC8990EE832D48E2FF65A7BCB20E2DBBD" + + "F81C762F688E2EE43822CF9DEDE11914DD982FAB2AC141496D912396F6F67E3C" + + "D04D0617F8EC2BE6D35AF9860C384DB8C21FD0B00494FA3188983E6200DE90B3" + + "E6E662C5B07AB202A1B9C3F10F03B88E677EC7BDC2873AA4DFF873DCD714AD39" + + "42C33E63442A855C709F58063D836F8CA77DB9D208F3DB2552D7AC611409E8BA" + + "942BE520D9B4951AB844892D123DF550BA4F22D255069AD77E6C14D730128FAD" + + "3736551CDBEED2022417A948B1BD2567C39DFB561F251C45D2F4B5711B4AC82A" + + "09226403432FEEEEAC5F54590CC2EC7925768651ACF42C32A114ED133718FB1A" + + "9FB0DCC35C10640226F7587BD4EA67C10EF963113D03988C82B3A0B43B1B60ED" + + "BE0A7CBCB6422CD9695BF206A190E8EA6F87E2FABB33549081EFA7A8B04CA044" + + "9DF52E133556781511CDFCBDC55B487DA51C8D476FB635896FD0C71754B2EEE9" + + "A395F355F4A56CCB8CE75D65F73AE8DE80215EA7CA9129FD90EC48ECF26828A6" + + "0E471FA5F34471AD1C21AD200E16C4E4E99FA55B5CF75BC4BF68D1A975E8276F" + + "5EBD47EEB21DB06092F51AEE6BDAA2CDBC9387620A2B3983247E9AA93252D49A" + + "3115301306092A864886F70D01091531060404000000003082028706092A8648" + + "86F70D010706A0820278308202740201003082026D06092A864886F70D010701" + + "3024060A2A864886F70D010C010330160410127070F8C0D10011CBE0CC8489A3" + + "0C71020207D080820238C151B340B678AE8CAD6C6B11E0661E91FD1F4107F0DB" + + "687F039362D88AFF382E3557A75157D8A6D93F0777AD41D7520D32916677699A" + + "EC3DAEE462344BF18410EE07E83811EC26569FF9CD8D13A77F387D6E7C5C21C1" + + "6BC9936DB1B8AB614A8B4B6F9975E4A0A5DFCE29EF14E833FB2526805901A782" + + "724AF6BA2A80E93A4C4BA07B1C1319169E200A4B7AB100AE2CA135512919120A" + + "1D1AB57EF6DED00144F87D051391676D205196ABB4B211698BF137436D6E39D6" + + "719737B66AD2E76D5766D36E87108D79145021C77328A9F2ADDF44EC2A95EEA3" + + "86DCA32FB53D0AA92FB5C5BB7B49CB1F1755ABF195C7274702681C616C21BB05" + + "817B5FD344FC25CC6145A4FFE36F4D5BC131434E6C44BD14769EB08FBDA0D1A0" + + "6DCF2D061FA4A2FCC45A30125680507AACEE7903ABA0C1D36395925A82741797" + + "CF93A11F249D7E7D8228F8F6AFD03FB317D1F2BDB319C0AEF15E19E9DB1067B1" + + "A6CC12CA458C33BAB31C3F275C45A956F71CFF939F393EC7D20E13B397E64263" + + "702B54DD228D0E1275B39A77B3B1A28EFD5C7DF2643CAF7AAF8574988CDF4112" + + "E057F715331F6E75462E6C948BFB92C5BC81B84FBB47FB97AEB3D8C228388B94" + + "CDFA0E2A48F05A32EA9F2CDFAE2B0CEFF815531B148C358ECE1D23F7B793A1FF" + + "ECE491E990BDF231756600B87FA2F7F3AD2C2AA2F6DB42FC6D62766F28F60436" + + "FFA4993C87BE6631D7CE6C06C5B7AE7218450C504ADB401B9FBA5FB2FC6A8289" + + "B42D51B4E1AF159AE7F3A63BA8644C8C5A99F108FC25A27DE54E268AF8A259D9" + + "F6E3303B301F300706052B0E03021A0414AF311074EBABE699402460BFFFE14E" + + "4D7314FCB4041469835268466D1390373566F7034C4736346CD17D020207D0").HexToByteArray(); + + internal static readonly byte[] Pkcs12OpenSslOneCertDefaultNoMac = + ("308209180201033082091106092A864886F70D010701A0820902048208FE3082" + + "08FA3082036E06092A864886F70D010701A082035F0482035B30820357308203" + + "53060B2A864886F70D010C0A0103A082031B30820317060A2A864886F70D0109" + + "1601A082030704820303308202FF308201E7A00302010202146543F3FA4FA3B8" + + "75A1BFDDB3CC23ECBFEA736A00300D06092A864886F70D01010B0500300F310D" + + "300B06035504030C0474657374301E170D3233303432303137323530385A170D" + + "3234303431393137323530385A300F310D300B06035504030C04746573743082" + + "0122300D06092A864886F70D01010105000382010F003082010A0282010100D6" + + "C77692CB15D98788AF8C00AA0C626AE45015C8300364C0303F09A1D48DFF5599" + + "A3B8DEC49365E28274E2E33288ECE042322CF8B2697E760D0FB4E8894C209859" + + "9D7BCB39DE74596638D11C87F8245EB245AA0FFDDABC45F804158776B6A1980F" + + "13F4C87F2BDFADEFDA91C3628544BF5F328E5F1215B5F277F6C7B89413720B22" + + "9FA9DFD132A7CC187F88154D6C091841461D2E7C35F75C1A40BB99EDC3C3873D" + + "51C2F68F804F58C0A4FCBA8001F6B92825581B4A9391C6A770614F6BD82B39AF" + + "13094EE7E49DCAB7FA2C751533B358C874148BAB88B915FC99BB8C8F907A4317" + + "3FA14F3B26EF4FE5B014D107DF73E5A2FC0E9ED651CB376A78CB1177E5D83B02" + + "03010001A3533051301D0603551D0E0416041479B60678BEFC1E3784E8A780B2" + + "30A7E826DFD627301F0603551D2304183016801479B60678BEFC1E3784E8A780" + + "B230A7E826DFD627300F0603551D130101FF040530030101FF300D06092A8648" + + "86F70D01010B050003820101000ED62D8DCEF486F04C6047AB0C2CB25FDF1923" + + "7E46D1AD9C16139B8C0A0236EDB46582AE45EDF2E242947A81AEE2AF4BA9AA9B" + + "3297FDD76B1C7F235288802DA590213B4A5E7986B828E072665D2855860B86F8" + + "F625F2BF6111938C54563DBE8AAD21DA2A883F901CC2D7CD08DB113F3468384C" + + "388A6B07E4C301839D4F0E8BD38920FEEA4DCAC3ED817BE1D0F3BBAC45E772C5" + + "41B0819DA4F0EBAFB952C082A57714758394C07416EAB0B53ACBEFD2F2E2138E" + + "B91CE02F4F8E008C976CDBED10340B79B9F9FF8CC94C65EAC274C4A08E1347B0" + + "21268E581CDF1730AE9DF3B959508262DED10B040A1D8346F0F544A2BBBA7454" + + "C2B2A7218B21B64D82B8C04A0F3125302306092A864886F70D01091531160414" + + "D222A8E9C8CF876416F9A2132202326C1AE63C203082058406092A864886F70D" + + "010701A0820575048205713082056D30820569060B2A864886F70D010C0A0102" + + "A08205313082052D305706092A864886F70D01050D304A302906092A864886F7" + + "0D01050C301C0408E0AF915D08BBD72002020800300C06082A864886F70D0209" + + "0500301D060960864801650304012A04100CA536131D2DDA95F84830C4E47E84" + + "4A048204D0075FAF61AAE642CAE276A7AED857DEB08094880EA949F763F54DA3" + + "B1C0CED0D383B3B305F5D4C033B33BCFA08385F58BE862048F739B59C30AB3C2" + + "1B8C877D2056D27CD1A015AB8EAF86725CB5F2892C667CC02DE0AB215D4859F4" + + "A4DFF4C6B8187B4DB5DD2BE8217482148D0135E1B6B7784E7C8515FF4155D662" + + "5E742D45D3DDDA357BB7FAA90601773C96468F2E023242CD6B087CD0C504C124" + + "8D747BF0465F33EA2904682678CFE57B4FC9DD8162230F7C6980B399D793B796" + + "08668B7FEF17430E128AADFAF37F7610924845327A631F51B4AFDBA3D08C2408" + + "8C6059FE0495003E210F53E616CAAD669954A7E517DFF203746DA1A6693BD23F" + + "3DFF096DF050DB68AF7E4213F2A40D03C274C3E1F224B66A3481001C445E99DF" + + "F5EAE97A9E7C34D035A89DDAAF982794E2BAB630EB61F96399B891F460463052" + + "CD6176E6561DDC60C9FE476D125758AD3EAF9747818A4F963181928E79FC667D" + + "396EFFD95CFF25DEAC00E0B1A4092E0F0AF9132116A78C75B3306469F64B536B" + + "FD6130B756899A7FBC79EB11BB0F124A8BF08AD818923ED9D1F93A79C1C1DEC6" + + "58B8859794A3C02F9299901F4303CFFBECD354BB77FB5BF63146B8F85C4C0F81" + + "85A7A6C8A28C51C33340DBB2204AFC70D4D79AD2C35514E4BEB413D519A12288" + + "29E7195A730A1A65FF9585E32F91F232DA23BDEB2DFDBEA5377AB9E053198899" + + "B140F816979D53F738601782C7CC24E881CF337F5CF50C24EBE2ADAD74FCD523" + + "2E620B8F144252BEE0B0FCC08AB239526B1DBE7F6C3D1FC8184F60B8B30C64F9" + + "B42F5B674C475A4D088C81C8D9102118273C6DB632CF7E127E037D6B317DE6FA" + + "C7B2752F1173851E3C099F40F207B0EF57BF80C73C224B70D0D975C15A8A2193" + + "2E34EF1113C48EB8258968B37D2358216E6302192C6FB6A68B325D7B731CDABC" + + "4D1FE7A4EA25AF60F5B224FB8BC0A3DB8ABED63FC1B91FB43AA4ACCDB32B46F8" + + "1441CBE62B1C38A4FC1E76E2419579890A10906044E18CC1D06A1866EAE98B78" + + "2BC5503BE5E24638C79FD804809F69D8019B77F498A36CEAF1582DAE245CE949" + + "BE493751FDD232A02AEF72DAF9B527AA29BF8C0B0384DB29BECE1A1E3B81E681" + + "00C047A22E73ADD5ED5B19BD922279BF8E0D4834B69408B5A9844B8421D6420B" + + "3BA243BEFBC2AFA74DA94D34B58BD827D59025438EF4E982E0BFA133F5EDB181" + + "FB5E3723CAF8BF0AFDDF20DF75A433850DFBBDAAB3FA7B61962A66A0AEEC1476" + + "B12A016CCC2B12C16AA8547D46B33F3B6008A65C165125BAD61345D9777E40DF" + + "232190FB47934E3B935B963DDE4717F4F4D1E932D1D7020226C0A2472327E16A" + + "F8E16DCEF37C52E58A65FA44842E5A323DD768322DD0F22BA1CDF9E54A3C3ABB" + + "7010A9E4C3D856719CD025C1EC7AE2DBCB8DE8ED68AC542797978C47310BB3B4" + + "D34A4244B3D0D01338A2EF8A43DF1180C01BA03AB9A382C888912BBBE541F8A8" + + "7C0CDE2EC2405F67AC22049410E2AFD9A8E4B8A836055C399A5422DB375A7520" + + "99A947C933DEFFC26532F399D08978443C6D040FD29C5842B6E0A8235D44012C" + + "6783AA66ACA31FCCA155AD6429BEDCABA71398B287AFFCE96AC8ECC36CD222C6" + + "7140E144656A0D8FCBDDBF5E96390DBB7C7C093EEBD8DFF0BDB68568BFE77D7A" + + "A7A3B6C94CD9737B8FF3C4F9684062E175AE9EB8DAB5B8956EAFB3A5EC84C9C7" + + "376F5F4B588E60875F3654A15899FD3734455366083125302306092A864886F7" + + "0D01091531160414D222A8E9C8CF876416F9A2132202326C1AE63C20").HexToByteArray(); + + // This is an RSA-1024 certificate, "CN=outer" which has an extension with + // the unregistered OID 0.0.1 that is a PEM-encoded ECDSA-secp521r1 + // certificate ("CN=inner"). + internal static readonly byte[] NestedCertificates = ( + "3082041930820382A00302010202084062D86F6A371DD7300D06092A864886F7" + + "0D01010B05003010310E300C060355040313056F75746572301E170D32343031" + + "32353031333630315A170D3234303132353031343630315A3010310E300C0603" + + "55040313056F7574657230819F300D06092A864886F70D010101050003818D00" + + "30818902818100AC24F75F85C4C38E6DBF57DE889AED7598DD01202FACC00EA7" + + "4EC449DD420E7CB49992E1BCCC69B3EAB8B2AEECA5B2BCFC1295DC82B83EDB71" + + "C1764191EA0F73D1BAB26D03B95F322EF8299B1DADAECCAADEBA052BD3BC2549" + + "D83AD1C2FB2DC556370306AC3CBCE09F3669448EDEF8FCA12164D793D5B456A7" + + "418393899E00590203010001A382027A3082027630820272060200010482026A" + + "0D0A2D2D2D2D2D424547494E2043455254494649434154452D2D2D2D2D0A4D49" + + "49426D6A43422F61414441674543416767312B7233664B4775457054414B4267" + + "6771686B6A4F50515144416A41514D5134774441594456515144457756700A62" + + "6D356C636A4165467730794E4441784D6A55774D544D324D444661467730794E" + + "4441784D6A55774D5451324D4446614D424178446A414D42674E5642414D540A" + + "42576C75626D56794D4947624D42414742797147534D34394167454742537542" + + "4241416A4134474741415142513143437A4448737A724E6839445456697A6E75" + + "0A4B38516F496B5649324138494E676B4B6F6B3649704158484C533058767333" + + "4E43745152396973486C5A376C707844366C6B72376449793363396756427063" + + "630A7131734242796B73534854745A4766337A2F6E526E5A3961684463755444" + + "2F486E71302B326A33476461423557594B6733336E65337730423258385A7377" + + "65390A46774F2B335A6954734D6D647038312B6B4D6C76463775453236597743" + + "6759494B6F5A497A6A304541774944675973414D494748416B4942454B6C4654" + + "4978560A434274324839636A30595042493632457734752F665A4B4A53657272" + + "7848707244476C67546B36635075585345723569395A397731505A3872654A51" + + "7A7367690A6C736268363653584B4F6C67466945435153587972457736633565" + + "734B587A6269484F7762564E4756305A4430424A6761316533667144507A4170" + + "75527734510A75656A636749684E723577504E7A4E696A464D57697944716D79" + + "6E586D4B6E627933457050396D370A2D2D2D2D2D454E44204345525449464943" + + "4154452D2D2D2D2D0D0A300D06092A864886F70D01010B05000381810032833C" + + "38E5F66D64880C191D247CE8B819899B20F284DD59CBDE1731A4D0FE09A95A52" + + "D5A7D049CA108BEB9673FD207C842B324DFD8086C9E1CDAB931D7403730E0521" + + "69943C58ECC3DA6E11C6ED4F16455152D6FF4D104C88976F9BA88120B0889563" + + "1378357F297A6B3E444296C06C636A589973250F2A096C39C1EDE5C1C9").HexToByteArray(); + + // This is a PFX that uses mixed algorithms and iteration counts. + // PFX { + // SC[0], encrypted, PBES2: AES128CBC, SHA256, 2000 iterations { + // cert, keyid=1 + // }, + // SC[1], plaintext { + // shrouded_key, PBES2: AES192CBC, SHA384, 2001 iterations, keyid=1 + // }, + // mac: SHA384, 39 iterations. + // } + // "Placeholder" + internal static readonly byte[] MixedIterationsPfx = ( + "30820A320201033082099206092A864886F70D010701A08209830482097F3082" + + "097B308203FA06092A864886F70D010706A08203EB308203E7020100308203E0" + + "06092A864886F70D010701305F06092A864886F70D01050D3052303106092A86" + + "4886F70D01050C302404101215FB90500DC30882E7D84625F97923020207D030" + + "0C06082A864886F70D020A0500301D06096086480165030401160410558E3FAE" + + "FE92ADEC4CF1405E7DE7C5118082037095F887CE32E748EC0D647062286EA9D0" + + "C3C04213ED9431FFE0F6028105D94D088684A55C997630D16EF7CE7D49729C30" + + "1F750D8864B988ABBAF551A8819ED7D17CBB810CA3DC39EBD804B3C388C73BA9" + + "320783DF3AABEBAD9F44E4ABFF931AAA95AE874E348B49F35749614D9EE94F5E" + + "EC7A5C597FE8CB6CCDB7A0721EE5836C8F839D83E1577A753F8B2B7E0C2C53BD" + + "D365322E211A62DE061FAC3ECF770CEBCF37AC2604A1CF318C4DB1BA83C85779" + + "3BE57C898EA80D52E0F83B17F877210C1A5E819F5C35DE6DB5EBACE1BFB3101C" + + "C2FE9FF4983BAB7B93CA5D466B855D973FB5D72D8DD109B61FBA238320C886C0" + + "8FC141168C43B946AAC28C114B20A667D189E473E9779432EF64C965459AE9C4" + + "6430B94E19BCEB4733D3038D9F65B85D79B7A1DA954887BAE1DB0A999135A09A" + + "25C38616DBADB9BE50F729925F23C107F899341F76B4B2161CCAF8C499DF7646" + + "F7DB16A21EFD90097D75EEF893B8B8115F6A8816B2D9628278F8ADAFD0E4AC7F" + + "14BED13FFB6DD7FEDE1EA1990CACE772DBFDACC2A4547F910D3F2B238FE86A4F" + + "1BAEE8AC282F918D3F82B8149DD3A99B78D433AB0F5592E7B7B25FBC4A726520" + + "A49CF2FDA60D747F19031B34F911C442E20BD91600A32F9A4FCC1E3DF330171A" + + "4E0721CD01A93CC20C2B770990029C30306945AFC7E3ABD9516D8C6F359D904B" + + "5F32F3BAEC8770A8BE15C89E4B836B1F20D29A7D3D3BB7491EFC9B88A161B598" + + "02819D2EC695000C2F83B1A363217CC2B0A70A4A226993BD32BE302DCD157EEE" + + "3F9DBFBDE0375A03DEA2761941F49D42A8F5029DC3B65E661B61DFBE5F2527D6" + + "C59927B5A0857FE7B9836A63D202895C1FA6FDE40E70107659CE3E4C7DDDBC9E" + + "04B85A46104EFDF6692BF898C565480F5A5163231167DB69F873D1341CDF8DF0" + + "7BD4473F58162A3EB653C6567C2E3120B0034B9E35D22838BBF02968651FC1F8" + + "873C405237B7F29297ED47CADEABF248386CE94D215C8D5731A503623C9CA916" + + "C5896B8F713A044ABBECA764D36D074DF523D989228B89589463DD3615A7E844" + + "E7E9E91BE0BFAD79218CD7B52A313458BBA8FC660B5DF8513464FE749EF3C201" + + "5C15B2EF15F63D5FDA726ED8E04B3B5E7DB8610367AB08EDF120DDEEA7382AC5" + + "1C778B8069E09F2EB68D19154DD10129A1E0DA0717AE1E108B2496C69904D851" + + "18B37E06FA95E1F5E75371D8C7EC3BC80C817F931744428BF03C1F51A0164C0A" + + "3082057906092A864886F70D010701A082056A04820566308205623082055E06" + + "0B2A864886F70D010C0A0102A082053930820535305F06092A864886F70D0105" + + "0D3052303106092A864886F70D01050C30240410B5D9DF064271CEFFDBC3C067" + + "F6DFEB74020207D1300C06082A864886F70D02090500301D0609608648016503" + + "0401020410DEC794EDB555B89CAFB3671FAB4A52E1048204D050B2A2E9E3D3C0" + + "84D5BFCAB1C7C27951C0B72FB45B52089605DBF5E60550521DEAB0A66EF2C182" + + "3AE2F4EC21A1B8BF65D240DF20759C66AB0C6311CABB30F9DE1290B81CF69AAE" + + "7B6F42A4E9BE7BB6F09058C7D0D6FBE6F24EC2E3457680907F000C5B457B1E7A" + + "E394A7A70E6289C3B009F522399D5CC41014F12A336926F3AB8E2A3AF5496BC3" + + "27B073206F20F131AFCB627A1E67B9B457C44DB6A6C10EEBB8856E0BAAF99D8A" + + "9D24D7C90AC5EA9EB14C66315E77F158948988BB729D8E0796741CAE29894DC9" + + "6614C2B06911013C168C7A4E6C46F09D1AB0D7933729FC88BA47A41BFAB0AB53" + + "C60FEAF6A93688E67039184B598BDF1CC95C3967F9ECC649745E265974713102" + + "E271ECFA6A067F10751C2A4A70A94FA39F37E944996C843809D82990A9D44C18" + + "9EE7B9DC8534F9B821A5C56A61DFAAF470444883200B3FFFAA23DCE84375937D" + + "D0149869B2683773F650B70003EB025A04E170DFD97D4F86D2913E91F757BC01" + + "5F1F85F497B1400052E0868ED6844E2390F71B036B524E824B3ED6381BCE2600" + + "71A5F6EB12E9C1C44BBE0664217C4A44EA07E440A33010A0CA46647E7FFA4F58" + + "74F1EFB9DD330DA2CD50AC01C489639520A1A27B603F44831EF235F0540817F2" + + "196AF45D3B9957D272B5549BEC5507F6BA443A37F54FD6D6AEBEBBB7AAE15F7F" + + "8C1C46D68CEEC2E95426D177A390E7C50B0122BDDA9EFC104294840F6D5374AD" + + "90952842C0EDF796AF48C6FDD4AA02C81AFCD606CD0E94F3DF2B06B649A47D9D" + + "7C8AE23E1A776E1D149CA25E4929238778B19E33DE05DE577B763305B4884B8A" + + "7DE31BB9712477C9CDC138B52FB2D59CFE42A1CDAEB0E205C70B38CC4B88E0A6" + + "ABCC41D29D11947FF5B03633A5E164ED22F10536AAD07DB2EB52A2C696EBB135" + + "B5220839D1E5A7DB6C2B8DEB09C883129BD5253F68169F9D5F5A2F3AB17C4921" + + "10636978B8573A5B4E1FFF5A4C3E75E2F03AD71AF874C544474E1B41969416D6" + + "D8FFFD595428EC7928BD17C652F67D9B6B150C6D4A9352C405BED162492A5FD5" + + "7EB6BA5F77AAC2BD7E4EEB6B0BD2E4329E2CA8A425F88F4743B25F259E292C04" + + "483472CA79FF52271A830AA6A27A52C3531E2B2503592C017A7CC00F91F63F73" + + "4E3E56746475B8B338440B7D5FAC87A90831EE78A2DE4FD6F60F1C66B31A520C" + + "44B73B09D5419451C4A32E8E1A5BB17E44B9FAABFB07021747093DBFE248BACB" + + "E2BB6C7F145DE7397A2B2AEAA083EE57F46C8DF85FA2DFF4582C2E3CE3CB2E91" + + "706395A63BA96343B0567E41A33FC964BC8C03CEDE5E3E1D7A8B285F745EEAF5" + + "CD1382C86DA82922DA1772158F8BCCFF70DA87A64602033560566F33A3793A4E" + + "5C404D2C69274EEE9E82B5B97B0760FD66067888711C572E84DB382491792CB2" + + "C7BFA472E6B0D70D529701C2A0B730F5E1E0A980EAD56EA323E0008C70D62F53" + + "5B9E0533F9A4B7CFA22319274E68C8B4737E5DAB5B1956C235E7EA548E24E23F" + + "F9FCA61D11DAFC6B90E0BE8E96A66B6973D5F025C0619D283CC92C3224000FEE" + + "F9BD002E7EFAD4C737C4CFEB42858DBDFBC489B1131AABDD1868C58EDCAD35C2" + + "AE1A42BBCF0A2A90E0557A7A5F79F2D92D19E39D505994163CC94F5EA56009F0" + + "5E9ABCAF24807130F90FCA606D5448C103489BB53090EC603394705B472132E4" + + "FAD50491069F44040A0D66F7D3D5C86593D61C9D37CDE3BBE5651EA2E104B324" + + "3272E665CDBB139E063112301006092A864886F70D0109153103040101308196" + + "304F300B060960864801650304020304405EDC86442FD573401BD2DF0F95356A" + + "1C1454F401231B7F772179626ABCB220C8096AC0ED6C27CACED7D94615768B61" + + "8BDDCF4B8A0996E019BD418423F79F173404406B32D0D889B85234D716C87F3D" + + "EEBD62B5DC14984FDB9EA9FC765B340F54D3E5203C6A9F4F23913B22605A32BD" + + "3D8D120CFFFE4ACB83BA4D488C67271E38CD40020127").HexToByteArray(); + + // This is a PFX that mixes encrypted and unencrypted certificates. + // PFX { + // SC[0], plaintext { + // cert, keyid=2 + // }, + // SC[1], encrypted, PBES2: AES128CBC, SHA384, 2000 iterations { + // cert, keyid=1 + // }, + // SC[2], plaintext { + // shrouded_key, PBES2: AES128CBC, SHA256, 2001 iterations, keyid=1, + // shrouded_key, PBES2: AES128CBC, SHA256, 27 iterations, keyid=2 + // }, + // mac: SHA384, 39 iterations. + // } + // "Placeholder" + internal static readonly byte[] TwoCertsPfx_OneEncrypted = ( + "30820CAB02010330820C0B06092A864886F70D010701A0820BFC04820BF83082" + + "0BF43082029406092A864886F70D010701A0820285048202813082027D308202" + + "79060B2A864886F70D010C0A0103A082025430820250060A2A864886F70D0109" + + "1601A08202400482023C30820238308201A1A003020102020900BD698EB46606" + + "F1E4300D06092A864886F70D01010B0500305E311E301C060355040A13154D69" + + "63726F736F667420436F72706F726174696F6E31173015060355040B130E2E4E" + + "4554204C6962726172696573312330210603550403131A506C61696E74657874" + + "2054657374204365727469666963617465301E170D3234303531303138303033" + + "375A170D3234303531303138313033375A305E311E301C060355040A13154D69" + + "63726F736F667420436F72706F726174696F6E31173015060355040B130E2E4E" + + "4554204C6962726172696573312330210603550403131A506C61696E74657874" + + "205465737420436572746966696361746530819F300D06092A864886F70D0101" + + "01050003818D0030818902818100A6638EFEFA9A1B364574D9A7BA4A85017E73" + + "61831606B717DCF8FC0F5B982583D5460BCD216E99FBC15ABF62B5C30FDC7CF7" + + "D13CDCF9E0A0A25C26AF0AC14D116569FAA6496CB87ECB0A3B87AAF624010630" + + "4E7F0DDB63FF7EC95396F2CF4E57A1F0356414C7D1032433831C327AC33DD264" + + "FC7B1BA567FBE360AF35446B63110203010001300D06092A864886F70D01010B" + + "0500038181001E2C2AC3FC484C42FBFC3A0D027E438BDBF6FDD4609BF1E11BA9" + + "CDCF50D121BAEDFA5361619B469D48F1CBA203F361A2E7D662A88239A500D056" + + "CB38D215197A3F5FFACA5AADDAC875D380C4435C0E8633243174623BF59836F0" + + "8DBA917C5B4A2270579574566FFA29A7A5FF0415D34E8CCFFB9EAD4BA8EC90BF" + + "B3A2F7041F6B3112301006092A864886F70D01091531030401023082031A0609" + + "2A864886F70D010706A082030B308203070201003082030006092A864886F70D" + + "010701305F06092A864886F70D01050D3052303106092A864886F70D01050C30" + + "24041078790CAC0DA9B3E9B0EE236CA8073CF3020207D0300C06082A864886F7" + + "0D020A0500301D06096086480165030401160410256DE9ED9807FD7D30597DB5" + + "51A5815080820290D8156A7DDCD344FE96B8D8BBA4303A373108D725BFC30071" + + "DBF716A7C1FD1B598BAE1FBC9A77CCBB7319646E83B747B59330760B6EBC29B0" + + "91E591FF0030ECB28626BA1594FEF6F0A01B60F2548AE1577FD05E7CAC5BCFA6" + + "2422E71F551FC2A8ACE488E871C03E1E02A9DB4ADA9DB335466EE1A6E7AE6B9C" + + "AE118AF4008879690C446F0D4A03740E31E4879B163E58FFA46394DF57CD98AE" + + "FA3C44E94BDD36D31A53EB2C9A74EA9F45718370106F205A81837A05952E7C17" + + "3460A2400195D658671BE62E76AAD565D848109BE41B6F06CA87A7B7676B9A5A" + + "525B6ADDE80A68A87FE82C8547D611F6DEC5C2AD617D354216CC7E724DA2F7A7" + + "BC67C336A68F7A8ED4F555E53330DDA1318332E170458D10E7A1CA60D87F61C5" + + "89B8BC4CDCAC26BA341B96BD20096B89977750B1E2BACDEE23130825B827F5CA" + + "E73373AD2258201B4DED998F003E1084BE969F5B1EE33B8443BE01C509927876" + + "1D69A1E48662EE91DF5211176DA5495AD54CB50EB2A2A3E077E93946958CAFE9" + + "5F2FB3450B6269F3541BB21B6F129288115E177594D6EC0DD516B8882100C9CA" + + "DB9874064B3A1F051FBB0B43257F0644A05C6D416C0652696E89DB6E43CE5E92" + + "94D05711CDBF7B304E076D73FEDA97A7E2DFF398761DE6425730CB576D26F49A" + + "2CCD93BDD0E410F70D4EF8DB6BCF221BD3E53C472CEA9C40E09A284923992BD2" + + "12B32E36BBB06EC3000261E8EC9D9E389F663DBAAFEBE0CF671E771A246D033E" + + "AFFDF400B94D8926E23FD660E0A8304C01D490EF54F868506EE8AA255F95E00A" + + "7E1AA1D18998B9AE8E0AA0472CD152A674536F5D2882502247D2B056137BFF97" + + "08CD8EC61A3339CCB1DF43DC60A0A75F9260905BB3D339580EA5B5B7BAA92374" + + "4A8850389A2E68B98FE6EF78D78207ACC858A97DEC55C0D03082063A06092A86" + + "4886F70D010701A082062B04820627308206233082030E060B2A864886F70D01" + + "0C0A0102A08202E9308202E5305F06092A864886F70D01050D3052303106092A" + + "864886F70D01050C3024041077CF0CA72DCB1B541E6F481D3A51470A020207D1" + + "300C06082A864886F70D02090500301D06096086480165030401020410503141" + + "7A0A42803706E84B8C7470CD2D048202807E3108B51EA4D80D2E7CF358E08C6E" + + "A30087AE0E9F80CF1B022A0DE0BBDA715733141C6877276864DE98D439D61650" + + "63ED517FBF206450AE6B29835F89914003B8D77F0F15D6FB073EF27F90E9D851" + + "ABE569D3DE6968E8F61EA184B484B4284C2FD3803D9AA4DE083F18A64B2F9320" + + "156004E49C4A179CF8800799451563200CD998CBA779032302CEA4227A78CD10" + + "8FC8BFA6C114F005A2937948F14DDDF05AEB543F4D2FB3C56FA7C1F14B7ACA57" + + "0CA8554A6EA4AC1C0FD356E3D1DD568F97D6EDBFD3B09C87BC2013A88C442993" + + "4C73F442A5D5DA3563FCB85EBEC41085843D14EE88E582151E6855741106CA1D" + + "D775A5793E8A0B4E036427E6D2FC9F0AA394085997E42875E4EF5742099141CD" + + "70B4AA887C40E3EDDB407C39F3C474F95D394D98DAA68BD1005F1223B6E58B52" + + "AA552FD25652AA871F03A53A58922953DB120BAFB44C29DCF9C9C2F96DEE3BEE" + + "7E0F7FF8F04C98ED631F6A845B8B89F30EFB956846BA87E9B3F6FCCB49FE0F21" + + "10139E855389C8EC5120983A09D5F4D655E64058ADD1E8C44F3B6A4936E6788B" + + "E939061221424D71666765D99FE6D883C9E8079992D585F73B0CD57947D38A54" + + "FE7CBE93BE117675A3E52F708B2A6BA6C390BD3BE01100243E7B7B5CB8A9F501" + + "CCCCD082F9867B4DA0A9FF3DC5BB0F0974D4EDEF9FF89F6EB355BCE07B00A32D" + + "AEE443D46EF19A96E6EB3EA4C0B141606A1FDB200D1BADBC11954951383E8935" + + "4603C10E241353FED1502D8111E42DA45198D1586CB0938947F3856CCC855F84" + + "231F131D636DFBB46394A3EBEDAC9A4DAD0DCB32163C57A9B5DD21BC56CA969A" + + "F574E38787DFA6EC2BA063E60375585ED5B9E8D5B6D1BC162D63036C52FF4473" + + "E0A4FD96AA055ED4014C3B0E4F7A098F553112301006092A864886F70D010915" + + "31030401013082030D060B2A864886F70D010C0A0102A08202E8308202E4305E" + + "06092A864886F70D01050D3051303006092A864886F70D01050C30230410DE7A" + + "16E6BCE889C8A5F823E7670FB9E302011B300C06082A864886F70D0209050030" + + "1D06096086480165030401020410683B6EE3E1287242ED73707ECE1F271F0482" + + "028018435588B8BEA17EB47E7947D4FE82D66F7C26850B840C0F06964DC99C56" + + "8391707178494F177EFB65E17D20D3C39E80A00342783B33164F371902D76658" + + "78A79802FE7D9EB31D21137B56692D1B7E2B38F54B00CEE506867968420CB4FA" + + "9EE135BA960C0D40EC22714F5D8576BAF03269D19384C246A60ED4B9F0F4268E" + + "2E135F7BE41536D844718B7339EE14A1CE261B8A27C2AA2AAA90C98F9437372B" + + "0448579A0C392B347C7E95A1A7617A47218F63D0C52C88A3F93AC47BB9493E2C" + + "67DE061F28E90FED463E392C6743C8E6F60524A2455C5E46218E357AFB31553D" + + "0AAD18A2784719D281B12560CB904527D912BA2B40790D9068661C01AA4B03E0" + + "B5316D6FEBBC38087B4DD46CF2CC0C98B6F488AA9940C6FA16BC8BB1853CD0B0" + + "E41D85F382F5BE36E5A821C54EBB8C34DD7CD7B970315FF65B657AED6CE2598A" + + "6DF4E96370815660B3711FDE0A2CA0AB5C2B234C95B699922CFC4E95B781F783" + + "F20373E8E23EB6D518C797A21B401A797DDAF8A823602ED38135F3FF3C555C7E" + + "7FA7B800F4535B781B4F6B21BF2147D8C20388CDD48DB3C17413DC4FBD347368" + + "809762C4851C28CAD52B4F916CAF53E2D5E05ACAC3E85BA1F63525F57F456059" + + "56B7830AFBC50D0CE517C282AE5DB5638635155073C567A82A7ACDEE6860D18A" + + "1CAACE341845A41470F47E7A440F21A3DA96CF986181E1F044B28FB3EEFA0E41" + + "82B93AA2FD50A23B045DABCD774519879CB824B9A58A1859ED553578508B5CF7" + + "85BE3D2A67F0FC508B94A738162455CD11A4BF0C3736914D8C5A99F9D5ACA959" + + "B1C4D5872BA852DD3F8B2B8C231017AEF373B3B5E7B65B6DF7115DB99D5EB11F" + + "E80E8CB3E1D3CE6F50DD7B255F103DC290B7287D6AD7AB12D5242E13092DB9E1" + + "ADE03112301006092A864886F70D0109153103040102308196304F300B060960" + + "8648016503040203044044A762FCCD979F096F1C0DE0C4C584E42C1848DB1138" + + "389DAC33048D6B81DBBCA7E031E8620B37C14F85257DA4A6F57FE58E939493C5" + + "928343622B80009D8C5B0440E74D33A5F7DA48801A4EF4FD65D0F442F26C845F" + + "A418D3E0D78FD0285A92A74B433661F516C6955EC40FE46DAB813B6AE940C2DE" + + "FE8F8F5E32E6B491C999D598020127").HexToByteArray(); + } +} diff --git a/src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestFiles.cs b/src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestFiles.cs new file mode 100644 index 00000000000000..7015c10fd621f3 --- /dev/null +++ b/src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestFiles.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using Test.Cryptography; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + internal static class TestFiles + { + internal const string TestDataFolder = "TestData"; + + // Certs + internal static readonly string MsCertificateDerFile = Path.Combine(TestDataFolder, "MS.cer"); + internal static readonly string MsCertificatePemFile = Path.Combine(TestDataFolder, "MS.pem"); + + internal const string MicrosoftRootCertFileName = "microsoft.cer"; + internal static readonly string MicrosoftRootCertFile = Path.Combine(TestDataFolder, MicrosoftRootCertFileName); + + internal const string MyCertFileName = "My.cer"; + + internal static readonly string SignedMsuFile = Path.Combine(TestDataFolder, "Windows6.1-KB3004361-x64.msu"); + + internal const string TestCertFileName = "test.cer"; + internal static readonly string TestCertFile = Path.Combine(TestDataFolder, TestCertFileName); + + // PKCS#7 + internal static readonly string Pkcs7ChainDerFile = Path.Combine(TestDataFolder, "certchain.p7b"); + internal static readonly string Pkcs7ChainPemFile = Path.Combine(TestDataFolder, "certchain.p7c"); + internal static readonly string Pkcs7EmptyDerFile = Path.Combine(TestDataFolder, "empty.p7b"); + internal static readonly string Pkcs7EmptyPemFile = Path.Combine(TestDataFolder, "empty.p7c"); + internal static readonly string Pkcs7SingleDerFile = Path.Combine(TestDataFolder, "singlecert.p7b"); + internal static readonly string Pkcs7SinglePemFile = Path.Combine(TestDataFolder, "singlecert.p7c"); + + // PKCS#12 + private static readonly string PfxSuffix = PlatformSupport.IsRC2Supported ? ".pfx" : ".noRC2.pfx"; + + internal static readonly string ChainPfxFile = Path.Combine(TestDataFolder, "test" + PfxSuffix); + internal static readonly string DummyTcpServerPfxFile = Path.Combine(TestDataFolder, "DummyTcpServer" + PfxSuffix); + internal static readonly string PfxFileName = "My" + PfxSuffix; + internal static readonly string PfxFile = Path.Combine(TestDataFolder, PfxFileName); + } +} diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj index 06f148fabc7ef3..fb549df74f4b4c 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj @@ -66,8 +66,6 @@ System.Security.Cryptography.Pkcs.EnvelopedCms Link="Common\System\HexConverter.cs" /> - Common\System\Security\Cryptography\Asn1\Pkcs7\ContentInfoAsn.xml @@ -619,8 +617,6 @@ System.Security.Cryptography.Pkcs.EnvelopedCms Common\System\Security\Cryptography\Asn1\Pkcs7\EncryptedDataAsn.xml.cs Common\System\Security\Cryptography\Asn1\Pkcs7\EncryptedDataAsn.xml - + diff --git a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs index 4bd007d9030c39..a84c8598882572 100644 --- a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs +++ b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs @@ -2882,6 +2882,30 @@ public enum OpenFlags OpenExistingOnly = 4, IncludeArchived = 8, } + public sealed partial class Pkcs12LoaderLimits + { + public Pkcs12LoaderLimits() { } + public Pkcs12LoaderLimits(System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits copyFrom) { } + public static System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits DangerousNoLimits { get { throw null; } } + public static System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits Defaults { get { throw null; } } + public bool IgnoreEncryptedAuthSafes { get { throw null; } set { } } + public bool IgnorePrivateKeys { get { throw null; } set { } } + public int? IndividualKdfIterationLimit { get { throw null; } set { } } + public bool IsReadOnly { get { throw null; } } + public int? MacIterationLimit { get { throw null; } set { } } + public int? MaxCertificates { get { throw null; } set { } } + public int? MaxKeys { get { throw null; } set { } } + public bool PreserveCertificateAlias { get { throw null; } set { } } + public bool PreserveKeyName { get { throw null; } set { } } + public bool PreserveStorageProvider { get { throw null; } set { } } + public bool PreserveUnknownAttributes { get { throw null; } set { } } + public int? TotalKdfIterationLimit { get { throw null; } set { } } + public void MakeReadOnly() { } + } + public sealed partial class Pkcs12LoadLimitExceededException : System.Security.Cryptography.CryptographicException + { + public Pkcs12LoadLimitExceededException(string propertyName) { } + } public sealed partial class PublicKey { public PublicKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } @@ -3304,6 +3328,21 @@ public void Reset() { } void System.Collections.IEnumerator.Reset() { } } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + public static partial class X509CertificateLoader + { + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadCertificate(byte[] data) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadCertificate(System.ReadOnlySpan data) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadCertificateFromFile(string path) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadPkcs12(byte[] data, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadPkcs12(System.ReadOnlySpan data, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2Collection LoadPkcs12Collection(byte[] data, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2Collection LoadPkcs12Collection(System.ReadOnlySpan data, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2Collection LoadPkcs12CollectionFromFile(string path, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2Collection LoadPkcs12CollectionFromFile(string path, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadPkcs12FromFile(string path, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 LoadPkcs12FromFile(string path, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet, System.Security.Cryptography.X509Certificates.Pkcs12LoaderLimits? loaderLimits = null) { throw null; } + } public partial class X509Chain : System.IDisposable { public X509Chain() { } diff --git a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx index 05c91987126a78..62b608677b74fa 100644 --- a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx @@ -717,6 +717,12 @@ The PKCS#12 Exportable flag is not supported on this platform. + + The PKCS#12/PFX violated the '{0}' limit. + + + This Pkcs12LoaderLimits object has been made read-only and can no longer be modified. + The PKCS#12 PersistKeySet flag is not supported on this platform. diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj index ae5affcbb971a0..778bbfa729d8fa 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -33,6 +33,8 @@ Link="Common\Microsoft\Win32\SafeHandles\SafeX509ChainHandle.cs" /> + - - + + + + @@ -547,11 +553,13 @@ + + @@ -683,6 +691,7 @@ + @@ -899,7 +908,6 @@ - @@ -908,10 +916,11 @@ - + + @@ -1029,7 +1038,6 @@ - @@ -1046,10 +1054,11 @@ - + + @@ -1162,9 +1171,9 @@ - + @@ -1255,12 +1264,12 @@ - + @@ -1284,12 +1293,11 @@ - - + @@ -1774,6 +1782,7 @@ + @@ -1788,6 +1797,7 @@ + diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs index 30a6cdbce3c291..4a76b99da51742 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs @@ -51,7 +51,6 @@ private static ICertificatePal FromBlob(ReadOnlySpan rawData, SafePassword { Debug.Assert(password != null); - bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); switch (contentType) @@ -62,14 +61,20 @@ private static ICertificatePal FromBlob(ReadOnlySpan rawData, SafePassword // We don't support determining this on Android right now, so we throw. throw new CryptographicException(SR.Cryptography_X509_PKCS7_NoSigner); case X509ContentType.Pkcs12: - if ((keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet) + try { - throw new PlatformNotSupportedException(SR.Cryptography_X509_PKCS12_PersistKeySetNotSupported); + return X509CertificateLoader.LoadPkcs12Pal( + rawData, + password.DangerousGetSpan(), + keyStorageFlags, + X509Certificate.GetPkcs12Limits(readingFromFile, password)); + } + catch (Pkcs12LoadLimitExceededException e) + { + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); } - - X509Certificate.EnforceIterationCountLimit(ref rawData, readingFromFile, password.PasswordProvided); - - return ReadPkcs12(rawData, password, ephemeralSpecified); case X509ContentType.Cert: default: { @@ -116,24 +121,6 @@ ref MemoryMarshal.GetReference(rawData), return true; } - private static AndroidCertificatePal ReadPkcs12(ReadOnlySpan rawData, SafePasswordHandle password, bool ephemeralSpecified) - { - using (var reader = new AndroidPkcs12Reader()) - { - reader.ParsePkcs12(rawData); - reader.Decrypt(password, ephemeralSpecified); - - UnixPkcs12Reader.CertAndKey certAndKey = reader.GetSingleCert(); - AndroidCertificatePal pal = (AndroidCertificatePal)certAndKey.Cert!; - if (certAndKey.Key != null) - { - pal.SetPrivateKey(AndroidPkcs12Reader.GetPrivateKey(certAndKey.Key)); - } - - return pal; - } - } - internal AndroidCertificatePal(SafeX509Handle handle) { _cert = handle; @@ -418,7 +405,7 @@ public ICertificatePal CopyWithPrivateKey(DSA privateKey) { typedKey.ImportParameters(dsaParameters); return CopyWithPrivateKeyHandle(typedKey.DuplicateKeyHandle()); - }; + } } public ICertificatePal CopyWithPrivateKey(ECDsa privateKey) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidPkcs12Reader.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidPkcs12Reader.cs deleted file mode 100644 index 10800a71d537bf..00000000000000 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidPkcs12Reader.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Formats.Asn1; -using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; -using Microsoft.Win32.SafeHandles; - -namespace System.Security.Cryptography.X509Certificates -{ - internal sealed class AndroidPkcs12Reader : UnixPkcs12Reader - { - internal AndroidPkcs12Reader() - { - } - - public static bool IsPkcs12(ReadOnlySpan data) - { - try - { - using (var reader = new AndroidPkcs12Reader()) - { - reader.ParsePkcs12(data); - return true; - } - } - catch (CryptographicException) - { - } - - return false; - } - - protected override ICertificatePalCore ReadX509Der(ReadOnlyMemory data) - { - ICertificatePal? cert; - if (!AndroidCertificatePal.TryReadX509(data.Span, out cert)) - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - - return cert; - } - - protected override AsymmetricAlgorithm LoadKey(ReadOnlyMemory pkcs8) - { - PrivateKeyInfoAsn privateKeyInfo = PrivateKeyInfoAsn.Decode(pkcs8, AsnEncodingRules.BER); - AsymmetricAlgorithm key; - - string algorithm = privateKeyInfo.PrivateKeyAlgorithm.Algorithm; - switch (algorithm) - { - case Oids.Rsa: - key = new RSAImplementation.RSAAndroid(); - break; - case Oids.Dsa: - key = new DSAImplementation.DSAAndroid(); - break; - case Oids.EcDiffieHellman: - case Oids.EcPublicKey: - key = new ECDsaImplementation.ECDsaAndroid(); - break; - default: - throw new CryptographicException(SR.Cryptography_UnknownAlgorithmIdentifier, algorithm); - } - - key.ImportPkcs8PrivateKey(pkcs8.Span, out int bytesRead); - if (bytesRead != pkcs8.Length) - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - - return key; - } - - internal static SafeKeyHandle GetPrivateKey(AsymmetricAlgorithm key) - { - if (key is ECDsaImplementation.ECDsaAndroid ecdsa) - { - return ecdsa.DuplicateKeyHandle(); - } - - if (key is RSAImplementation.RSAAndroid rsa) - { - return rsa.DuplicateKeyHandle(); - } - - if (key is DSAImplementation.DSAAndroid dsa) - { - return dsa.DuplicateKeyHandle(); - } - - throw new NotImplementedException($"{nameof(GetPrivateKey)} ({key.GetType()})"); - } - } -} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.ImportExport.iOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.ImportExport.iOS.cs index dbbdf1d5c2eaa2..e317dc7fb67c56 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.ImportExport.iOS.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.ImportExport.iOS.cs @@ -98,8 +98,6 @@ internal static ICertificatePal FromDerBlob( { Debug.Assert(password != null); - bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); - if (contentType == X509ContentType.Pkcs7) { throw new CryptographicException( @@ -109,18 +107,20 @@ internal static ICertificatePal FromDerBlob( if (contentType == X509ContentType.Pkcs12) { - if ((keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable) + try { - throw new PlatformNotSupportedException(SR.Cryptography_X509_PKCS12_ExportableNotSupported); + return (AppleCertificatePal)X509CertificateLoader.LoadPkcs12Pal( + rawData, + password.DangerousGetSpan(), + keyStorageFlags, + X509Certificate.GetPkcs12Limits(readingFromFile, password)); } - - if ((keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet) + catch (Pkcs12LoadLimitExceededException e) { - throw new PlatformNotSupportedException(SR.Cryptography_X509_PKCS12_PersistKeySetNotSupported); + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); } - - X509Certificate.EnforceIterationCountLimit(ref rawData, readingFromFile, password.PasswordProvided); - return ImportPkcs12(rawData, password, ephemeralSpecified); } SafeSecIdentityHandle identityHandle; diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.ImportExport.macOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.ImportExport.macOS.cs index ce4745c42d58f3..2d1b84b1138638 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.ImportExport.macOS.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.ImportExport.macOS.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using System.Formats.Asn1; using System.Security.Cryptography.Apple; -using System.Threading; using Microsoft.Win32.SafeHandles; namespace System.Security.Cryptography.X509Certificates @@ -44,24 +43,19 @@ private static AppleCertificatePal FromBlob( if (contentType == X509ContentType.Pkcs12) { - if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet) + try { - throw new PlatformNotSupportedException(SR.Cryptography_X509_NoEphemeralPfx); + return (AppleCertificatePal)X509CertificateLoader.LoadPkcs12Pal( + rawData, + password.DangerousGetSpan(), + keyStorageFlags, + X509Certificate.GetPkcs12Limits(readingFromFile, password)); } - - X509Certificate.EnforceIterationCountLimit(ref rawData, readingFromFile, password.PasswordProvided); - bool exportable = (keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable; - - bool persist = - (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet; - - SafeKeychainHandle keychain = persist - ? Interop.AppleCrypto.SecKeychainCopyDefault() - : Interop.AppleCrypto.CreateTemporaryKeychain(); - - using (keychain) + catch (Pkcs12LoadLimitExceededException e) { - return ImportPkcs12(rawData, password, exportable, keychain); + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Keys.iOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Keys.iOS.cs index 9076f0c4899a1b..e5903066a14bfd 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Keys.iOS.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Keys.iOS.cs @@ -20,17 +20,17 @@ public ICertificatePal CopyWithPrivateKey(DSA privateKey) public ICertificatePal CopyWithPrivateKey(ECDsa privateKey) { - return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); + return ImportPkcs12(this, privateKey); } public ICertificatePal CopyWithPrivateKey(ECDiffieHellman privateKey) { - return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); + return ImportPkcs12(this, privateKey); } public ICertificatePal CopyWithPrivateKey(RSA privateKey) { - return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); + return ImportPkcs12(this, privateKey); } } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Pkcs12.iOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Pkcs12.iOS.cs index 1de7e8ce9bee72..7616da6ffe4ec1 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Pkcs12.iOS.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Pkcs12.iOS.cs @@ -9,26 +9,11 @@ internal sealed partial class AppleCertificatePal : ICertificatePal { private static readonly SafePasswordHandle s_passwordExportHandle = new SafePasswordHandle("DotnetExportPassphrase", passwordProvided: true); - private static AppleCertificatePal ImportPkcs12( - ReadOnlySpan rawData, - SafePasswordHandle password, - bool ephemeralSpecified) + internal static AppleCertificatePal ImportPkcs12(AppleCertificatePal pal, AsymmetricAlgorithm? key) { - using (ApplePkcs12Reader reader = new ApplePkcs12Reader()) + if (key is not null) { - reader.ParsePkcs12(rawData); - reader.Decrypt(password, ephemeralSpecified); - return ImportPkcs12(reader.GetSingleCert()); - } - } - - internal static AppleCertificatePal ImportPkcs12(UnixPkcs12Reader.CertAndKey certAndKey) - { - AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert!; - - if (certAndKey.Key != null) - { - AppleCertificateExporter exporter = new AppleCertificateExporter(new TempExportPal(pal), certAndKey.Key); + AppleCertificateExporter exporter = new AppleCertificateExporter(new TempExportPal(pal), key); byte[] smallPfx = exporter.Export(X509ContentType.Pkcs12, s_passwordExportHandle)!; SafeSecIdentityHandle identityHandle; diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Pkcs12.macOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Pkcs12.macOS.cs index 6e329434278de1..1951e7dc8bd09f 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Pkcs12.macOS.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Pkcs12.macOS.cs @@ -9,53 +9,6 @@ namespace System.Security.Cryptography.X509Certificates { internal sealed partial class AppleCertificatePal : ICertificatePal { - private static AppleCertificatePal ImportPkcs12( - ReadOnlySpan rawData, - SafePasswordHandle password, - bool exportable, - SafeKeychainHandle keychain) - { - using (ApplePkcs12Reader reader = new ApplePkcs12Reader()) - { - reader.ParsePkcs12(rawData); - reader.Decrypt(password, ephemeralSpecified: false); - - UnixPkcs12Reader.CertAndKey certAndKey = reader.GetSingleCert(); - AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert!; - - SafeSecKeyRefHandle? safeSecKeyRefHandle = - ApplePkcs12Reader.GetPrivateKey(certAndKey.Key); - - AppleCertificatePal? newPal; - - using (safeSecKeyRefHandle) - { - // SecItemImport doesn't seem to respect non-exportable import for PKCS#8, - // only PKCS#12. - // - // So, as part of reading this PKCS#12 we now need to write the minimum - // PKCS#12 in a normalized form, and ask the OS to import it. - if (!exportable && safeSecKeyRefHandle != null) - { - using (pal) - { - return ImportPkcs12NonExportable(pal, safeSecKeyRefHandle, password, keychain); - } - } - - newPal = pal.MoveToKeychain(keychain, safeSecKeyRefHandle); - - if (newPal != null) - { - pal.Dispose(); - } - } - - // If no new PAL came back, it means we moved the cert, but had no private key. - return newPal ?? pal; - } - } - internal static AppleCertificatePal ImportPkcs12NonExportable( AppleCertificatePal cert, SafeSecKeyRefHandle privateKey, diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12CertLoader.iOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12CertLoader.iOS.cs deleted file mode 100644 index c190f3400b9590..00000000000000 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12CertLoader.iOS.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Threading; -using Microsoft.Win32.SafeHandles; - -namespace System.Security.Cryptography.X509Certificates -{ - internal sealed class ApplePkcs12CertLoader : ILoaderPal - { - private readonly ApplePkcs12Reader _pkcs12; - private SafePasswordHandle _password; - - public ApplePkcs12CertLoader( - ApplePkcs12Reader pkcs12, - SafePasswordHandle password) - { - _pkcs12 = pkcs12; - - bool addedRef = false; - password.DangerousAddRef(ref addedRef); - _password = password; - } - - public void Dispose() - { - _pkcs12.Dispose(); - - SafePasswordHandle? password = Interlocked.Exchange(ref _password, null!); - password?.DangerousRelease(); - } - - public void MoveTo(X509Certificate2Collection collection) - { - foreach (UnixPkcs12Reader.CertAndKey certAndKey in _pkcs12.EnumerateAll()) - { - collection.Add(new X509Certificate2(AppleCertificatePal.ImportPkcs12(certAndKey))); - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12Reader.iOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12Reader.iOS.cs deleted file mode 100644 index e493436e01d7b4..00000000000000 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12Reader.iOS.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Formats.Asn1; -using System.Security.Cryptography.Asn1; -using Microsoft.Win32.SafeHandles; - -namespace System.Security.Cryptography.X509Certificates -{ - internal sealed class ApplePkcs12Reader : UnixPkcs12Reader - { - internal ApplePkcs12Reader() - { - } - - protected override ICertificatePalCore ReadX509Der(ReadOnlyMemory data) - { - SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( - data.Span, - X509ContentType.Cert, - SafePasswordHandle.InvalidHandle, - out SafeSecIdentityHandle identityHandle); - - if (identityHandle.IsInvalid) - { - identityHandle.Dispose(); - return new AppleCertificatePal(certHandle); - } - - Debug.Fail("Non-PKCS12 import produced an identity handle"); - - identityHandle.Dispose(); - certHandle.Dispose(); - throw new CryptographicException(); - } - - protected override AsymmetricAlgorithm LoadKey(ReadOnlyMemory pkcs8) - { - PrivateKeyInfoAsn privateKeyInfo = PrivateKeyInfoAsn.Decode(pkcs8, AsnEncodingRules.BER); - AsymmetricAlgorithm key; - - switch (privateKeyInfo.PrivateKeyAlgorithm.Algorithm) - { - case Oids.Rsa: - key = new RSAImplementation.RSASecurityTransforms(); - break; - case Oids.EcDiffieHellman: - case Oids.EcPublicKey: - key = new ECDsaImplementation.ECDsaSecurityTransforms(); - break; - default: - throw new CryptographicException( - SR.Cryptography_UnknownAlgorithmIdentifier, - privateKeyInfo.PrivateKeyAlgorithm.Algorithm); - } - - key.ImportPkcs8PrivateKey(pkcs8.Span, out int bytesRead); - - if (bytesRead != pkcs8.Length) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return key; - } - } -} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12Reader.macOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12Reader.macOS.cs deleted file mode 100644 index 8f3274d15d2329..00000000000000 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12Reader.macOS.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Formats.Asn1; -using System.Security.Cryptography.Apple; -using System.Security.Cryptography.Asn1; -using Microsoft.Win32.SafeHandles; - -namespace System.Security.Cryptography.X509Certificates -{ - internal sealed class ApplePkcs12Reader : UnixPkcs12Reader - { - internal ApplePkcs12Reader() - { - } - - protected override ICertificatePalCore ReadX509Der(ReadOnlyMemory data) - { - SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( - data.Span, - X509ContentType.Cert, - SafePasswordHandle.InvalidHandle, - SafeTemporaryKeychainHandle.InvalidHandle, - exportable: true, - out SafeSecIdentityHandle identityHandle); - - if (identityHandle.IsInvalid) - { - identityHandle.Dispose(); - return new AppleCertificatePal(certHandle); - } - - Debug.Fail("Non-PKCS12 import produced an identity handle"); - - identityHandle.Dispose(); - certHandle.Dispose(); - throw new CryptographicException(); - } - - protected override AsymmetricAlgorithm LoadKey(ReadOnlyMemory pkcs8) - { - PrivateKeyInfoAsn privateKeyInfo = PrivateKeyInfoAsn.Decode(pkcs8, AsnEncodingRules.BER); - AsymmetricAlgorithm key; - - switch (privateKeyInfo.PrivateKeyAlgorithm.Algorithm) - { - case Oids.Rsa: - key = new RSAImplementation.RSASecurityTransforms(); - break; - case Oids.Dsa: - key = new DSAImplementation.DSASecurityTransforms(); - break; - case Oids.EcDiffieHellman: - case Oids.EcPublicKey: - key = new ECDsaImplementation.ECDsaSecurityTransforms(); - break; - default: - throw new CryptographicException( - SR.Cryptography_UnknownAlgorithmIdentifier, - privateKeyInfo.PrivateKeyAlgorithm.Algorithm); - } - - key.ImportPkcs8PrivateKey(pkcs8.Span, out int bytesRead); - - if (bytesRead != pkcs8.Length) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return key; - } - - internal static SafeSecKeyRefHandle? GetPrivateKey(AsymmetricAlgorithm? key) - { - if (key == null) - { - return null; - } - - if (key is RSAImplementation.RSASecurityTransforms rsa) - { - // Convert data key to legacy CSSM key that can be imported into keychain - byte[] rsaPrivateKey = rsa.ExportRSAPrivateKey(); - using (PinAndClear.Track(rsaPrivateKey)) - { - return Interop.AppleCrypto.ImportEphemeralKey(rsaPrivateKey, true); - } - } - - if (key is DSAImplementation.DSASecurityTransforms dsa) - { - // DSA always uses legacy CSSM keys do no need to convert - return dsa.GetKeys().PrivateKey; - } - - if (key is ECDsaImplementation.ECDsaSecurityTransforms ecdsa) - { - // Convert data key to legacy CSSM key that can be imported into keychain - byte[] ecdsaPrivateKey = ecdsa.ExportECPrivateKey(); - using (PinAndClear.Track(ecdsaPrivateKey)) - { - return Interop.AppleCrypto.ImportEphemeralKey(ecdsaPrivateKey, true); - } - } - - Debug.Fail("Invalid key implementation"); - return null; - } - } -} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificatePal.Windows.Import.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificatePal.Windows.Import.cs index 2b5feeee7fe79f..dee85ca9b9b1a1 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificatePal.Windows.Import.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificatePal.Windows.Import.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.IO; using System.Runtime.InteropServices; using Internal.Cryptography; using Microsoft.Win32.SafeHandles; @@ -27,13 +26,9 @@ private static CertificatePal FromBlobOrFile(ReadOnlySpan rawData, string? Debug.Assert(password != null); bool loadFromFile = (fileName != null); - - Interop.Crypt32.PfxCertStoreFlags pfxCertStoreFlags = MapKeyStorageFlags(keyStorageFlags); bool deleteKeyContainer = false; - Interop.Crypt32.CertEncodingType msgAndCertEncodingType; Interop.Crypt32.ContentType contentType; - Interop.Crypt32.FormatType formatType; SafeCertStoreHandle? hCertStore = null; SafeCryptMsgHandle? hCryptMsg = null; SafeCertContextHandle? pCertContext = null; @@ -57,13 +52,13 @@ private static CertificatePal FromBlobOrFile(ReadOnlySpan rawData, string? X509ExpectedContentTypeFlags, X509ExpectedFormatTypeFlags, 0, - out msgAndCertEncodingType, + out _, out contentType, - out formatType, + out _, out hCertStore, out hCryptMsg, - out pCertContext - ); + out pCertContext); + if (!success) { int hr = Marshal.GetHRForLastWin32Error(); @@ -79,21 +74,35 @@ out pCertContext } else if (contentType == Interop.Crypt32.ContentType.CERT_QUERY_CONTENT_PFX) { - if (loadFromFile) + try { - rawData = File.ReadAllBytes(fileName!); - } - - pCertContext?.Dispose(); - X509Certificate.EnforceIterationCountLimit(ref rawData, readingFromFile: loadFromFile, password.PasswordProvided); - pCertContext = FilterPFXStore(rawData, password, pfxCertStoreFlags); + Pkcs12LoaderLimits limits = X509Certificate.GetPkcs12Limits(loadFromFile, password); - // If PersistKeySet is set we don't delete the key, so that it persists. - // If EphemeralKeySet is set we don't delete the key, because there's no file, so it's a wasteful call. - const X509KeyStorageFlags DeleteUnless = - X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.EphemeralKeySet; + if (loadFromFile) + { + Debug.Assert(fileName is not null); - deleteKeyContainer = ((keyStorageFlags & DeleteUnless) == 0); + return (CertificatePal)X509CertificateLoader.LoadPkcs12PalFromFile( + fileName, + password.DangerousGetSpan(), + keyStorageFlags, + limits); + } + else + { + return (CertificatePal)X509CertificateLoader.LoadPkcs12Pal( + rawData, + password.DangerousGetSpan(), + keyStorageFlags, + limits); + } + } + catch (Pkcs12LoadLimitExceededException e) + { + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); + } } CertificatePal pal = new CertificatePal(pCertContext, deleteKeyContainer); @@ -149,112 +158,6 @@ private static unsafe SafeCertContextHandle GetSignerInPKCS7Store(SafeCertStoreH } } - private static SafeCertContextHandle FilterPFXStore( - ReadOnlySpan rawData, - SafePasswordHandle password, - Interop.Crypt32.PfxCertStoreFlags pfxCertStoreFlags) - { - SafeCertStoreHandle hStore; - unsafe - { - fixed (byte* pbRawData = rawData) - { - Interop.Crypt32.DATA_BLOB certBlob = new Interop.Crypt32.DATA_BLOB(new IntPtr(pbRawData), (uint)rawData.Length); - hStore = Interop.Crypt32.PFXImportCertStore(ref certBlob, password, pfxCertStoreFlags); - if (hStore.IsInvalid) - { - Exception e = Marshal.GetHRForLastWin32Error().ToCryptographicException(); - hStore.Dispose(); - throw e; - } - } - } - - try - { - // Find the first cert with private key. If none, then simply take the very first cert. Along the way, delete the keycontainers - // of any cert we don't accept. - SafeCertContextHandle pCertContext = SafeCertContextHandle.InvalidHandle; - SafeCertContextHandle? pEnumContext = null; - while (Interop.crypt32.CertEnumCertificatesInStore(hStore, ref pEnumContext)) - { - if (pEnumContext.ContainsPrivateKey) - { - if ((!pCertContext.IsInvalid) && pCertContext.ContainsPrivateKey) - { - // We already found our chosen one. Free up this one's key and move on. - - // If this one has a persisted private key, clean up the key file. - // If it was an ephemeral private key no action is required. - if (pEnumContext.HasPersistedPrivateKey) - { - SafeCertContextHandleWithKeyContainerDeletion.DeleteKeyContainer(pEnumContext); - } - } - else - { - // Found our first cert that has a private key. Set it up as our chosen one but keep iterating - // as we need to free up the keys of any remaining certs. - pCertContext.Dispose(); - pCertContext = pEnumContext.Duplicate(); - } - } - else - { - if (pCertContext.IsInvalid) - { - // Doesn't have a private key but hang on to it anyway in case we don't find any certs with a private key. - pCertContext.Dispose(); - pCertContext = pEnumContext.Duplicate(); - } - } - } - - if (pCertContext.IsInvalid) - { - pCertContext.Dispose(); - throw new CryptographicException(SR.Cryptography_Pfx_NoCertificates); - } - - return pCertContext; - } - finally - { - hStore.Dispose(); - } - } - - private static Interop.Crypt32.PfxCertStoreFlags MapKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) - { - if ((keyStorageFlags & X509Certificate.KeyStorageFlagsAll) != keyStorageFlags) - throw new ArgumentException(SR.Argument_InvalidFlag, nameof(keyStorageFlags)); - - Interop.Crypt32.PfxCertStoreFlags pfxCertStoreFlags = 0; - if ((keyStorageFlags & X509KeyStorageFlags.UserKeySet) == X509KeyStorageFlags.UserKeySet) - pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_USER_KEYSET; - else if ((keyStorageFlags & X509KeyStorageFlags.MachineKeySet) == X509KeyStorageFlags.MachineKeySet) - pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_MACHINE_KEYSET; - - if ((keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable) - pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_EXPORTABLE; - if ((keyStorageFlags & X509KeyStorageFlags.UserProtected) == X509KeyStorageFlags.UserProtected) - pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_USER_PROTECTED; - - // If a user is asking for an Ephemeral key they should be willing to test their code to find out - // that it will no longer import into CAPI. This solves problems of legacy CSPs being - // difficult to do SHA-2 RSA signatures with, simplifies the story for UWP, and reduces the - // complexity of pointer interpretation. - if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet) - pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.PKCS12_NO_PERSIST_KEY | Interop.Crypt32.PfxCertStoreFlags.PKCS12_ALWAYS_CNG_KSP; - - // In .NET Framework loading a PFX then adding the key to the Windows Certificate Store would - // enable a native application compiled against CAPI to find that private key and interoperate with it. - // - // For .NET Core this behavior is being retained. - - return pfxCertStoreFlags; - } - private const Interop.Crypt32.ExpectedContentTypeFlags X509ExpectedContentTypeFlags = Interop.Crypt32.ExpectedContentTypeFlags.CERT_QUERY_CONTENT_FLAG_CERT | Interop.Crypt32.ExpectedContentTypeFlags.CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT | diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificatePal.Windows.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificatePal.Windows.cs index 9b15a429e7c22d..ad3077da247245 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificatePal.Windows.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificatePal.Windows.cs @@ -520,7 +520,7 @@ private CertificatePal(CertificatePal copyFrom) _certContext = new SafeCertContextHandle(copyFrom._certContext); } - private CertificatePal(SafeCertContextHandle certContext, bool deleteKeyContainer) + internal CertificatePal(SafeCertContextHandle certContext, bool deleteKeyContainer) { if (deleteKeyContainer) { diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/LocalAppContextSwitches.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/LocalAppContextSwitches.cs index 0af05ab604b677..c87cb0cb4c9228 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/LocalAppContextSwitches.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/LocalAppContextSwitches.cs @@ -7,7 +7,7 @@ namespace System { internal static partial class LocalAppContextSwitches { - internal const long DefaultPkcs12UnspecifiedPasswordIterationLimit = 600_000; + internal const int DefaultPkcs12UnspecifiedPasswordIterationLimit = 600_000; internal static long Pkcs12UnspecifiedPasswordIterationLimit { get; } = InitializePkcs12UnspecifiedPasswordIterationLimit(); diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslPkcs12Reader.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslPkcs12Reader.cs deleted file mode 100644 index c0a4616273c049..00000000000000 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslPkcs12Reader.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; -using System.Formats.Asn1; -using System.Security.Cryptography.Asn1; - -namespace System.Security.Cryptography.X509Certificates -{ - internal sealed class OpenSslPkcs12Reader : UnixPkcs12Reader - { - private OpenSslPkcs12Reader() - { - } - - protected override ICertificatePalCore ReadX509Der(ReadOnlyMemory data) - { - if (OpenSslX509CertificateReader.TryReadX509Der(data.Span, out ICertificatePal? ret)) - { - return ret; - } - - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - public static bool TryRead(ReadOnlySpan data, [NotNullWhen(true)] out OpenSslPkcs12Reader? pkcs12Reader) => - TryRead(data, out pkcs12Reader, out _, captureException: false); - - public static bool TryRead(ReadOnlySpan data, [NotNullWhen(true)] out OpenSslPkcs12Reader? pkcs12Reader, [NotNullWhen(false)] out Exception? openSslException) => - TryRead(data, out pkcs12Reader, out openSslException!, captureException: true); - - protected override AsymmetricAlgorithm LoadKey(ReadOnlyMemory pkcs8) - { - PrivateKeyInfoAsn privateKeyInfo = PrivateKeyInfoAsn.Decode(pkcs8, AsnEncodingRules.BER); - AsymmetricAlgorithm key; - - switch (privateKeyInfo.PrivateKeyAlgorithm.Algorithm) - { - case Oids.Rsa: - key = new RSAOpenSsl(); - break; - case Oids.Dsa: - key = new DSAOpenSsl(); - break; - case Oids.EcDiffieHellman: - case Oids.EcPublicKey: - key = new ECDiffieHellmanOpenSsl(); - break; - default: - throw new CryptographicException( - SR.Cryptography_UnknownAlgorithmIdentifier, - privateKeyInfo.PrivateKeyAlgorithm.Algorithm); - } - - key.ImportPkcs8PrivateKey(pkcs8.Span, out int bytesRead); - - if (bytesRead != pkcs8.Length) - { - key.Dispose(); - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return key; - } - - internal static SafeEvpPKeyHandle GetPrivateKey(AsymmetricAlgorithm key) - { - if (key is RSAOpenSsl rsa) - { - return rsa.DuplicateKeyHandle(); - } - - if (key is DSAOpenSsl dsa) - { - return dsa.DuplicateKeyHandle(); - } - - return ((ECDiffieHellmanOpenSsl)key).DuplicateKeyHandle(); - } - - private static bool TryRead( - ReadOnlySpan data, - [NotNullWhen(true)] out OpenSslPkcs12Reader? pkcs12Reader, - out Exception? openSslException, - bool captureException) - { - openSslException = null; - - try - { - pkcs12Reader = new OpenSslPkcs12Reader(); - pkcs12Reader.ParsePkcs12(data); - return true; - } - catch (CryptographicException e) - { - if (captureException) - { - openSslException = e; - } - - pkcs12Reader = null; - return false; - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslPkcsFormatReader.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslPkcsFormatReader.cs index 41a3c9bcd5a03b..63d00a0ca6cd84 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslPkcsFormatReader.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslPkcsFormatReader.cs @@ -1,12 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; using Microsoft.Win32.SafeHandles; namespace System.Security.Cryptography.X509Certificates @@ -237,116 +234,5 @@ private static bool TryReadPkcs7( certPals = readPals; return true; } - - internal static bool TryReadPkcs12( - ReadOnlySpan rawData, - SafePasswordHandle password, - bool ephemeralSpecified, - bool readingFromFile, - [NotNullWhen(true)] out ICertificatePal? certPal, - out Exception? openSslException) - { - return TryReadPkcs12( - rawData, - password, - single: true, - ephemeralSpecified, - readingFromFile, - out certPal!, - out _, - out openSslException); - } - - internal static bool TryReadPkcs12( - ReadOnlySpan rawData, - SafePasswordHandle password, - bool ephemeralSpecified, - bool readingFromFile, - [NotNullWhen(true)] out List? certPals, - out Exception? openSslException) - { - return TryReadPkcs12( - rawData, - password, - single: false, - ephemeralSpecified, - readingFromFile, - out _, - out certPals!, - out openSslException); - } - - private static bool TryReadPkcs12( - ReadOnlySpan rawData, - SafePasswordHandle password, - bool single, - bool ephemeralSpecified, - bool readingFromFile, - out ICertificatePal? readPal, - out List? readCerts, - out Exception? openSslException) - { - // DER-PKCS12 - OpenSslPkcs12Reader? pfx; - - if (!OpenSslPkcs12Reader.TryRead(rawData, out pfx, out openSslException)) - { - readPal = null; - readCerts = null; - return false; - } - - using (pfx) - { - return TryReadPkcs12(rawData, pfx, password, single, ephemeralSpecified, readingFromFile, out readPal, out readCerts); - } - } - - private static bool TryReadPkcs12( - ReadOnlySpan rawData, - OpenSslPkcs12Reader pfx, - SafePasswordHandle password, - bool single, - bool ephemeralSpecified, - bool readingFromFile, - out ICertificatePal? readPal, - out List? readCerts) - { - X509Certificate.EnforceIterationCountLimit(ref rawData, readingFromFile, password.PasswordProvided); - pfx.Decrypt(password, ephemeralSpecified); - - if (single) - { - UnixPkcs12Reader.CertAndKey certAndKey = pfx.GetSingleCert(); - OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)certAndKey.Cert!; - - if (certAndKey.Key != null) - { - pal.SetPrivateKey(OpenSslPkcs12Reader.GetPrivateKey(certAndKey.Key)); - } - - readPal = pal; - readCerts = null; - return true; - } - - readPal = null; - List certs = new List(pfx.GetCertCount()); - - foreach (UnixPkcs12Reader.CertAndKey certAndKey in pfx.EnumerateAll()) - { - OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)certAndKey.Cert!; - - if (certAndKey.Key != null) - { - pal.SetPrivateKey(OpenSslPkcs12Reader.GetPrivateKey(certAndKey.Key)); - } - - certs.Add(pal); - } - - readCerts = certs; - return true; - } } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509CertificateReader.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509CertificateReader.cs index 706d2a024b0470..8c07209c61cf1b 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509CertificateReader.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509CertificateReader.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.IO; using System.Runtime.InteropServices; using System.Text; using Microsoft.Win32.SafeHandles; @@ -45,33 +44,35 @@ public static ICertificatePal FromBlob(ReadOnlySpan rawData, SafePasswordH Debug.Assert(password != null); ICertificatePal? cert; - Exception? openSslException; - bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); if (TryReadX509Der(rawData, out cert) || TryReadX509Pem(rawData, out cert) || OpenSslPkcsFormatReader.TryReadPkcs7Der(rawData, out cert) || - OpenSslPkcsFormatReader.TryReadPkcs7Pem(rawData, out cert) || - OpenSslPkcsFormatReader.TryReadPkcs12(rawData, password, ephemeralSpecified, readingFromFile: false, out cert, out openSslException)) + OpenSslPkcsFormatReader.TryReadPkcs7Pem(rawData, out cert)) { - if (cert == null) - { - // Empty collection, most likely. - throw new CryptographicException(); - } - + Debug.Assert(cert is not null); return cert; } - // Unsupported - Debug.Assert(openSslException != null); - throw openSslException; + try + { + return X509CertificateLoader.LoadPkcs12Pal( + rawData, + password.DangerousGetSpan(), + keyStorageFlags, + X509Certificate.GetPkcs12Limits(fromFile: false, password)); + } + catch (Pkcs12LoadLimitExceededException e) + { + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); + } } public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { ICertificatePal? pal; - bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); // If we can't open the file, fail right away. using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(fileName, "rb")) @@ -83,20 +84,20 @@ public static ICertificatePal FromFile(string fileName, SafePasswordHandle passw if (pal == null) { - OpenSslPkcsFormatReader.TryReadPkcs12( - File.ReadAllBytes(fileName), - password, - ephemeralSpecified, - readingFromFile: true, - out pal, - out Exception? exception); - - if (exception != null) + try { - throw exception; + pal = X509CertificateLoader.LoadPkcs12PalFromFile( + fileName, + password.DangerousGetSpan(), + keyStorageFlags, + X509Certificate.GetPkcs12Limits(fromFile: true, password)); + } + catch (Pkcs12LoadLimitExceededException e) + { + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); } - - Debug.Assert(pal != null); } return pal; @@ -109,7 +110,7 @@ public static ICertificatePal FromFile(string fileName, SafePasswordHandle passw Debug.Assert(bioPosition >= 0); ICertificatePal? certPal; - if (TryReadX509Pem(bio, out certPal)) + if (TryReadX509Der(bio, out certPal)) { return certPal; } @@ -117,7 +118,7 @@ public static ICertificatePal FromFile(string fileName, SafePasswordHandle passw // Rewind, try again. RewindBio(bio, bioPosition); - if (TryReadX509Der(bio, out certPal)) + if (TryReadX509Pem(bio, out certPal)) { return certPal; } @@ -125,7 +126,7 @@ public static ICertificatePal FromFile(string fileName, SafePasswordHandle passw // Rewind, try again. RewindBio(bio, bioPosition); - if (OpenSslPkcsFormatReader.TryReadPkcs7Pem(bio, out certPal)) + if (OpenSslPkcsFormatReader.TryReadPkcs7Der(bio, out certPal)) { return certPal; } @@ -133,7 +134,7 @@ public static ICertificatePal FromFile(string fileName, SafePasswordHandle passw // Rewind, try again. RewindBio(bio, bioPosition); - if (OpenSslPkcsFormatReader.TryReadPkcs7Der(bio, out certPal)) + if (OpenSslPkcsFormatReader.TryReadPkcs7Pem(bio, out certPal)) { return certPal; } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509Encoder.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509Encoder.cs index 4d43399e6f7c5e..5a8c9697c77224 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509Encoder.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509Encoder.cs @@ -80,14 +80,9 @@ public X509ContentType GetCertContentType(ReadOnlySpan rawData) return X509ContentType.Pkcs7; } + if (X509CertificateLoader.IsPkcs12(rawData)) { - OpenSslPkcs12Reader? pfx; - - if (OpenSslPkcs12Reader.TryRead(rawData, out pfx)) - { - pfx.Dispose(); - return X509ContentType.Pkcs12; - } + return X509ContentType.Pkcs12; } // Unsupported format. @@ -147,15 +142,9 @@ public X509ContentType GetCertContentType(string fileName) } // X509ContentType.Pkcs12 (aka PFX) + if (X509CertificateLoader.IsPkcs12(fileName)) { - OpenSslPkcs12Reader? pkcs12Reader; - - if (OpenSslPkcs12Reader.TryRead(File.ReadAllBytes(fileName), out pkcs12Reader)) - { - pkcs12Reader.Dispose(); - - return X509ContentType.Pkcs12; - } + return X509ContentType.Pkcs12; } // Unsupported format. diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Android.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Android.cs index 962287bc2630e6..2f6a4c2cd44b71 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Android.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Android.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Diagnostics; using System.IO; using Microsoft.Win32.SafeHandles; @@ -15,18 +14,29 @@ internal static partial IStorePal FromHandle(IntPtr storeHandle) throw new NotImplementedException($"{nameof(StorePal)}.{nameof(FromHandle)}"); } - private static AndroidCertLoader FromBlob(ReadOnlySpan rawData, SafePasswordHandle password, bool readingFromFile, X509KeyStorageFlags keyStorageFlags) + private static ILoaderPal FromBlob(ReadOnlySpan rawData, SafePasswordHandle password, bool readingFromFile, X509KeyStorageFlags keyStorageFlags) { Debug.Assert(password != null); X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); - bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); if (contentType == X509ContentType.Pkcs12) { - X509Certificate.EnforceIterationCountLimit(ref rawData, readingFromFile, password.PasswordProvided); - ICertificatePal[] certPals = ReadPkcs12Collection(rawData, password, ephemeralSpecified); - return new AndroidCertLoader(certPals); + try + { + return new CollectionBasedLoader( + X509CertificateLoader.LoadPkcs12Collection( + rawData, + password.DangerousGetSpan(), + keyStorageFlags, + X509Certificate.GetPkcs12Limits(readingFromFile, password))); + } + catch (Pkcs12LoadLimitExceededException e) + { + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); + } } else { @@ -112,33 +122,5 @@ internal static partial IStorePal FromSystemStore(string storeName, StoreLocatio string message = SR.Format(SR.Cryptography_X509_StoreCannotCreate, storeName, storeLocation); throw new CryptographicException(message, new PlatformNotSupportedException(message)); } - - private static ICertificatePal[] ReadPkcs12Collection( - ReadOnlySpan rawData, - SafePasswordHandle password, - bool ephemeralSpecified) - { - using (var reader = new AndroidPkcs12Reader()) - { - reader.ParsePkcs12(rawData); - reader.Decrypt(password, ephemeralSpecified); - - ICertificatePal[] certs = new ICertificatePal[reader.GetCertCount()]; - int idx = 0; - foreach (UnixPkcs12Reader.CertAndKey certAndKey in reader.EnumerateAll()) - { - AndroidCertificatePal pal = (AndroidCertificatePal)certAndKey.Cert!; - if (certAndKey.Key != null) - { - pal.SetPrivateKey(AndroidPkcs12Reader.GetPrivateKey(certAndKey.Key)); - } - - certs[idx] = pal; - idx++; - } - - return certs; - } - } } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.OpenSsl.cs index a87e89344d8951..6e75c900a699af 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.OpenSsl.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.OpenSsl.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Diagnostics; -using System.IO; using Microsoft.Win32.SafeHandles; namespace System.Security.Cryptography.X509Certificates @@ -22,7 +21,6 @@ internal static partial ILoaderPal FromBlob(ReadOnlySpan rawData, SafePass Debug.Assert(password != null); ICertificatePal? singleCert; - bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); if (OpenSslX509CertificateReader.TryReadX509Der(rawData, out singleCert) || OpenSslX509CertificateReader.TryReadX509Pem(rawData, out singleCert)) @@ -35,30 +33,39 @@ internal static partial ILoaderPal FromBlob(ReadOnlySpan rawData, SafePass } List? certPals; - Exception? openSslException; if (OpenSslPkcsFormatReader.TryReadPkcs7Der(rawData, out certPals) || - OpenSslPkcsFormatReader.TryReadPkcs7Pem(rawData, out certPals) || - OpenSslPkcsFormatReader.TryReadPkcs12(rawData, password, ephemeralSpecified, readingFromFile: false, out certPals, out openSslException)) + OpenSslPkcsFormatReader.TryReadPkcs7Pem(rawData, out certPals)) { Debug.Assert(certPals != null); return ListToLoaderPal(certPals); } - Debug.Assert(openSslException != null); - throw openSslException; + try + { + return new CollectionBasedLoader( + X509CertificateLoader.LoadPkcs12Collection( + rawData, + password.DangerousGetSpan(), + keyStorageFlags, + X509Certificate.GetPkcs12Limits(fromFile: false, password))); + } + catch (Pkcs12LoadLimitExceededException e) + { + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); + } } internal static partial ILoaderPal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { - bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); - using (SafeBioHandle bio = Interop.Crypto.BioNewFile(fileName, "rb")) { Interop.Crypto.CheckValidOpenSslHandle(bio); - return FromBio(fileName, bio, password, ephemeralSpecified); + return FromBio(fileName, bio, password, keyStorageFlags); } } @@ -66,7 +73,7 @@ private static ILoaderPal FromBio( string fileName, SafeBioHandle bio, SafePasswordHandle password, - bool ephemeralSpecified) + X509KeyStorageFlags keyStorageFlags) { int bioPosition = Interop.Crypto.BioTell(bio); Debug.Assert(bioPosition >= 0); @@ -104,29 +111,21 @@ private static ILoaderPal FromBio( return ListToLoaderPal(certPals); } - // Rewind, try again. - OpenSslX509CertificateReader.RewindBio(bio, bioPosition); - - // Capture the exception so in case of failure, the call to BioSeek does not override it. - Exception? openSslException; - byte[] data = File.ReadAllBytes(fileName); - if (OpenSslPkcsFormatReader.TryReadPkcs12(data, password, ephemeralSpecified, readingFromFile: true, out certPals, out openSslException)) + try { - return ListToLoaderPal(certPals); + return new CollectionBasedLoader( + X509CertificateLoader.LoadPkcs12CollectionFromFile( + fileName, + password.DangerousGetSpan(), + keyStorageFlags, + X509Certificate.GetPkcs12Limits(fromFile: true, password))); } - - // Since we aren't going to finish reading, leaving the buffer where it was when we got - // it seems better than leaving it in some arbitrary other position. - // - // Use BioSeek directly for the last seek attempt, because any failure here should instead - // report the already created (but not yet thrown) exception. - if (Interop.Crypto.BioSeek(bio, bioPosition) < 0) + catch (Pkcs12LoadLimitExceededException e) { - Interop.Crypto.ErrClearError(); + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); } - - Debug.Assert(openSslException != null); - throw openSslException; } internal static partial IExportPal FromCertificate(ICertificatePalCore cert) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Windows.Import.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Windows.Import.cs index b34da971931c2c..59155e2b76a2e8 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Windows.Import.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Windows.Import.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.IO; using System.Runtime.InteropServices; using Internal.Cryptography; using Microsoft.Win32.SafeHandles; @@ -21,7 +20,7 @@ internal static partial ILoaderPal FromFile(string fileName, SafePasswordHandle return FromBlobOrFile(null, fileName, password, keyStorageFlags); } - private static StorePal FromBlobOrFile(ReadOnlySpan rawData, string? fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) + private static ILoaderPal FromBlobOrFile(ReadOnlySpan rawData, string? fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { Debug.Assert(password != null); @@ -34,9 +33,6 @@ private static StorePal FromBlobOrFile(ReadOnlySpan rawData, string? fileN fixed (char* pFileName = fileName) { Interop.Crypt32.DATA_BLOB blob = new Interop.Crypt32.DATA_BLOB(new IntPtr(pRawData), (uint)(fromFile ? 0 : rawData!.Length)); - bool persistKeySet = (0 != (keyStorageFlags & X509KeyStorageFlags.PersistKeySet)); - Interop.Crypt32.PfxCertStoreFlags certStoreFlags = MapKeyStorageFlags(keyStorageFlags); - void* pvObject = fromFile ? (void*)pFileName : (void*)&blob; Interop.Crypt32.ContentType contentType; @@ -64,46 +60,55 @@ private static StorePal FromBlobOrFile(ReadOnlySpan rawData, string? fileN { certStore.Dispose(); - if (fromFile) - { - rawData = File.ReadAllBytes(fileName!); - } - else - { - X509Certificate.EnforceIterationCountLimit(ref rawData, readingFromFile: false, password.PasswordProvided); - } + X509Certificate2Collection coll; - fixed (byte* pRawData2 = rawData) + try { - Interop.Crypt32.DATA_BLOB blob2 = new Interop.Crypt32.DATA_BLOB(new IntPtr(pRawData2), (uint)rawData!.Length); - certStore = Interop.Crypt32.PFXImportCertStore(ref blob2, password, certStoreFlags); - if (certStore == null || certStore.IsInvalid) + Pkcs12LoaderLimits limits = X509Certificate.GetPkcs12Limits(fromFile, password); + + if (fromFile) { - Exception e = Marshal.GetLastPInvokeError().ToCryptographicException(); - certStore?.Dispose(); - throw e; - } - } + Debug.Assert(fileName is not null); - if (!persistKeySet) - { - // - // If the user did not want us to persist private keys, then we should loop through all - // the certificates in the collection and set our custom CERT_CLR_DELETE_KEY_PROP_ID property - // so the key container will be deleted when the cert contexts will go away. - // - SafeCertContextHandle? pCertContext = null; - while (Interop.crypt32.CertEnumCertificatesInStore(certStore, ref pCertContext)) + coll = X509CertificateLoader.LoadPkcs12CollectionFromFile( + fileName, + password.DangerousGetSpan(), + keyStorageFlags, + limits); + } + else { - Interop.Crypt32.DATA_BLOB nullBlob = new Interop.Crypt32.DATA_BLOB(IntPtr.Zero, 0); - if (!Interop.Crypt32.CertSetCertificateContextProperty(pCertContext, Interop.Crypt32.CertContextPropId.CERT_CLR_DELETE_KEY_PROP_ID, Interop.Crypt32.CertSetPropertyFlags.CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAG, &nullBlob)) - { - Exception e = Marshal.GetLastPInvokeError().ToCryptographicException(); - certStore.Dispose(); - throw e; - } + coll = X509CertificateLoader.LoadPkcs12Collection( + rawData, + password.DangerousGetSpan(), + keyStorageFlags, + limits); } } + catch (Pkcs12LoadLimitExceededException e) + { + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); + } + + // The PFX-Collection loader for .NET Framework and .NET Core and .NET 5-8 assigned + // CERT_CLR_DELETE_KEY_PROP_ID on any certificate loaded when PersistKeySet wasn't asserted, + // which was different than the delete-tracking method utilized for single certificate PFX loads. + // + // The property-based approach meant that `new X509Certificate2(someCert.Handle)` would produce a + // second instance that was responsible for deleting the private key, and whenever the first one + // was disposed (or finalized) it would delete the key out from under the second. Since + // X509Certificate2Collection.Find produces clones, this made for some "interesting" interactions. + // + // X509CertificateLoader.LoadPkcs12Collection uses the same .NET/managed-only tracking, without + // setting a property on the native representation. + // + // If, for some reason, we want the old behavior back, we have two choices: + // 1) change it in X509CertificateLoader + // 2) Transform the returned certificates PALs here. + + return new CollectionBasedLoader(coll); } return new StorePal(certStore); diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.cs index 54a29a92fd65a6..93a00a240eaebb 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using Microsoft.Win32.SafeHandles; namespace System.Security.Cryptography.X509Certificates @@ -28,5 +29,36 @@ internal static partial IStorePal FromSystemStore( string storeName, StoreLocation storeLocation, OpenFlags openFlags); + + internal sealed class CollectionBasedLoader : ILoaderPal + { + private X509Certificate2Collection? _coll; + + internal CollectionBasedLoader(X509Certificate2Collection coll) + { + _coll = coll; + } + + public void Dispose() + { + X509Certificate2Collection? coll = _coll; + _coll = null; + + if (coll is not null) + { + foreach (X509Certificate2 cert in coll) + { + cert.Dispose(); + } + } + } + + public void MoveTo(X509Certificate2Collection collection) + { + Debug.Assert(_coll is not null); + collection.AddRange(_coll); + _coll = null; + } + } } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.iOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.iOS.cs index edccc0b79e337f..112092dcbed6cd 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.iOS.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.iOS.cs @@ -33,7 +33,6 @@ private static ILoaderPal FromBlob(ReadOnlySpan rawData, SafePasswordHandl return new CertCollectionLoader(certificateList); } - bool ephemeralSpecified = keyStorageFlags.HasFlag(X509KeyStorageFlags.EphemeralKeySet); X509ContentType contentType = AppleCertificatePal.GetDerCertContentType(rawData); if (contentType == X509ContentType.Pkcs7) @@ -45,19 +44,20 @@ private static ILoaderPal FromBlob(ReadOnlySpan rawData, SafePasswordHandl if (contentType == X509ContentType.Pkcs12) { - X509Certificate.EnforceIterationCountLimit(ref rawData, readingFromFile, password.PasswordProvided); - ApplePkcs12Reader reader = new ApplePkcs12Reader(); - try { - reader.ParsePkcs12(rawData); - reader.Decrypt(password, ephemeralSpecified); - return new ApplePkcs12CertLoader(reader, password); + return new CollectionBasedLoader( + X509CertificateLoader.LoadPkcs12Collection( + rawData, + password.DangerousGetSpan(), + keyStorageFlags, + X509Certificate.GetPkcs12Limits(readingFromFile, password))); } - catch + catch (Pkcs12LoadLimitExceededException e) { - reader.Dispose(); - throw; + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.macOS.LoaderPal.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.macOS.LoaderPal.cs index fa00011b419c12..3a9d9e0ff56a42 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.macOS.LoaderPal.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.macOS.LoaderPal.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Security.Cryptography.Apple; -using System.Threading; using Microsoft.Win32.SafeHandles; namespace System.Security.Cryptography.X509Certificates @@ -53,80 +52,5 @@ public void MoveTo(X509Certificate2Collection collection) } } } - - private sealed class ApplePkcs12CertLoader : ILoaderPal - { - private readonly ApplePkcs12Reader _pkcs12; - private readonly SafeKeychainHandle _keychain; - private SafePasswordHandle _password; - private readonly bool _exportable; - - public ApplePkcs12CertLoader( - ApplePkcs12Reader pkcs12, - SafeKeychainHandle keychain, - SafePasswordHandle password, - bool exportable) - { - _pkcs12 = pkcs12; - _keychain = keychain; - _exportable = exportable; - - bool addedRef = false; - password.DangerousAddRef(ref addedRef); - _password = password; - } - - public void Dispose() - { - _pkcs12.Dispose(); - - // Only dispose the keychain if it's a temporary handle. - (_keychain as SafeTemporaryKeychainHandle)?.Dispose(); - - SafePasswordHandle? password = Interlocked.Exchange(ref _password, null!); - password?.DangerousRelease(); - } - - public void MoveTo(X509Certificate2Collection collection) - { - foreach (UnixPkcs12Reader.CertAndKey certAndKey in _pkcs12.EnumerateAll()) - { - AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert!; - SafeSecKeyRefHandle? safeSecKeyRefHandle = - ApplePkcs12Reader.GetPrivateKey(certAndKey.Key); - - using (safeSecKeyRefHandle) - { - AppleCertificatePal newPal; - - // SecItemImport doesn't seem to respect non-exportable import for PKCS#8, - // only PKCS#12. - // - // So, as part of reading this PKCS#12 we now need to write the minimum - // PKCS#12 in a normalized form, and ask the OS to import it. - if (!_exportable && safeSecKeyRefHandle != null) - { - newPal = AppleCertificatePal.ImportPkcs12NonExportable( - pal, - safeSecKeyRefHandle, - _password, - _keychain); - } - else - { - newPal = pal.MoveToKeychain(_keychain, safeSecKeyRefHandle) ?? pal; - } - - X509Certificate2 cert = new X509Certificate2(newPal); - collection.Add(cert); - - if (newPal != pal) - { - pal.Dispose(); - } - } - } - } - } } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.macOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.macOS.cs index b424e971b09e4a..46fbc922ddb33a 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.macOS.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.macOS.cs @@ -37,22 +37,21 @@ private static ILoaderPal FromBlob(ReadOnlySpan rawData, SafePasswordHandl if (contentType == X509ContentType.Pkcs12) { - if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet) + try { - throw new PlatformNotSupportedException(SR.Cryptography_X509_NoEphemeralPfx); + return new CollectionBasedLoader( + X509CertificateLoader.LoadPkcs12Collection( + rawData, + password.DangerousGetSpan(), + keyStorageFlags, + X509Certificate.GetPkcs12Limits(readingFromFile, password))); + } + catch (Pkcs12LoadLimitExceededException e) + { + throw new CryptographicException( + SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded, + e); } - - X509Certificate.EnforceIterationCountLimit(ref rawData, readingFromFile, password.PasswordProvided); - bool exportable = (keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable; - - bool persist = - (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet; - - SafeKeychainHandle keychain = persist - ? Interop.AppleCrypto.SecKeychainCopyDefault() - : Interop.AppleCrypto.CreateTemporaryKeychain(); - - return ImportPkcs12(rawData, password, exportable, ephemeralSpecified: false, keychain); } SafeCFArrayHandle certs = Interop.AppleCrypto.X509ImportCollection( @@ -65,29 +64,6 @@ private static ILoaderPal FromBlob(ReadOnlySpan rawData, SafePasswordHandl return new AppleCertLoader(certs, null); } - private static ApplePkcs12CertLoader ImportPkcs12( - ReadOnlySpan rawData, - SafePasswordHandle password, - bool exportable, - bool ephemeralSpecified, - SafeKeychainHandle keychain) - { - ApplePkcs12Reader reader = new ApplePkcs12Reader(); - - try - { - reader.ParsePkcs12(rawData); - reader.Decrypt(password, ephemeralSpecified); - return new ApplePkcs12CertLoader(reader, keychain, password, exportable); - } - catch - { - reader.Dispose(); - keychain.Dispose(); - throw; - } - } - internal static partial ILoaderPal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { Debug.Assert(password != null); diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/UnixPkcs12Reader.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/UnixPkcs12Reader.cs deleted file mode 100644 index 1f5a24fa15be26..00000000000000 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/UnixPkcs12Reader.cs +++ /dev/null @@ -1,857 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics; -using System.Formats.Asn1; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security.Cryptography.Asn1; -using System.Security.Cryptography.Asn1.Pkcs12; -using System.Security.Cryptography.Asn1.Pkcs7; -using System.Threading; -using Internal.Cryptography; -using Microsoft.Win32.SafeHandles; - -namespace System.Security.Cryptography.X509Certificates -{ - internal abstract class UnixPkcs12Reader : IDisposable - { - private const string DecryptedSentinel = nameof(UnixPkcs12Reader); - private const int ErrorInvalidPasswordHResult = unchecked((int)0x80070056); - - private PfxAsn _pfxAsn; - private ContentInfoAsn[]? _safeContentsValues; - private CertAndKey[]? _certs; - private int _certCount; - private PointerMemoryManager? _tmpManager; - private bool _allowDoubleBind; - - protected abstract ICertificatePalCore ReadX509Der(ReadOnlyMemory data); - protected abstract AsymmetricAlgorithm LoadKey(ReadOnlyMemory safeBagBagValue); - - internal void ParsePkcs12(ReadOnlySpan data) - { - try - { - // RFC7292 specifies BER instead of DER - AsnValueReader reader = new AsnValueReader(data, AsnEncodingRules.BER); - - // Windows compatibility: Ignore trailing data. - ReadOnlySpan encodedData = reader.PeekEncodedValue(); - - unsafe - { - void* tmpPtr = NativeMemory.Alloc((uint)encodedData.Length); - - try - { - Span tmpSpan = new Span((byte*)tmpPtr, encodedData.Length); - encodedData.CopyTo(tmpSpan); - _tmpManager = new PointerMemoryManager(tmpPtr, encodedData.Length); - } - catch - { - NativeMemory.Free(tmpPtr); - throw; - } - } - - ReadOnlyMemory tmpMemory = _tmpManager.Memory; - reader = new AsnValueReader(tmpMemory.Span, AsnEncodingRules.BER); - - PfxAsn.Decode(ref reader, tmpMemory, out PfxAsn pfxAsn); - - if (pfxAsn.AuthSafe.ContentType != Oids.Pkcs7Data) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - _pfxAsn = pfxAsn; - } - catch (AsnContentException e) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); - } - } - - internal CertAndKey GetSingleCert() - { - CertAndKey[]? certs = _certs; - Debug.Assert(certs != null); - - if (_certCount < 1) - { - throw new CryptographicException(SR.Cryptography_Pfx_NoCertificates); - } - - CertAndKey ret; - - for (int i = _certCount - 1; i >= 0; --i) - { - if (certs[i].Key != null) - { - ret = certs[i]; - certs[i] = default; - return ret; - } - } - - ret = certs[_certCount - 1]; - certs[_certCount - 1] = default; - return ret; - } - - internal int GetCertCount() - { - return _certCount; - } - - internal IEnumerable EnumerateAll() - { - while (_certCount > 0) - { - int idx = _certCount - 1; - CertAndKey ret = _certs![idx]; - _certs[idx] = default; - _certCount--; - yield return ret; - } - } - - public void Dispose() - { - // Generally, having a MemoryManager cleaned up in a Dispose is a bad practice. - // In this case, the UnixPkcs12Reader is only ever created in a using statement, - // never accessed by a second thread, and there isn't a manual call to Dispose - // mixed in anywhere outside of an aborted allocation path. - - PointerMemoryManager? manager = _tmpManager; - _tmpManager = null; - - if (manager != null) - { - unsafe - { - Span tmp = manager.GetSpan(); - CryptographicOperations.ZeroMemory(tmp); - NativeMemory.Free(Unsafe.AsPointer(ref MemoryMarshal.GetReference(tmp))); - } - - ((IDisposable)manager).Dispose(); - } - - ContentInfoAsn[]? rentedContents = _safeContentsValues; - CertAndKey[]? rentedCerts = _certs; - _safeContentsValues = null; - _certs = null; - - if (rentedContents != null) - { - ReturnRentedContentInfos(rentedContents); - } - - if (rentedCerts != null) - { - for (int i = _certCount - 1; i >= 0; --i) - { - rentedCerts[i].Dispose(); - } - - ArrayPool.Shared.Return(rentedCerts, clearArray: true); - } - } - - private static void ReturnRentedContentInfos(ContentInfoAsn[] rentedContents) - { - for (int i = 0; i < rentedContents.Length; i++) - { - string contentType = rentedContents[i].ContentType; - - if (contentType == null) - { - break; - } - - if (contentType == DecryptedSentinel) - { - ReadOnlyMemory content = rentedContents[i].Content; - rentedContents[i].Content = default; - - if (!MemoryMarshal.TryGetArray(content, out ArraySegment segment)) - { - Debug.Fail("Couldn't unpack decrypted buffer."); - } - - CryptoPool.Return(segment); - } - } - - ArrayPool.Shared.Return(rentedContents, clearArray: true); - } - - public void Decrypt(SafePasswordHandle password, bool ephemeralSpecified) - { - ReadOnlyMemory authSafeContents = - Helpers.DecodeOctetStringAsMemory(_pfxAsn.AuthSafe.Content); - - _allowDoubleBind = !ephemeralSpecified; - - bool hasRef = false; - - try - { - password.DangerousAddRef(ref hasRef); - ReadOnlySpan passwordChars = password.DangerousGetSpan(); - - if (_pfxAsn.MacData.HasValue) - { - VerifyAndDecrypt(passwordChars, authSafeContents); - } - else if (passwordChars.IsEmpty) - { - try - { - // Try the empty password first. - // If anything goes wrong, try the null password. - // - // The same password has to work for the entirety of the file, - // null and empty aren't interchangeable between parts. - Decrypt("", authSafeContents); - } - catch (CryptographicException) - { - ContentInfoAsn[]? partialSuccess = _safeContentsValues; - _safeContentsValues = null; - - if (partialSuccess != null) - { - ReturnRentedContentInfos(partialSuccess); - } - - Decrypt(null, authSafeContents); - } - } - else - { - Decrypt(passwordChars, authSafeContents); - } - } - catch (Exception e) - { - throw new CryptographicException(SR.Cryptography_Pfx_BadPassword, e) - { - HResult = ErrorInvalidPasswordHResult - }; - } - finally - { - if (hasRef) - { - password.DangerousRelease(); - } - } - } - - private void VerifyAndDecrypt(ReadOnlySpan password, ReadOnlyMemory authSafeContents) - { - Debug.Assert(_pfxAsn.MacData.HasValue); - ReadOnlySpan authSafeSpan = authSafeContents.Span; - - if (password.Length == 0) - { - // VerifyMac produces different answers for the empty string and the null string, - // when the length is 0 try empty first (more common), then null. - if (_pfxAsn.VerifyMac("", authSafeSpan)) - { - Decrypt("", authSafeContents); - return; - } - - if (_pfxAsn.VerifyMac(default, authSafeSpan)) - { - Decrypt(default, authSafeContents); - return; - } - } - else if (_pfxAsn.VerifyMac(password, authSafeSpan)) - { - Decrypt(password, authSafeContents); - return; - } - - throw new CryptographicException(SR.Cryptography_Pfx_BadPassword) - { - HResult = ErrorInvalidPasswordHResult - }; - } - - private void Decrypt(ReadOnlySpan password, ReadOnlyMemory authSafeContents) - { - _safeContentsValues ??= DecodeSafeContents(authSafeContents); - - // The average PFX contains one cert, and one key. - // The next most common PFX contains 3 certs, and one key. - // - // Nothing requires that there be fewer keys than certs, - // but it's sort of nonsensical when loading this way. - CertBagAsn[] certBags = ArrayPool.Shared.Rent(10); - AttributeAsn[]?[] certBagAttrs = ArrayPool.Shared.Rent(10); - SafeBagAsn[] keyBags = ArrayPool.Shared.Rent(10); - RentedSubjectPublicKeyInfo[]? publicKeyInfos = null; - AsymmetricAlgorithm[]? keys = null; - CertAndKey[]? certs = null; - int certBagIdx = 0; - int keyBagIdx = 0; - - try - { - DecryptAndProcessSafeContents( - password, - ref certBags, - ref certBagAttrs, - ref certBagIdx, - ref keyBags, - ref keyBagIdx); - - certs = ArrayPool.Shared.Rent(certBagIdx); - certs.AsSpan().Clear(); - - keys = ArrayPool.Shared.Rent(keyBagIdx); - keys.AsSpan().Clear(); - - publicKeyInfos = ArrayPool.Shared.Rent(keyBagIdx); - publicKeyInfos.AsSpan().Clear(); - - ExtractPrivateKeys(password, keyBags, keyBagIdx, keys, publicKeyInfos); - - BuildCertsWithKeys( - password, - certBags, - certBagAttrs, - certs, - certBagIdx, - keyBags, - publicKeyInfos, - keys, - keyBagIdx); - - _certCount = certBagIdx; - _certs = certs; - } - catch - { - if (certs != null) - { - for (int i = 0; i < certBagIdx; i++) - { - CertAndKey certAndKey = certs[i]; - certAndKey.Dispose(); - } - } - - throw; - } - finally - { - if (keys != null) - { - foreach (AsymmetricAlgorithm key in keys) - { - key?.Dispose(); - } - - ArrayPool.Shared.Return(keys); - } - - if (publicKeyInfos != null) - { - for (int i = 0; i < keyBagIdx; i++) - { - publicKeyInfos[i].Dispose(); - } - - ArrayPool.Shared.Return(publicKeyInfos, clearArray: true); - } - - ArrayPool.Shared.Return(certBags, clearArray: true); - ArrayPool.Shared.Return(certBagAttrs, clearArray: true); - ArrayPool.Shared.Return(keyBags, clearArray: true); - } - } - - private static ContentInfoAsn[] DecodeSafeContents(ReadOnlyMemory authSafeContents) - { - // The expected number of ContentInfoAsns to read is 2, one encrypted (contains certs), - // and one plain (contains encrypted keys) - ContentInfoAsn[] rented = ArrayPool.Shared.Rent(10); - - try - { - AsnValueReader outer = new AsnValueReader(authSafeContents.Span, AsnEncodingRules.BER); - AsnValueReader reader = outer.ReadSequence(); - outer.ThrowIfNotEmpty(); - int i = 0; - - while (reader.HasData) - { - GrowIfNeeded(ref rented, i); - ContentInfoAsn.Decode(ref reader, authSafeContents, out rented[i]); - i++; - } - - rented.AsSpan(i).Clear(); - return rented; - } - catch (AsnContentException e) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); - } - } - - private void DecryptAndProcessSafeContents( - ReadOnlySpan password, - ref CertBagAsn[] certBags, - ref AttributeAsn[]?[] certBagAttrs, - ref int certBagIdx, - ref SafeBagAsn[] keyBags, - ref int keyBagIdx) - { - for (int i = 0; i < _safeContentsValues!.Length; i++) - { - string contentType = _safeContentsValues[i].ContentType; - bool process = false; - - if (contentType == null) - { - break; - } - - // Should enveloped throw here? - if (contentType == Oids.Pkcs7Data) - { - process = true; - } - else if (contentType == Oids.Pkcs7Encrypted) - { - DecryptSafeContents(password, ref _safeContentsValues[i]); - process = true; - } - - if (process) - { - ProcessSafeContents( - _safeContentsValues[i], - ref certBags, - ref certBagAttrs, - ref certBagIdx, - ref keyBags, - ref keyBagIdx); - } - } - } - - private void ExtractPrivateKeys( - ReadOnlySpan password, - SafeBagAsn[] keyBags, - int keyBagIdx, - AsymmetricAlgorithm[] keys, - RentedSubjectPublicKeyInfo[] publicKeyInfos) - { - byte[]? spkiBuf = null; - - for (int i = keyBagIdx - 1; i >= 0; i--) - { - ref RentedSubjectPublicKeyInfo cur = ref publicKeyInfos[i]; - - try - { - SafeBagAsn keyBag = keyBags[i]; - AsymmetricAlgorithm key = LoadKey(keyBag, password); - - int pubLength; - - while (!key.TryExportSubjectPublicKeyInfo(spkiBuf, out pubLength)) - { - byte[]? toReturn = spkiBuf; - spkiBuf = CryptoPool.Rent((toReturn?.Length ?? 128) * 2); - - if (toReturn != null) - { - // public key info doesn't need to be cleared - CryptoPool.Return(toReturn, clearSize: 0); - } - } - - cur.Value = SubjectPublicKeyInfoAsn.Decode( - spkiBuf.AsMemory(0, pubLength), - AsnEncodingRules.DER); - - keys[i] = key; - cur.TrackArray(spkiBuf, clearSize: 0); - spkiBuf = null; - } - catch (CryptographicException) - { - // Windows 10 compatibility: - // If anything goes wrong loading this key, just ignore it. - // If no one ended up needing it, no harm/no foul. - // If this has a LocalKeyId and something references it, then it'll fail. - } - finally - { - if (spkiBuf != null) - { - // Public key data doesn't need to be cleared. - CryptoPool.Return(spkiBuf, clearSize: 0); - } - } - } - } - - private void BuildCertsWithKeys( - ReadOnlySpan password, - CertBagAsn[] certBags, - AttributeAsn[]?[] certBagAttrs, - CertAndKey[] certs, - int certBagIdx, - SafeBagAsn[] keyBags, - RentedSubjectPublicKeyInfo[] publicKeyInfos, - AsymmetricAlgorithm?[] keys, - int keyBagIdx) - { - for (certBagIdx--; certBagIdx >= 0; certBagIdx--) - { - int matchingKeyIdx = -1; - - foreach (AttributeAsn attr in certBagAttrs[certBagIdx] ?? Array.Empty()) - { - if (attr.AttrType == Oids.LocalKeyId && attr.AttrValues.Length > 0) - { - matchingKeyIdx = FindMatchingKey( - keyBags, - keyBagIdx, - Helpers.DecodeOctetStringAsMemory(attr.AttrValues[0]).Span); - - // Only try the first one. - break; - } - } - - ReadOnlyMemory x509Data = - Helpers.DecodeOctetStringAsMemory(certBags[certBagIdx].CertValue); - - certs[certBagIdx].Cert = ReadX509Der(x509Data); - - // If no matching key was found, but there are keys, - // compare SubjectPublicKeyInfo values - if (matchingKeyIdx == -1 && keyBagIdx > 0) - { - ICertificatePalCore cert = certs[certBagIdx].Cert!; - string algorithm = cert.KeyAlgorithm; - byte[] keyParams = cert.KeyAlgorithmParameters; - byte[] keyValue = cert.PublicKeyValue; - - for (int i = 0; i < keyBagIdx; i++) - { - if (PublicKeyMatches(algorithm, keyParams, keyValue, ref publicKeyInfos[i].Value)) - { - matchingKeyIdx = i; - break; - } - } - } - - if (matchingKeyIdx != -1) - { - // Windows compat: - // If the PFX is loaded with EphemeralKeySet, don't allow double-bind. - // Otherwise, reload the key so a second instance is bound (avoiding one - // cert Dispose removing the key of another). - if (keys[matchingKeyIdx] == null) - { - if (_allowDoubleBind) - { - certs[certBagIdx].Key = LoadKey(keyBags[matchingKeyIdx], password); - } - else - { - throw new CryptographicException(SR.Cryptography_Pfx_BadKeyReference); - } - } - else - { - certs[certBagIdx].Key = keys[matchingKeyIdx]; - keys[matchingKeyIdx] = null; - } - } - } - } - - private static bool PublicKeyMatches( - string algorithm, - byte[] keyParams, - byte[] keyValue, - ref SubjectPublicKeyInfoAsn publicKeyInfo) - { - if (!publicKeyInfo.SubjectPublicKey.Span.SequenceEqual(keyValue)) - { - return false; - } - - switch (algorithm) - { - case Oids.Rsa: - case Oids.RsaPss: - switch (publicKeyInfo.Algorithm.Algorithm) - { - case Oids.Rsa: - case Oids.RsaPss: - break; - default: - return false; - } - - return - publicKeyInfo.Algorithm.HasNullEquivalentParameters() && - AlgorithmIdentifierAsn.RepresentsNull(keyParams); - case Oids.EcPublicKey: - case Oids.EcDiffieHellman: - switch (publicKeyInfo.Algorithm.Algorithm) - { - case Oids.EcPublicKey: - case Oids.EcDiffieHellman: - break; - default: - return false; - } - - return - publicKeyInfo.Algorithm.Parameters.HasValue && - publicKeyInfo.Algorithm.Parameters.Value.Span.SequenceEqual(keyParams); - } - - if (algorithm != publicKeyInfo.Algorithm.Algorithm) - { - return false; - } - - if (!publicKeyInfo.Algorithm.Parameters.HasValue) - { - return (keyParams?.Length ?? 0) == 0; - } - - return publicKeyInfo.Algorithm.Parameters.Value.Span.SequenceEqual(keyParams); - } - - private static int FindMatchingKey( - SafeBagAsn[] keyBags, - int keyBagCount, - ReadOnlySpan localKeyId) - { - for (int i = 0; i < keyBagCount; i++) - { - foreach (AttributeAsn attr in keyBags[i].BagAttributes ?? Array.Empty()) - { - if (attr.AttrType == Oids.LocalKeyId && attr.AttrValues.Length > 0) - { - ReadOnlyMemory curKeyId = - Helpers.DecodeOctetStringAsMemory(attr.AttrValues[0]); - - if (curKeyId.Span.SequenceEqual(localKeyId)) - { - return i; - } - - break; - } - } - } - - return -1; - } - - private static void DecryptSafeContents( - ReadOnlySpan password, - ref ContentInfoAsn safeContentsAsn) - { - EncryptedDataAsn encryptedData = - EncryptedDataAsn.Decode(safeContentsAsn.Content, AsnEncodingRules.BER); - - // https://tools.ietf.org/html/rfc5652#section-8 - if (encryptedData.Version != 0 && encryptedData.Version != 2) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - // Since the contents are supposed to be the BER-encoding of an instance of - // SafeContents (https://tools.ietf.org/html/rfc7292#section-4.1) that implies the - // content type is simply "data", and that content is present. - if (encryptedData.EncryptedContentInfo.ContentType != Oids.Pkcs7Data) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - if (!encryptedData.EncryptedContentInfo.EncryptedContent.HasValue) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - int encryptedValueLength = encryptedData.EncryptedContentInfo.EncryptedContent.Value.Length; - byte[] destination = CryptoPool.Rent(encryptedValueLength); - int written; - - try - { - written = PasswordBasedEncryption.Decrypt( - encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm, - password, - default, - encryptedData.EncryptedContentInfo.EncryptedContent.Value.Span, - destination); - } - catch - { - // Clear the whole thing, since we don't know what state we're in. - CryptoPool.Return(destination); - throw; - } - - // The DecryptedSentiel content type value will cause Dispose to return - // `destination` to the pool. - safeContentsAsn.Content = destination.AsMemory(0, written); - safeContentsAsn.ContentType = DecryptedSentinel; - } - - private static void ProcessSafeContents( - in ContentInfoAsn safeContentsAsn, - ref CertBagAsn[] certBags, - ref AttributeAsn[]?[] certBagAttrs, - ref int certBagIdx, - ref SafeBagAsn[] keyBags, - ref int keyBagIdx) - { - ReadOnlyMemory contentData = safeContentsAsn.Content; - - if (safeContentsAsn.ContentType == Oids.Pkcs7Data) - { - contentData = Helpers.DecodeOctetStringAsMemory(contentData); - } - - try - { - AsnValueReader outer = new AsnValueReader(contentData.Span, AsnEncodingRules.BER); - AsnValueReader reader = outer.ReadSequence(); - outer.ThrowIfNotEmpty(); - - while (reader.HasData) - { - SafeBagAsn.Decode(ref reader, contentData, out SafeBagAsn bag); - - if (bag.BagId == Oids.Pkcs12CertBag) - { - CertBagAsn certBag = CertBagAsn.Decode(bag.BagValue, AsnEncodingRules.BER); - - if (certBag.CertId == Oids.Pkcs12X509CertBagType) - { - GrowIfNeeded(ref certBags, certBagIdx); - GrowIfNeeded(ref certBagAttrs, certBagIdx); - certBags[certBagIdx] = certBag; - certBagAttrs[certBagIdx] = bag.BagAttributes; - certBagIdx++; - } - } - else if (bag.BagId == Oids.Pkcs12KeyBag || bag.BagId == Oids.Pkcs12ShroudedKeyBag) - { - GrowIfNeeded(ref keyBags, keyBagIdx); - keyBags[keyBagIdx] = bag; - keyBagIdx++; - } - } - } - catch (AsnContentException e) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); - } - } - - private AsymmetricAlgorithm LoadKey(SafeBagAsn safeBag, ReadOnlySpan password) - { - if (safeBag.BagId == Oids.Pkcs12ShroudedKeyBag) - { - ArraySegment decrypted = KeyFormatHelper.DecryptPkcs8( - password, - safeBag.BagValue, - out int localRead); - - try - { - if (localRead != safeBag.BagValue.Length) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - return LoadKey(decrypted.AsMemory()); - } - finally - { - CryptoPool.Return(decrypted); - } - } - - Debug.Assert(safeBag.BagId == Oids.Pkcs12KeyBag); - return LoadKey(safeBag.BagValue); - } - - private static void GrowIfNeeded(ref T[] array, int idx) - { - T[] oldRent = array; - - if (idx >= oldRent.Length) - { - T[] newRent = ArrayPool.Shared.Rent(oldRent.Length * 2); - Array.Copy(oldRent, 0, newRent, 0, idx); - array = newRent; - ArrayPool.Shared.Return(oldRent, clearArray: true); - } - } - - internal struct CertAndKey - { - internal ICertificatePalCore? Cert; - internal AsymmetricAlgorithm? Key; - - internal void Dispose() - { - Cert?.Dispose(); - Key?.Dispose(); - } - } - - private struct RentedSubjectPublicKeyInfo - { - private byte[]? _rented; - private int _clearSize; - internal SubjectPublicKeyInfoAsn Value; - - internal void TrackArray(byte[]? rented, int clearSize = CryptoPool.ClearAll) - { - Debug.Assert(_rented == null); - - _rented = rented; - _clearSize = clearSize; - } - - public void Dispose() - { - byte[]? rented = Interlocked.Exchange(ref _rented, null); - - if (rented != null) - { - CryptoPool.Return(rented, _clearSize); - } - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.LegacyLimits.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.LegacyLimits.cs new file mode 100644 index 00000000000000..16b784215456b6 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.LegacyLimits.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; + +namespace System.Security.Cryptography.X509Certificates +{ + public partial class X509Certificate + { + private static readonly Pkcs12LoaderLimits s_legacyLimits = MakeLegacyLimits(); + + internal static Pkcs12LoaderLimits GetPkcs12Limits(bool fromFile, SafePasswordHandle safePasswordHandle) + { + if (fromFile || safePasswordHandle.PasswordProvided) + { + return Pkcs12LoaderLimits.DangerousNoLimits; + } + + return s_legacyLimits; + } + + private static Pkcs12LoaderLimits MakeLegacyLimits() + { + Pkcs12LoaderLimits limits = new Pkcs12LoaderLimits + { + MacIterationLimit = 600_000, + IndividualKdfIterationLimit = 600_000, + MaxCertificates = null, + MaxKeys = null, + PreserveCertificateAlias = true, + PreserveKeyName = true, + PreserveStorageProvider = true, + PreserveUnknownAttributes = true, + }; + + long totalKdfLimit = LocalAppContextSwitches.Pkcs12UnspecifiedPasswordIterationLimit; + + if (totalKdfLimit == -1) + { + limits.TotalKdfIterationLimit = null; + } + else if (totalKdfLimit < 0) + { + limits.TotalKdfIterationLimit = LocalAppContextSwitches.DefaultPkcs12UnspecifiedPasswordIterationLimit; + } + else + { + limits.TotalKdfIterationLimit = (int)long.Min(int.MaxValue, totalKdfLimit); + } + + limits.MakeReadOnly(); + return limits; + } + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs index e186cdd15823fa..ec1be141b98a2e 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs @@ -1,22 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Formats.Asn1; using System.Globalization; using System.Runtime.Serialization; using System.Runtime.Versioning; -using System.Security.Cryptography.Asn1.Pkcs12; using System.Text; using Internal.Cryptography; using Microsoft.Win32.SafeHandles; namespace System.Security.Cryptography.X509Certificates { - public class X509Certificate : IDisposable, IDeserializationCallback, ISerializable + public partial class X509Certificate : IDisposable, IDeserializationCallback, ISerializable { private volatile byte[]? _lazyCertHash; private volatile string? _lazyIssuer; @@ -662,125 +659,15 @@ protected static string FormatDate(DateTime date) return date.ToString(culture); } - internal static void ValidateKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) - { - if ((keyStorageFlags & ~KeyStorageFlagsAll) != 0) - throw new ArgumentException(SR.Argument_InvalidFlag, nameof(keyStorageFlags)); - - const X509KeyStorageFlags EphemeralPersist = - X509KeyStorageFlags.EphemeralKeySet | X509KeyStorageFlags.PersistKeySet; - - X509KeyStorageFlags persistenceFlags = keyStorageFlags & EphemeralPersist; - - if (persistenceFlags == EphemeralPersist) - { - throw new ArgumentException( - SR.Format(SR.Cryptography_X509_InvalidFlagCombination, persistenceFlags), - nameof(keyStorageFlags)); - } - } +#pragma warning disable CA1416 // Not callable on browser. + internal static void ValidateKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) => + X509CertificateLoader.ValidateKeyStorageFlags(keyStorageFlags); +#pragma warning restore CA1416 private static void VerifyContentType(X509ContentType contentType) { if (!(contentType == X509ContentType.Cert || contentType == X509ContentType.SerializedCert || contentType == X509ContentType.Pkcs12)) throw new CryptographicException(SR.Cryptography_X509_InvalidContentType); } - - internal static void EnforceIterationCountLimit(ref ReadOnlySpan pkcs12, bool readingFromFile, bool passwordProvided) - { - if (readingFromFile || passwordProvided) - { - return; - } - - long pkcs12UnspecifiedPasswordIterationLimit = LocalAppContextSwitches.Pkcs12UnspecifiedPasswordIterationLimit; - - // -1 = no limit - if (LocalAppContextSwitches.Pkcs12UnspecifiedPasswordIterationLimit == -1) - { - return; - } - - // any other negative number means use default limits - if (pkcs12UnspecifiedPasswordIterationLimit < 0) - { - pkcs12UnspecifiedPasswordIterationLimit = LocalAppContextSwitches.DefaultPkcs12UnspecifiedPasswordIterationLimit; - } - - try - { - try - { - checked - { - KdfWorkLimiter.SetIterationLimit((ulong)pkcs12UnspecifiedPasswordIterationLimit); - ulong observedIterationCount = GetIterationCount(pkcs12, out int bytesConsumed); - pkcs12 = pkcs12.Slice(0, bytesConsumed); - - // Check both conditions: we want a KDF-exceeded failure anywhere in the system to produce a failure here. - // There are some places within the GetIterationCount method where we optimistically try processing the - // PFX in one manner, and if we see failures we'll swallow any exceptions and try a different manner - // instead. The problem with this is that when we swallow failures, we don't have the ability to add the - // so-far-observed iteration count back to the running total returned by GetIterationCount. This - // potentially allows a clever adversary a window through which to squeeze in work beyond our configured - // limits. To mitigate this risk, we'll fail now if we observed *any* KDF-exceeded failure while processing - // this PFX. - if (observedIterationCount > (ulong)pkcs12UnspecifiedPasswordIterationLimit || KdfWorkLimiter.WasWorkLimitExceeded()) - { - throw new X509IterationCountExceededException(); // iteration count exceeded - } - } - } - finally - { - KdfWorkLimiter.ResetIterationLimit(); - } - } - catch (X509IterationCountExceededException) - { - throw new CryptographicException(SR.Cryptography_X509_PfxWithoutPassword_MaxAllowedIterationsExceeded); - } - catch (Exception ex) - { - // It's important for this catch-all block to be *outside* the inner try/finally - // so that we can prevent exception filters from running before we've had a chance - // to clean up the threadstatic. - throw new CryptographicException(SR.Cryptography_X509_PfxWithoutPassword_ProblemFound, ex); - } - } - - internal static ulong GetIterationCount(ReadOnlySpan pkcs12, out int bytesConsumed) - { - ulong iterations; - - unsafe - { - fixed (byte* pin = pkcs12) - { - using (var manager = new PointerMemoryManager(pin, pkcs12.Length)) - { - AsnValueReader reader = new AsnValueReader(pkcs12, AsnEncodingRules.BER); - int encodedLength = reader.PeekEncodedValue().Length; - PfxAsn.Decode(ref reader, manager.Memory, out PfxAsn pfx); - - // Don't throw when trailing data is present. - // Windows doesn't have such enforcement as well. - - iterations = pfx.CountTotalIterations(); - bytesConsumed = encodedLength; - } - } - } - - return iterations; - } - - internal const X509KeyStorageFlags KeyStorageFlagsAll = - X509KeyStorageFlags.UserKeySet | - X509KeyStorageFlags.MachineKeySet | - X509KeyStorageFlags.Exportable | - X509KeyStorageFlags.UserProtected | - X509KeyStorageFlags.PersistKeySet | - X509KeyStorageFlags.EphemeralKeySet; } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Android.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Android.cs new file mode 100644 index 00000000000000..8a1da9bc03d53e --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Android.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Formats.Asn1; +using System.IO; + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + private static partial ICertificatePal LoadCertificatePal(ReadOnlySpan data) + { + if (!AndroidCertificatePal.TryReadX509(data, out ICertificatePal? cert)) + { + cert?.Dispose(); + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return cert; + } + + private static partial ICertificatePal LoadCertificatePalFromFile(string path) + { + ArgumentException.ThrowIfNullOrEmpty(path); + + using (FileStream stream = File.OpenRead(path)) + { + int length = (int)long.Min(int.MaxValue, stream.Length); + byte[] buf = CryptoPool.Rent(length); + + try + { + stream.ReadAtLeast(buf, length); + return LoadCertificatePal(buf.AsSpan(0, length)); + } + finally + { + CryptoPool.Return(buf, length); + } + } + } + + static partial void ValidatePlatformKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) + { + if ((keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet) + { + throw new PlatformNotSupportedException(SR.Cryptography_X509_PKCS12_PersistKeySetNotSupported); + } + } + + private static partial Pkcs12Return FromCertAndKey(CertAndKey certAndKey, ImportState importState) + { + AndroidCertificatePal pal = (AndroidCertificatePal)certAndKey.Cert!; + + if (certAndKey.Key != null) + { + pal.SetPrivateKey(GetPrivateKey(certAndKey.Key)); + certAndKey.Key.Dispose(); + } + + return new Pkcs12Return(pal); + } + + private static partial AsymmetricAlgorithm? CreateKey(string algorithm) + { + return algorithm switch + { + Oids.Rsa or Oids.RsaPss => new RSAImplementation.RSAAndroid(), + Oids.EcPublicKey or Oids.EcDiffieHellman => new ECDsaImplementation.ECDsaAndroid(), + Oids.Dsa => new DSAImplementation.DSAAndroid(), + _ => null, + }; + } + + internal static SafeKeyHandle GetPrivateKey(AsymmetricAlgorithm key) + { + if (key is ECDsaImplementation.ECDsaAndroid ecdsa) + { + return ecdsa.DuplicateKeyHandle(); + } + + if (key is RSAImplementation.RSAAndroid rsa) + { + return rsa.DuplicateKeyHandle(); + } + + if (key is DSAImplementation.DSAAndroid dsa) + { + return dsa.DuplicateKeyHandle(); + } + + throw new NotImplementedException($"{nameof(GetPrivateKey)} ({key.GetType()})"); + } + + private static partial ICertificatePalCore LoadX509Der(ReadOnlyMemory data) + { + ReadOnlySpan span = data.Span; + + AsnValueReader reader = new AsnValueReader(span, AsnEncodingRules.DER); + reader.ReadSequence(); + reader.ThrowIfNotEmpty(); + + if (!AndroidCertificatePal.TryReadX509(span, out ICertificatePal? cert)) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return cert; + } + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.NotSupported.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.NotSupported.cs new file mode 100644 index 00000000000000..95e2f393d69405 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.NotSupported.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + private static partial ICertificatePal LoadCertificatePal(ReadOnlySpan data) + { + throw new PlatformNotSupportedException(SR.SystemSecurityCryptographyX509Certificates_PlatformNotSupported); + } + + private static partial ICertificatePal LoadCertificatePalFromFile(string path) + { + throw new PlatformNotSupportedException(SR.SystemSecurityCryptographyX509Certificates_PlatformNotSupported); + } + + private static partial Pkcs12Return LoadPkcs12( + ref BagState bagState, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags) + { + throw new PlatformNotSupportedException(SR.SystemSecurityCryptographyX509Certificates_PlatformNotSupported); + } + + private static partial X509Certificate2Collection LoadPkcs12Collection( + ref BagState bagState, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags) + { + throw new PlatformNotSupportedException(SR.SystemSecurityCryptographyX509Certificates_PlatformNotSupported); + } + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs new file mode 100644 index 00000000000000..c725ce79b82698 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + private static partial ICertificatePal LoadCertificatePal(ReadOnlySpan data) + { + ICertificatePal? pal; + + if (OpenSslX509CertificateReader.TryReadX509Der(data, out pal) || + OpenSslX509CertificateReader.TryReadX509Pem(data, out pal)) + { + return pal; + } + + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + private static partial ICertificatePal LoadCertificatePalFromFile(string path) + { + ICertificatePal? pal; + + using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(path, "rb")) + { + Interop.Crypto.CheckValidOpenSslHandle(fileBio); + + int bioPosition = Interop.Crypto.BioTell(fileBio); + + if (!OpenSslX509CertificateReader.TryReadX509Der(fileBio, out pal)) + { + Interop.Crypto.BioSeek(fileBio, bioPosition); + + if (!OpenSslX509CertificateReader.TryReadX509Pem(fileBio, out pal)) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + } + } + + return pal; + } + + private static partial Pkcs12Return FromCertAndKey(CertAndKey certAndKey, ImportState importState) + { + OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)certAndKey.Cert!; + + if (certAndKey.Key is not null) + { + pal.SetPrivateKey(GetPrivateKey(certAndKey.Key)); + certAndKey.Key.Dispose(); + } + + return new Pkcs12Return(pal); + } + + private static partial AsymmetricAlgorithm? CreateKey(string algorithm) + { + return algorithm switch + { + Oids.Rsa or Oids.RsaPss => new RSAOpenSsl(), + Oids.EcPublicKey or Oids.EcDiffieHellman => new ECDiffieHellmanOpenSsl(), + Oids.Dsa => new DSAOpenSsl(), + _ => null, + }; + } + + internal static SafeEvpPKeyHandle GetPrivateKey(AsymmetricAlgorithm key) + { + if (key is RSAOpenSsl rsa) + { + return rsa.DuplicateKeyHandle(); + } + + if (key is DSAOpenSsl dsa) + { + return dsa.DuplicateKeyHandle(); + } + + return ((ECDiffieHellmanOpenSsl)key).DuplicateKeyHandle(); + } + + private static partial ICertificatePalCore LoadX509Der(ReadOnlyMemory data) + { + if (OpenSslX509CertificateReader.TryReadX509Der(data.Span, out ICertificatePal? ret)) + { + return ret; + } + + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Unix.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Unix.cs new file mode 100644 index 00000000000000..8cf2d16bdde673 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Unix.cs @@ -0,0 +1,604 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Formats.Asn1; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Asn1.Pkcs12; +using Internal.Cryptography; + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + private static partial Pkcs12Return FromCertAndKey(CertAndKey certAndKey, ImportState importState); + + private static partial AsymmetricAlgorithm? CreateKey(string algorithm); + + private static partial ICertificatePalCore LoadX509Der(ReadOnlyMemory data); + + static partial void InitializeImportState(ref ImportState importState, X509KeyStorageFlags keyStorageFlags); + + private static partial Pkcs12Return LoadPkcs12( + ref BagState bagState, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags) + { + bool ephemeral = (keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) != 0; + + CertKeyMatcher matcher = default; + CertAndKey[]? certsAndKeys = null; + ImportState importState = default; + + try + { + matcher.LoadCerts(ref bagState); + matcher.LoadKeys(ref bagState); + + // Windows compat: Don't allow double-bind for EphemeralKeySet loads. + certsAndKeys = matcher.MatchCertAndKeys(ref bagState, !ephemeral); + + int matchIndex; + + for (matchIndex = bagState.CertCount - 1; matchIndex >= 0; matchIndex--) + { + if (certsAndKeys[matchIndex].Key is not null) + { + break; + } + } + + if (matchIndex < 0) + { + matchIndex = bagState.CertCount - 1; + } + + Debug.Assert(matchIndex >= 0); + + InitializeImportState(ref importState, keyStorageFlags); + Pkcs12Return ret = FromCertAndKey(certsAndKeys[matchIndex], importState); + certsAndKeys[matchIndex] = default; + + return ret; + } + finally + { + if (certsAndKeys is not null) + { + CertKeyMatcher.Free(certsAndKeys, bagState.CertCount); + } + + importState.Dispose(); + matcher.Dispose(); + } + } + + private static partial X509Certificate2Collection LoadPkcs12Collection( + ref BagState bagState, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags) + { + bool ephemeral = (keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) != 0; + + CertKeyMatcher matcher = default; + CertAndKey[]? certsAndKeys = null; + ImportState importState = default; + + try + { + matcher.LoadCerts(ref bagState); + matcher.LoadKeys(ref bagState); + + // Windows compat: Don't allow double-bind for EphemeralKeySet loads. + certsAndKeys = matcher.MatchCertAndKeys(ref bagState, !ephemeral); + + InitializeImportState(ref importState, keyStorageFlags); + + X509Certificate2Collection coll = new X509Certificate2Collection(); + + for (int i = bagState.CertCount - 1; i >= 0; i--) + { + coll.Add(FromCertAndKey(certsAndKeys[i], importState).ToCertificate()); + certsAndKeys[i] = default; + } + + return coll; + } + finally + { + if (certsAndKeys is not null) + { + CertKeyMatcher.Free(certsAndKeys, bagState.CertCount); + } + + importState.Dispose(); + matcher.Dispose(); + } + } + + internal static unsafe bool IsPkcs12(ReadOnlySpan data) + { + if (data.IsEmpty) + { + return false; + } + + fixed (byte* ptr = data) + { + using (PointerMemoryManager manager = new(ptr, data.Length)) + { + try + { + ReadOnlyMemory memory = manager.Memory; + AsnValueReader reader = new AsnValueReader(memory.Span, AsnEncodingRules.BER); + PfxAsn.Decode(ref reader, memory, out _); + return true; + } + catch (AsnContentException) + { + } + catch (CryptographicException) + { + } + + return false; + } + } + } + + internal static bool IsPkcs12(string path) + { + Debug.Assert(!string.IsNullOrEmpty(path)); + + (byte[]? rented, int length, MemoryManager? manager) = ReadAllBytesIfBerSequence(path); + + try + { + ReadOnlyMemory memory = manager?.Memory ?? new ReadOnlyMemory(rented, 0, length); + + AsnValueReader reader = new AsnValueReader(memory.Span, AsnEncodingRules.BER); + PfxAsn.Decode(ref reader, memory, out _); + return true; + } + catch (AsnContentException) + { + } + catch (CryptographicException) + { + } + finally + { + (manager as IDisposable)?.Dispose(); + + if (rented is not null) + { + CryptoPool.Return(rented, length); + } + } + + return false; + } + + private partial struct BagState + { + internal ReadOnlySpan GetCertsSpan() + { + return new ReadOnlySpan(_certBags, 0, _certCount); + } + + internal ReadOnlySpan GetKeysSpan() + { + return new ReadOnlySpan(_keyBags, 0, _keyCount); + } + } + + private struct CertAndKey + { + internal ICertificatePalCore? Cert; + internal AsymmetricAlgorithm? Key; + + internal void Dispose() + { + Cert?.Dispose(); + Key?.Dispose(); + } + } + + private struct CertKeyMatcher + { + private CertAndKey[] _certAndKeys; + private int _certCount; + private AsymmetricAlgorithm?[] _keys; + private RentedSubjectPublicKeyInfo[] _rentedSpki; + private int _keyCount; + + internal void LoadCerts(ref BagState bagState) + { + if (bagState.CertCount == 0) + { + return; + } + + _certAndKeys = ArrayPool.Shared.Rent(bagState.CertCount); + + foreach (SafeBagAsn safeBag in bagState.GetCertsSpan()) + { + Debug.Assert(safeBag.BagId == Oids.Pkcs12CertBag); + + CertBagAsn certBag = CertBagAsn.Decode(safeBag.BagValue, AsnEncodingRules.BER); + + // Non-X.509 cert-type bags should have already been removed. + Debug.Assert(certBag.CertId == Oids.Pkcs12X509CertBagType); + ReadOnlyMemory certData = Helpers.DecodeOctetStringAsMemory(certBag.CertValue); + + ICertificatePalCore pal = LoadX509Der(certData); + Debug.Assert(pal is not null); + + _certAndKeys[_certCount].Cert = pal; + _certCount++; + } + } + + internal void LoadKeys(ref BagState bagState) + { + if (bagState.KeyCount == 0) + { + return; + } + + _keys = ArrayPool.Shared.Rent(bagState.KeyCount); + + foreach (SafeBagAsn safeBag in bagState.GetKeysSpan()) + { + AsymmetricAlgorithm? key = null; + + try + { + if (safeBag.BagId == Oids.Pkcs12KeyBag) + { + PrivateKeyInfoAsn privateKeyInfo = + PrivateKeyInfoAsn.Decode(safeBag.BagValue, AsnEncodingRules.BER); + + key = CreateKey(privateKeyInfo.PrivateKeyAlgorithm.Algorithm); + + if (key is not null) + { + // No need to check bytesRead since it was verified + // in PrivateKeyInfoAsn.Decode + key.ImportPkcs8PrivateKey(safeBag.BagValue.Span, out int _); + + if (_rentedSpki is null) + { + _rentedSpki = + ArrayPool.Shared.Rent(bagState.KeyCount); + _rentedSpki.AsSpan().Clear(); + } + + ExtractPublicKey(ref _rentedSpki[_keyCount], key, safeBag.BagValue.Length); + } + } + else + { + // There may still be shrouded bags in the state, signifying that + // decryption failed for them. They get ignored, unless matched + // by keyId, which produces a failure. + // + // If there's any other kind of bag here, there's a mismatch between + // this code and the main extractor. + Debug.Assert(safeBag.BagId == Oids.Pkcs12ShroudedKeyBag); + } + } + catch (AsnContentException) + { + key?.Dispose(); + key = null; + } + catch (CryptographicException) + { + key?.Dispose(); + key = null; + } + + if (key is not null) + { + _keys[_keyCount] = key; + } + + _keyCount++; + } + } + + internal CertAndKey[] MatchCertAndKeys(ref BagState bagState, bool allowDoubleBind) + { + ReadOnlySpan certBags = bagState.GetCertsSpan(); + ReadOnlySpan keyBags = bagState.GetKeysSpan(); + + for (int certBagIdx = certBags.Length - 1; certBagIdx >= 0; certBagIdx--) + { + int matchingKeyIdx = -1; + + foreach (AttributeAsn attr in certBags[certBagIdx].BagAttributes ?? Array.Empty()) + { + if (attr.AttrType == Oids.LocalKeyId && attr.AttrValues.Length > 0) + { + matchingKeyIdx = FindMatchingKey( + keyBags, + Helpers.DecodeOctetStringAsMemory(attr.AttrValues[0]).Span); + + // Only try the first one. + break; + } + } + + ICertificatePalCore cert = _certAndKeys[certBagIdx].Cert!; + + // If no matching key was found, but there are keys, + // compare SubjectPublicKeyInfo values + if (matchingKeyIdx == -1 && _rentedSpki is not null) + { + for (int i = 0; i < keyBags.Length; i++) + { + if (PublicKeyMatches(cert, ref _rentedSpki[i].Value)) + { + matchingKeyIdx = i; + break; + } + } + } + + if (matchingKeyIdx != -1) + { + // Windows compat: + // If the PFX is loaded with EphemeralKeySet, don't allow double-bind. + // Otherwise, reload the key so a second instance is bound (avoiding one + // cert Dispose removing the key of another). + if (_keys[matchingKeyIdx] is null) + { + // The key could be null because we already matched it (and made it null), + // or because it never loaded. + SafeBagAsn keyBag = keyBags[matchingKeyIdx]; + + if (keyBag.BagId != Oids.Pkcs12KeyBag) + { + // The key bag didn't get transformed. + // That means the password didn't decrypt it. + + if (bagState.ConfirmedPassword) + { + throw new CryptographicException(SR.Cryptography_Pfx_BadKeyReference); + } + + throw new CryptographicException(SR.Cryptography_Pfx_BadPassword) + { + HResult = ERROR_INVALID_PASSWORD, + }; + } + + if (allowDoubleBind) + { + + AsymmetricAlgorithm? key = CreateKey(cert.KeyAlgorithm); + + if (key is null) + { + // The key is actually an algorithm that isn't supported... + + throw new CryptographicException( + SR.Cryptography_UnknownAlgorithmIdentifier, + cert.KeyAlgorithm); + } + + _certAndKeys[certBagIdx].Key = key; + key.ImportPkcs8PrivateKey(keyBag.BagValue.Span, out _); + } + else + { + throw new CryptographicException(SR.Cryptography_Pfx_BadKeyReference); + } + } + else + { + _certAndKeys[certBagIdx].Key = _keys[matchingKeyIdx]; + _keys[matchingKeyIdx] = null; + } + } + } + + CertAndKey[] ret = _certAndKeys; + _certAndKeys = null!; + return ret; + } + + private static int FindMatchingKey( + ReadOnlySpan keyBags, + ReadOnlySpan localKeyId) + { + for (int i = 0; i < keyBags.Length; i++) + { + foreach (AttributeAsn attr in keyBags[i].BagAttributes ?? Array.Empty()) + { + if (attr.AttrType == Oids.LocalKeyId && attr.AttrValues.Length > 0) + { + ReadOnlyMemory curKeyId = + Helpers.DecodeOctetStringAsMemory(attr.AttrValues[0]); + + if (curKeyId.Span.SequenceEqual(localKeyId)) + { + return i; + } + + break; + } + } + } + + return -1; + } + + private static bool PublicKeyMatches( + ICertificatePalCore cert, + ref SubjectPublicKeyInfoAsn publicKeyInfo) + { + string certAlgorithm = cert.KeyAlgorithm; + string keyAlgorithm = publicKeyInfo.Algorithm.Algorithm; + + bool algorithmMatches = certAlgorithm switch + { + // RSA/RSA-PSS are interchangeable + Oids.Rsa or Oids.Rsa => + keyAlgorithm is Oids.Rsa or Oids.RsaPss, + + // id-ecPublicKey and id-ecDH are interchangeable + Oids.EcPublicKey or Oids.EcDiffieHellman => + keyAlgorithm is Oids.EcPublicKey or Oids.EcDiffieHellman, + + // Everything else is an exact match. + _ => certAlgorithm.Equals(keyAlgorithm, StringComparison.Ordinal), + }; + + if (!algorithmMatches) + { + return false; + } + + // Both cert.PublicKeyValue and cert.KeyAlgorithmParameters use memoization + // on all applicable platforms, but they are still worth deferring past the + // algorithm family check. Once they need to be queried at all, + // PublicKeyValue is more likely to be distinct, so query it first. + + byte[] certEncodedKeyValue = cert.PublicKeyValue; + + if (!publicKeyInfo.SubjectPublicKey.Span.SequenceEqual(certEncodedKeyValue)) + { + return false; + } + + byte[] certKeyParameters = cert.KeyAlgorithmParameters; + + switch (certAlgorithm) + { + // Accept either DER-NULL or missing for RSA algorithm parameters + case Oids.Rsa: + case Oids.RsaPss: + return + publicKeyInfo.Algorithm.HasNullEquivalentParameters() && + AlgorithmIdentifierAsn.RepresentsNull(certKeyParameters); + + // For ECC the parameters are required, and must match exactly. + case Oids.EcPublicKey: + case Oids.EcDiffieHellman: + return + publicKeyInfo.Algorithm.Parameters.HasValue && + publicKeyInfo.Algorithm.Parameters.Value.Span.SequenceEqual(certKeyParameters); + } + + // Any other algorithm matches null/empty parameters as equivalent + if (!publicKeyInfo.Algorithm.Parameters.HasValue) + { + return (certKeyParameters?.Length ?? 0) == 0; + } + + return publicKeyInfo.Algorithm.Parameters.Value.Span.SequenceEqual(certKeyParameters); + } + + private static void ExtractPublicKey( + ref RentedSubjectPublicKeyInfo spki, + AsymmetricAlgorithm key, + int sizeHint) + { + Debug.Assert(sizeHint > 0); + + byte[] buf = CryptoPool.Rent(sizeHint); + int written; + + while (!key.TryExportSubjectPublicKeyInfo(buf, out written)) + { + sizeHint = checked(buf.Length * 2); + CryptoPool.Return(buf); + buf = CryptoPool.Rent(sizeHint); + } + + spki.TrackArray(buf, written); + + spki.Value = SubjectPublicKeyInfoAsn.Decode( + buf.AsMemory(0, written), + AsnEncodingRules.BER); + } + + internal static void Free(CertAndKey[] certAndKeys, int count) + { + for (int i = count - 1; i >= 0; i--) + { + certAndKeys[i].Dispose(); + } + + ArrayPool.Shared.Return(certAndKeys, clearArray: true); + } + + internal void Dispose() + { + if (_certAndKeys is not null) + { + Free(_certAndKeys, _certCount); + } + + if (_keys is not null) + { + for (int i = _keyCount - 1; i >= 0; i--) + { + _keys[i]?.Dispose(); + } + + ArrayPool.Shared.Return(_keys, clearArray: true); + } + + if (_rentedSpki is not null) + { + for (int i = _keyCount - 1; i >= 0; i--) + { + _rentedSpki[i].Dispose(); + } + } + + this = default; + } + } + + private struct RentedSubjectPublicKeyInfo + { + private byte[]? _rented; + private int _clearSize; + internal SubjectPublicKeyInfoAsn Value; + + internal void TrackArray(byte[]? rented, int clearSize = CryptoPool.ClearAll) + { + Debug.Assert(_rented is null); + + _rented = rented; + _clearSize = clearSize; + } + + internal void Dispose() + { + byte[]? rented = _rented; + _rented = null; + + if (rented != null) + { + CryptoPool.Return(rented, _clearSize); + } + } + } + + private partial struct ImportState + { + partial void DisposeCore(); + + internal void Dispose() + { + DisposeCore(); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Windows.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Windows.cs new file mode 100644 index 00000000000000..9672990faff844 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Windows.cs @@ -0,0 +1,336 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using Internal.Cryptography; +using Microsoft.Win32.SafeHandles; + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + private static partial ICertificatePal LoadCertificatePal(ReadOnlySpan data) + { + unsafe + { + fixed (byte* dataPtr = data) + { + Interop.Crypt32.DATA_BLOB blob = new Interop.Crypt32.DATA_BLOB( + (IntPtr)dataPtr, + (uint)data.Length); + + return LoadCertificate( + Interop.Crypt32.CertQueryObjectType.CERT_QUERY_OBJECT_BLOB, + &blob); + } + } + } + + private static partial ICertificatePal LoadCertificatePalFromFile(string path) + { + unsafe + { + fixed (char* pathPtr = path) + { + return LoadCertificate( + Interop.Crypt32.CertQueryObjectType.CERT_QUERY_OBJECT_FILE, + pathPtr); + } + } + } + + static partial void LoadPkcs12NoLimits( + ReadOnlyMemory data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + ref Pkcs12Return earlyReturn) + { + bool deleteKeyContainer = ShouldDeleteKeyContainer(keyStorageFlags); + + using (SafeCertStoreHandle storeHandle = ImportPfx(data.Span, password, keyStorageFlags)) + { + CertificatePal pal = LoadPkcs12(storeHandle, deleteKeyContainer); + earlyReturn = new Pkcs12Return(pal); + } + } + + static partial void LoadPkcs12NoLimits( + ReadOnlyMemory data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + ref X509Certificate2Collection? earlyReturn) + { + bool deleteKeyContainers = ShouldDeleteKeyContainer(keyStorageFlags); + + using (SafeCertStoreHandle storeHandle = ImportPfx(data.Span, password, keyStorageFlags)) + { + earlyReturn = LoadPkcs12Collection(storeHandle, deleteKeyContainers); + } + } + + private static partial Pkcs12Return LoadPkcs12( + ref BagState bagState, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags) + { + bool deleteKeyContainer = ShouldDeleteKeyContainer(keyStorageFlags); + + using (SafeCertStoreHandle storeHandle = ImportPfx(ref bagState, password, keyStorageFlags)) + { + CertificatePal pal = LoadPkcs12(storeHandle, deleteKeyContainer); + return new Pkcs12Return(pal); + } + } + + private static CertificatePal LoadPkcs12( + SafeCertStoreHandle storeHandle, + bool deleteKeyContainer) + { + // Find the first cert with private key. If none, then simply take the very first cert. + // Along the way, delete the persisted keys of any cert we don't accept. + SafeCertContextHandle? bestCert = null; + SafeCertContextHandle? nextCert = null; + bool havePrivKey = false; + + while (Interop.crypt32.CertEnumCertificatesInStore(storeHandle, ref nextCert)) + { + Debug.Assert(nextCert is not null); + Debug.Assert(!nextCert.IsInvalid); + + if (nextCert.ContainsPrivateKey) + { + if (bestCert is not null && bestCert.ContainsPrivateKey) + { + // We already found our chosen one. Free up this one's key and move on. + + // If this one has a persisted private key, clean up the key file. + // If it was an ephemeral private key no action is required. + if (nextCert.HasPersistedPrivateKey) + { + SafeCertContextHandleWithKeyContainerDeletion.DeleteKeyContainer(nextCert); + } + } + else + { + // Found our first cert that has a private key. + // + // Set it up as our chosen one but keep iterating + // as we need to free up the keys of any remaining certs. + bestCert?.Dispose(); + bestCert = nextCert.Duplicate(); + havePrivKey = true; + } + } + else + { + // Doesn't have a private key but hang on to it anyway, + // in case we don't find any certs with a private key. + bestCert ??= nextCert.Duplicate(); + } + } + + if (bestCert is null) + { + throw new CryptographicException(SR.Cryptography_Pfx_NoCertificates); + } + + bool deleteThisKeyContainer = havePrivKey && deleteKeyContainer; + CertificatePal pal = new CertificatePal(bestCert, deleteThisKeyContainer); + return pal; + } + + private static partial X509Certificate2Collection LoadPkcs12Collection( + ref BagState bagState, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags) + { + bool deleteKeyContainers = ShouldDeleteKeyContainer(keyStorageFlags); + + using (SafeCertStoreHandle storeHandle = ImportPfx(ref bagState, password, keyStorageFlags)) + { + return LoadPkcs12Collection(storeHandle, deleteKeyContainers); + } + } + + private static X509Certificate2Collection LoadPkcs12Collection( + SafeCertStoreHandle storeHandle, + bool deleteKeyContainers) + { + X509Certificate2Collection coll = new X509Certificate2Collection(); + SafeCertContextHandle? nextCert = null; + + while (Interop.crypt32.CertEnumCertificatesInStore(storeHandle, ref nextCert)) + { + Debug.Assert(nextCert is not null); + Debug.Assert(!nextCert.IsInvalid); + + bool deleteThis = deleteKeyContainers && nextCert.HasPersistedPrivateKey; + CertificatePal pal = new CertificatePal(nextCert.Duplicate(), deleteThis); + coll.Add(new X509Certificate2(pal)); + } + + return coll; + } + + private static unsafe CertificatePal LoadCertificate( + Interop.Crypt32.CertQueryObjectType objectType, + void* pvObject) + { + Debug.Assert(objectType != 0); + Debug.Assert(pvObject != (void*)0); + + const Interop.Crypt32.ContentType ContentType = + Interop.Crypt32.ContentType.CERT_QUERY_CONTENT_CERT; + const Interop.Crypt32.ExpectedContentTypeFlags ExpectedContentType = + Interop.Crypt32.ExpectedContentTypeFlags.CERT_QUERY_CONTENT_FLAG_CERT; + + bool loaded = Interop.Crypt32.CryptQueryObject( + objectType, + pvObject, + ExpectedContentType, + Interop.Crypt32.ExpectedFormatTypeFlags.CERT_QUERY_FORMAT_FLAG_ALL, + dwFlags: 0, + pdwMsgAndCertEncodingType: IntPtr.Zero, + out Interop.Crypt32.ContentType actualType, + pdwFormatType: IntPtr.Zero, + phCertStore: IntPtr.Zero, + phMsg: IntPtr.Zero, + out SafeCertContextHandle singleContext); + + if (!loaded) + { + singleContext.Dispose(); + throw Marshal.GetHRForLastWin32Error().ToCryptographicException(); + } + + // Since contentType is an input filter, actualType should not be possible to disagree. + // + // Since contentType is only CERT, singleContext should either be valid, or the + // function should have returned false. + if (actualType != ContentType || singleContext.IsInvalid) + { + singleContext.Dispose(); + throw new CryptographicException(); + } + + CertificatePal pal = new CertificatePal(singleContext, deleteKeyContainer: false); + return pal; + } + + private static SafeCertStoreHandle ImportPfx( + ref BagState bagState, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags) + { + ArraySegment reassembled = bagState.ToPfx(password); + SafeCertStoreHandle storeHandle = ImportPfx(reassembled, password, keyStorageFlags); + CryptoPool.Return(reassembled); + + return storeHandle; + } + + private static unsafe SafeCertStoreHandle ImportPfx( + ReadOnlySpan data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags) + { + const int MaxStackPasswordLength = 64; + void* alloc = default; + Span szPassword = stackalloc char[MaxStackPasswordLength + 1]; + ReadOnlySpan effectivePassword = szPassword; + Interop.Crypt32.PfxCertStoreFlags flags = MapKeyStorageFlags(keyStorageFlags); + + try + { + if (password.Contains('\0')) + { + effectivePassword = password; + } + else + { + if (password.Length >= MaxStackPasswordLength) + { + alloc = NativeMemory.Alloc((uint)password.Length + 1, sizeof(char)); + szPassword = new Span(alloc, password.Length + 1); + } + + password.CopyTo(szPassword); + szPassword[password.Length] = '\0'; + } + + SafeCertStoreHandle storeHandle; + + fixed (byte* dataPtr = data) + fixed (char* szPtr = szPassword) + { + Interop.Crypt32.DATA_BLOB blob = new((IntPtr)dataPtr, (uint)data.Length); + + storeHandle = Interop.Crypt32.PFXImportCertStore( + ref blob, + szPtr, + flags); + } + + if (storeHandle.IsInvalid) + { + Exception e = Marshal.GetHRForLastWin32Error().ToCryptographicException(); + storeHandle.Dispose(); + throw e; + } + + return storeHandle; + } + finally + { + CryptographicOperations.ZeroMemory(MemoryMarshal.AsBytes(szPassword)); + + if (alloc != (void*)0) + { + NativeMemory.Free(alloc); + } + } + } + + private static Interop.Crypt32.PfxCertStoreFlags MapKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) + { + Debug.Assert((keyStorageFlags & KeyStorageFlagsAll) == keyStorageFlags); + + Interop.Crypt32.PfxCertStoreFlags pfxCertStoreFlags = 0; + if ((keyStorageFlags & X509KeyStorageFlags.UserKeySet) == X509KeyStorageFlags.UserKeySet) + pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_USER_KEYSET; + else if ((keyStorageFlags & X509KeyStorageFlags.MachineKeySet) == X509KeyStorageFlags.MachineKeySet) + pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_MACHINE_KEYSET; + + if ((keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable) + pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_EXPORTABLE; + if ((keyStorageFlags & X509KeyStorageFlags.UserProtected) == X509KeyStorageFlags.UserProtected) + pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_USER_PROTECTED; + + // If a user is asking for an Ephemeral key they should be willing to test their code to find out + // that it will no longer import into CAPI. This solves problems of legacy CSPs being + // difficult to do SHA-2 RSA signatures with, simplifies the story for UWP, and reduces the + // complexity of pointer interpretation. + if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet) + pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.PKCS12_NO_PERSIST_KEY | Interop.Crypt32.PfxCertStoreFlags.PKCS12_ALWAYS_CNG_KSP; + + // In .NET Framework loading a PFX then adding the key to the Windows Certificate Store would + // enable a native application compiled against CAPI to find that private key and interoperate with it. + // + // For .NET Core this behavior is being retained. + + return pfxCertStoreFlags; + } + + private static bool ShouldDeleteKeyContainer(X509KeyStorageFlags keyStorageFlags) + { + // If PersistKeySet is set we don't delete the key, so that it persists. + // If EphemeralKeySet is set we don't delete the key, because there's no file, so it's a wasteful call. + const X509KeyStorageFlags DeleteUnless = + X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.EphemeralKeySet; + + bool deleteKeyContainer = ((keyStorageFlags & DeleteUnless) == 0); + return deleteKeyContainer; + } + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs new file mode 100644 index 00000000000000..097f2d49dfb8f7 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs @@ -0,0 +1,123 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Formats.Asn1; +using System.IO; +using Microsoft.Win32.SafeHandles; + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + private static partial ICertificatePal LoadCertificatePal(ReadOnlySpan data) + { + if (data.IsEmpty) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + if (X509Certificate2.GetCertContentType(data) != X509ContentType.Cert) + { + + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return LoadX509(data); + } + + private static partial ICertificatePal LoadCertificatePalFromFile(string path) + { + ArgumentException.ThrowIfNullOrEmpty(path); + + using (FileStream stream = File.OpenRead(path)) + { + int length = (int)long.Min(int.MaxValue, stream.Length); + byte[] buf = CryptoPool.Rent(length); + + try + { + stream.ReadAtLeast(buf, length); + return LoadCertificatePal(buf.AsSpan(0, length)); + } + finally + { + CryptoPool.Return(buf, length); + } + } + } + + static partial void ValidatePlatformKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) + { + // Unlike macOS, iOS does support EphemeralKeySet. + + if ((keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable) + { + throw new PlatformNotSupportedException(SR.Cryptography_X509_PKCS12_ExportableNotSupported); + } + + if ((keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet) + { + throw new PlatformNotSupportedException(SR.Cryptography_X509_PKCS12_PersistKeySetNotSupported); + } + } + + private static partial Pkcs12Return FromCertAndKey(CertAndKey certAndKey, ImportState importState) + { + AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert!; + + if (certAndKey.Key is not null) + { + AppleCertificatePal newPal = AppleCertificatePal.ImportPkcs12(pal, certAndKey.Key); + pal.Dispose(); + pal = newPal; + } + + return new Pkcs12Return(pal); + } + + private static partial AsymmetricAlgorithm? CreateKey(string algorithm) + { + return algorithm switch + { + Oids.Rsa or Oids.RsaPss => new RSAImplementation.RSASecurityTransforms(), + Oids.EcPublicKey or Oids.EcDiffieHellman => new ECDsaImplementation.ECDsaSecurityTransforms(), + // There's no DSA support on iOS/tvOS. + _ => null, + }; + } + + private static partial ICertificatePalCore LoadX509Der(ReadOnlyMemory data) + { + ReadOnlySpan span = data.Span; + + AsnValueReader reader = new AsnValueReader(span, AsnEncodingRules.DER); + reader.ReadSequence(); + reader.ThrowIfNotEmpty(); + + return LoadX509(span); + } + + private static AppleCertificatePal LoadX509(ReadOnlySpan data) + { + SafeSecIdentityHandle identityHandle; + SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( + data, + X509ContentType.Cert, + SafePasswordHandle.InvalidHandle, + out identityHandle); + + if (identityHandle.IsInvalid) + { + identityHandle.Dispose(); + return new AppleCertificatePal(certHandle); + } + + Debug.Fail("Non-PKCS12 import produced an identity handle"); + + identityHandle.Dispose(); + certHandle.Dispose(); + throw new CryptographicException(); + } + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.macOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.macOS.cs new file mode 100644 index 00000000000000..4928c4f27025d7 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.macOS.cs @@ -0,0 +1,230 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Formats.Asn1; +using System.IO; +using System.IO.MemoryMappedFiles; +using System.Security.Cryptography.Apple; +using Microsoft.Win32.SafeHandles; + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + private static partial ICertificatePal LoadCertificatePal(ReadOnlySpan data) + { + if (data.IsEmpty) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + if (X509Certificate2.GetCertContentType(data) != X509ContentType.Cert) + { + + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + return LoadX509(data); + } + + private static partial ICertificatePal LoadCertificatePalFromFile(string path) + { + ArgumentException.ThrowIfNullOrEmpty(path); + + using (FileStream stream = File.OpenRead(path)) + { + int length = (int)long.Min(int.MaxValue, stream.Length); + byte[]? rented = null; + MemoryManager? manager = null; + + try + { + ReadOnlySpan span; + + if (length > MemoryMappedFileCutoff) + { + manager = MemoryMappedFileMemoryManager.CreateFromFileClamped(stream); + span = manager.Memory.Span; + } + else + { + rented = CryptoPool.Rent(length); + stream.ReadAtLeast(rented, length); + span = rented.AsSpan(0, length); + } + + return LoadCertificatePal(span); + } + finally + { + (manager as IDisposable)?.Dispose(); + + if (rented is not null) + { + CryptoPool.Return(rented, length); + } + } + } + } + + static partial void ValidatePlatformKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) + { + if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet) + { + throw new PlatformNotSupportedException(SR.Cryptography_X509_NoEphemeralPfx); + } + } + + static partial void InitializeImportState(ref ImportState importState, X509KeyStorageFlags keyStorageFlags) + { + bool exportable = (keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable; + + bool persist = + (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet; + + SafeKeychainHandle keychain = persist + ? Interop.AppleCrypto.SecKeychainCopyDefault() + : Interop.AppleCrypto.CreateTemporaryKeychain(); + + importState.Exportable = exportable; + importState.Persisted = persist; + importState.Keychain = keychain; + } + + private static partial Pkcs12Return FromCertAndKey(CertAndKey certAndKey, ImportState importState) + { + AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert!; + SafeSecKeyRefHandle? key = null; + + if (certAndKey.Key is not null) + { + key = GetPrivateKey(certAndKey.Key); + certAndKey.Key.Dispose(); + } + + if (key is not null || importState.Persisted) + { + if (key is not null && !importState.Exportable) + { + AppleCertificatePal newPal = AppleCertificatePal.ImportPkcs12NonExportable( + pal, + key, + SafePasswordHandle.InvalidHandle, + importState.Keychain); + + pal.Dispose(); + pal = newPal; + } + else + { + AppleCertificatePal? identity = pal.MoveToKeychain(importState.Keychain, key); + + if (identity is not null) + { + pal.Dispose(); + pal = identity; + } + } + } + + return new Pkcs12Return(pal); + } + + private static partial AsymmetricAlgorithm? CreateKey(string algorithm) + { + return algorithm switch + { + Oids.Rsa or Oids.RsaPss => new RSAImplementation.RSASecurityTransforms(), + Oids.EcPublicKey or Oids.EcDiffieHellman => new ECDsaImplementation.ECDsaSecurityTransforms(), + Oids.Dsa => new DSAImplementation.DSASecurityTransforms(), + _ => null, + }; + } + + internal static SafeSecKeyRefHandle? GetPrivateKey(AsymmetricAlgorithm? key) + { + if (key == null) + { + return null; + } + + if (key is RSAImplementation.RSASecurityTransforms rsa) + { + // Convert data key to legacy CSSM key that can be imported into keychain + byte[] rsaPrivateKey = rsa.ExportRSAPrivateKey(); + using (PinAndClear.Track(rsaPrivateKey)) + { + return Interop.AppleCrypto.ImportEphemeralKey(rsaPrivateKey, true); + } + } + + if (key is DSAImplementation.DSASecurityTransforms dsa) + { + // DSA always uses legacy CSSM keys do no need to convert + return dsa.GetKeys().PrivateKey; + } + + if (key is ECDsaImplementation.ECDsaSecurityTransforms ecdsa) + { + // Convert data key to legacy CSSM key that can be imported into keychain + byte[] ecdsaPrivateKey = ecdsa.ExportECPrivateKey(); + using (PinAndClear.Track(ecdsaPrivateKey)) + { + return Interop.AppleCrypto.ImportEphemeralKey(ecdsaPrivateKey, true); + } + } + + Debug.Fail("Invalid key implementation"); + return null; + } + + private static partial ICertificatePalCore LoadX509Der(ReadOnlyMemory data) + { + ReadOnlySpan span = data.Span; + + AsnValueReader reader = new AsnValueReader(span, AsnEncodingRules.DER); + reader.ReadSequence(); + reader.ThrowIfNotEmpty(); + + return LoadX509(span); + } + + private static AppleCertificatePal LoadX509(ReadOnlySpan data) + { + SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( + data, + X509ContentType.Cert, + SafePasswordHandle.InvalidHandle, + SafeTemporaryKeychainHandle.InvalidHandle, + exportable: true, + out SafeSecIdentityHandle identityHandle); + + if (identityHandle.IsInvalid) + { + identityHandle.Dispose(); + return new AppleCertificatePal(certHandle); + } + + Debug.Fail("Non-PKCS12 import produced an identity handle"); + + identityHandle.Dispose(); + certHandle.Dispose(); + throw new CryptographicException(); + } + + private partial struct ImportState + { + internal bool Exportable; + internal bool Persisted; + internal SafeKeychainHandle Keychain; + + partial void DisposeCore() + { + Keychain?.Dispose(); + this = default; + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netcore.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netcore.cs new file mode 100644 index 00000000000000..9eaed37784a652 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.netcore.cs @@ -0,0 +1,146 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; + +namespace System.Security.Cryptography.X509Certificates +{ + public static partial class X509CertificateLoader + { + public static partial X509Certificate2 LoadCertificate(byte[] data) + { + ThrowIfNull(data); + + return LoadCertificate(new ReadOnlySpan(data)); + } + + public static partial X509Certificate2 LoadCertificate(ReadOnlySpan data) + { + if (data.IsEmpty) + { + ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE); + } + + ICertificatePal pal = LoadCertificatePal(data); + Debug.Assert(pal is not null); + return new X509Certificate2(pal); + } + + public static partial X509Certificate2 LoadCertificateFromFile(string path) + { + ArgumentException.ThrowIfNullOrEmpty(path); + + ICertificatePal pal = LoadCertificatePalFromFile(path); + Debug.Assert(pal is not null); + return new X509Certificate2(pal); + } + + private static partial ICertificatePal LoadCertificatePal(ReadOnlySpan data); + private static partial ICertificatePal LoadCertificatePalFromFile(string path); + + internal static ICertificatePal LoadPkcs12Pal( + ReadOnlySpan data, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + Debug.Assert(loaderLimits is not null); + + unsafe + { + fixed (byte* pinned = data) + { + using (PointerMemoryManager manager = new(pinned, data.Length)) + { + return LoadPkcs12( + manager.Memory, + password, + keyStorageFlags, + loaderLimits).GetPal(); + } + } + } + } + + internal static ICertificatePal LoadPkcs12PalFromFile( + string path, + ReadOnlySpan password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + Debug.Assert(loaderLimits is not null); + + ThrowIfNullOrEmpty(path); + + return LoadFromFile( + path, + password, + keyStorageFlags, + loaderLimits, + LoadPkcs12).GetPal(); + } + + private const X509KeyStorageFlags KeyStorageFlagsAll = + X509KeyStorageFlags.UserKeySet | + X509KeyStorageFlags.MachineKeySet | + X509KeyStorageFlags.Exportable | + X509KeyStorageFlags.UserProtected | + X509KeyStorageFlags.PersistKeySet | + X509KeyStorageFlags.EphemeralKeySet; + + internal static void ValidateKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) + { + ValidateKeyStorageFlagsCore(keyStorageFlags); + } + + static partial void ValidateKeyStorageFlagsCore(X509KeyStorageFlags keyStorageFlags) + { + if ((keyStorageFlags & ~KeyStorageFlagsAll) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(keyStorageFlags)); + } + + const X509KeyStorageFlags EphemeralPersist = + X509KeyStorageFlags.EphemeralKeySet | X509KeyStorageFlags.PersistKeySet; + + X509KeyStorageFlags persistenceFlags = keyStorageFlags & EphemeralPersist; + + if (persistenceFlags == EphemeralPersist) + { + throw new ArgumentException( + SR.Format(SR.Cryptography_X509_InvalidFlagCombination, persistenceFlags), + nameof(keyStorageFlags)); + } + + ValidatePlatformKeyStorageFlags(keyStorageFlags); + } + + static partial void ValidatePlatformKeyStorageFlags(X509KeyStorageFlags keyStorageFlags); + + private readonly partial struct Pkcs12Return + { + private readonly ICertificatePal? _pal; + + internal Pkcs12Return(ICertificatePal pal) + { + _pal = pal; + } + + internal ICertificatePal GetPal() + { + Debug.Assert(_pal is not null); + return _pal; + } + + internal partial bool HasValue() => _pal is not null; + + internal partial X509Certificate2 ToCertificate() + { + Debug.Assert(_pal is not null); + + return new X509Certificate2(_pal); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Pal.Android.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Pal.Android.cs index 0b82be4079ff7e..4f8dd772449e8c 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Pal.Android.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Pal.Android.cs @@ -98,7 +98,7 @@ public X509ContentType GetCertContentType(ReadOnlySpan rawData) return contentType; } - if (AndroidPkcs12Reader.IsPkcs12(rawData)) + if (X509CertificateLoader.IsPkcs12(rawData)) { return X509ContentType.Pkcs12; } diff --git a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj index 09ffc37aca351a..fabc02833ca4e4 100644 --- a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj +++ b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj @@ -19,6 +19,8 @@ + + + + @@ -389,6 +396,8 @@ + diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs new file mode 100644 index 00000000000000..c4c9bc2550c834 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using Xunit; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + public class CertLoaderTests + { + public class NewLoaderFromArray : CommonTests + { + protected override X509Certificate2 LoadCertificate(byte[] data) => + X509CertificateLoader.LoadCertificate(data); + } + + public class NewLoaderFromSpan : CommonTests + { + protected override X509Certificate2 LoadCertificate(byte[] data) => + X509CertificateLoader.LoadCertificate(new ReadOnlySpan(data)); + } + + public class NewLoaderFromFile : CommonTests + { + protected override X509Certificate2 LoadCertificate(byte[] data) + { + string path = Path.GetTempFileName(); + + try + { + File.WriteAllBytes(path, data); + return X509CertificateLoader.LoadCertificateFromFile(path); + } + finally + { + File.Delete(path); + } + } + } + + public class LegacyLoaderFromArray : CommonTests + { + protected override X509Certificate2 LoadCertificate(byte[] data) => + new X509Certificate2(data); + } + + [ActiveIssue("NestedCertificates tests fail", TestPlatforms.Linux)] + public class LegacyLoaderFromFile : CommonTests + { + protected override X509Certificate2 LoadCertificate(byte[] data) + { + string path = Path.GetTempFileName(); + + try + { + File.WriteAllBytes(path, data); + return new X509Certificate2(path); + } + finally + { + File.Delete(path); + } + } + } + + public abstract class CommonTests + { + protected abstract X509Certificate2 LoadCertificate(byte[] data); + + [Fact] + public void LoadWrappingCertificate_DER() + { + using (X509Certificate2 cert = LoadCertificate(TestData.NestedCertificates)) + { + AssertExtensions.SequenceEqual(TestData.NestedCertificates, cert.RawDataMemory.Span); + } + } + + [Fact] + public void LoadWrappingCertificate_PEM() + { + byte[] data = System.Text.Encoding.ASCII.GetBytes( + PemEncoding.Write("CERTIFICATE", TestData.NestedCertificates)); + + using (X509Certificate2 cert = LoadCertificate(data)) + { + AssertExtensions.SequenceEqual(TestData.NestedCertificates, cert.RawDataMemory.Span); + } + } + + [Fact] + public void LoadWrappingCertificate_DER_Trailing() + { + byte[] source = TestData.NestedCertificates; + byte[] data = new byte[source.Length + 5]; + Array.Copy(source, 0, data, 0, source.Length); + RandomNumberGenerator.Fill(data.AsSpan(source.Length)); + + using (X509Certificate2 cert = LoadCertificate(data)) + { + AssertExtensions.SequenceEqual(TestData.NestedCertificates, cert.RawDataMemory.Span); + } + } + + [Fact] + [ActiveIssue("Fails as NewFile, NewSpan, NewArray, LegacyArray", TestPlatforms.Linux)] + public void LoadWrappingCertificate_PEM_TrailingInner() + { + byte[] source = TestData.NestedCertificates; + byte[] data = new byte[source.Length + 5]; + Array.Copy(source, 0, data, 0, source.Length); + RandomNumberGenerator.Fill(data.AsSpan(source.Length)); + + data = System.Text.Encoding.ASCII.GetBytes( + PemEncoding.Write("CERTIFICATE", data)); + + using (X509Certificate2 cert = LoadCertificate(data)) + { + AssertExtensions.SequenceEqual(TestData.NestedCertificates, cert.RawDataMemory.Span); + } + } + + [Fact] + [ActiveIssue("Fails as NewFile, NewSpan, NewArray, LegacyArray", TestPlatforms.Linux)] + public void LoadWrappingCertificate_PEM_Surround() + { + string pem = PemEncoding.WriteString("CERTIFICATE", TestData.NestedCertificates); + + byte[] data = System.Text.Encoding.ASCII.GetBytes( + "Four score and seven years ago ...\n" + pem + "... perish from this Earth."); + + using (X509Certificate2 cert = LoadCertificate(data)) + { + AssertExtensions.SequenceEqual(TestData.NestedCertificates, cert.RawDataMemory.Span); + } + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionImportTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionImportTests.cs index a789367bac6a53..38605cdc5e2be9 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionImportTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionImportTests.cs @@ -30,6 +30,19 @@ public static void ImportEmpty_Pkcs12() } } + [Fact] + public static void ImportEmpty_Pkcs12_NL() + { + X509Certificate2Collection collection = X509CertificateLoader.LoadPkcs12Collection( + TestData.EmptyPfx, + (string?)null); + + using (ImportedCollection ic = new ImportedCollection(collection)) + { + Assert.Equal(0, collection.Count); + } + } + [Fact] public static void ImportX509DerBytes() { @@ -254,6 +267,21 @@ public static void ImportPkcs12Bytes_Single_VerifyContents_ArrayString(X509KeySt } } + [Theory] + [MemberData(nameof(StorageFlags))] + public static void ImportPkcs12Bytes_Single_VerifyContents_ArrayString_NL(X509KeyStorageFlags keyStorageFlags) + { + X509Certificate2Collection coll = X509CertificateLoader.LoadPkcs12Collection( + TestData.PfxData, + TestData.PfxDataPassword, + keyStorageFlags); + + using (ImportedCollection ic = new ImportedCollection(coll)) + { + ImportPkcs12Bytes_Single_VerifyContents(ic); + } + } + [Theory] [MemberData(nameof(StorageFlags))] public static void ImportPkcs12Bytes_Single_VerifyContents_SpanSpan(X509KeyStorageFlags keyStorageFlags) @@ -267,6 +295,24 @@ public static void ImportPkcs12Bytes_Single_VerifyContents_SpanSpan(X509KeyStora } } + [Theory] + [MemberData(nameof(StorageFlags))] + public static void ImportPkcs12Bytes_Single_VerifyContents_SpanSpan_NL(X509KeyStorageFlags keyStorageFlags) + { + ReadOnlySpan rawData = TestData.PfxData.AsSpan(); + ReadOnlySpan password = TestData.PfxDataPassword.AsSpan(); + + X509Certificate2Collection coll = X509CertificateLoader.LoadPkcs12Collection( + rawData, + password, + keyStorageFlags); + + using (ImportedCollection ic = new ImportedCollection(coll)) + { + ImportPkcs12Bytes_Single_VerifyContents(ic); + } + } + private static void ImportPkcs12Bytes_Single_VerifyContents(ImportedCollection ic) { using (var pfxCer = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, Cert.EphemeralIfPossible)) @@ -298,6 +344,22 @@ public static void ImportPkcs12File_Single(X509KeyStorageFlags keyStorageFlags) } } + [Theory] + [MemberData(nameof(StorageFlags))] + public static void ImportPkcs12File_Single_NL(X509KeyStorageFlags keyStorageFlags) + { + X509Certificate2Collection cc2 = X509CertificateLoader.LoadPkcs12CollectionFromFile( + TestFiles.PfxFile, + TestData.PfxDataPassword, + keyStorageFlags); + + using (ImportedCollection ic = new ImportedCollection(cc2)) + { + int count = cc2.Count; + Assert.Equal(1, count); + } + } + [Theory] [MemberData(nameof(StorageFlags))] public static void ImportPkcs12File_Single_SpanPassword(X509KeyStorageFlags keyStorageFlags) diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs index 76b45bf66dc352..76a9e78cd74280 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs @@ -88,6 +88,11 @@ public static void TestConstructor_DER() assert(c2); } } + + using (X509Certificate2 c3 = X509CertificateLoader.LoadCertificate(TestData.MsCertificate)) + { + assert(c3); + } } [Fact] @@ -119,6 +124,11 @@ public static void TestConstructor_PEM() assert(c2); } } + + using (X509Certificate2 c3 = X509CertificateLoader.LoadCertificate(TestData.MsCertificatePemBytes)) + { + assert(c3); + } } [Fact] @@ -198,6 +208,33 @@ public static void TestCopyConstructor_Lifetime_Independent() } } + [Fact] + public static void Loader_Lifetime_Independent() + { + var c1 = X509CertificateLoader.LoadPkcs12(TestData.PfxData, TestData.PfxDataPassword); + using (var c2 = X509CertificateLoader.LoadPkcs12(TestData.PfxData, TestData.PfxDataPassword)) + { + RSA rsa = c2.GetRSAPrivateKey(); + byte[] hash = new byte[SHA256.HashSizeInBytes]; + byte[] sig = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + Assert.Equal(TestData.PfxSha256Empty_ExpectedSig, sig); + + c1.Dispose(); + rsa.Dispose(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + // Verify other cert and previous key do not affect cert + using (rsa = c2.GetRSAPrivateKey()) + { + hash = new byte[SHA256.HashSizeInBytes]; + sig = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + Assert.Equal(TestData.PfxSha256Empty_ExpectedSig, sig); + } + } + } + [Fact] public static void TestCopyConstructor_Lifetime_Cloned() { @@ -354,7 +391,13 @@ public static void InvalidCertificateBlob() } else // Any Unix { - Assert.Equal(new CryptographicException("message").HResult, ex.HResult); + const int COR_E_SYSTEM = -2146233087; + const int CRYPT_E_BAD_DECODE = -2146885630; + + if (ex.HResult != CRYPT_E_BAD_DECODE) + { + Assert.Equal(COR_E_SYSTEM, ex.HResult); + } } } diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/LoadFromFileTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/LoadFromFileTests.cs index 222b468ff33672..e22832785fd2a8 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/LoadFromFileTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/LoadFromFileTests.cs @@ -55,6 +55,25 @@ public static void TestSerial() } } + [Fact] + public static void TestSerialFromNewLoader() + { + string expectedSerialHex = "33000000B011AF0A8BD03B9FDD0001000000B0"; + byte[] expectedSerial = "B00000000100DD9F3BD08B0AAF11B000000033".HexToByteArray(); + + using (X509Certificate2 c = X509CertificateLoader.LoadCertificateFromFile(TestFiles.MsCertificateDerFile)) + { + byte[] serial = c.GetSerialNumber(); + Assert.Equal(expectedSerial, serial); + string serialHex = c.GetSerialNumberString(); + Assert.Equal(expectedSerialHex, serialHex); + serialHex = c.SerialNumber; + Assert.Equal(expectedSerialHex, serialHex); + + Assert.Equal(expectedSerialHex, c.SerialNumberBytes.ByteArrayToHex()); + } + } + [Fact] public static void TestThumbprint() { diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs index c73af1ac3173a7..fd7bb4f65a5d54 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs @@ -59,7 +59,8 @@ private void ReadMultiPfx( string correctPassword, X509Certificate2 expectedSingleCert, X509Certificate2[] expectedOrder, - Action perCertOtherWork = null) + Action perCertOtherWork = null, + bool newOnly = false) { ReadMultiPfx( pfxBytes, @@ -67,7 +68,8 @@ private void ReadMultiPfx( expectedSingleCert, expectedOrder, s_importFlags, - perCertOtherWork); + perCertOtherWork, + newOnly); } protected abstract void ReadPfx( @@ -83,16 +85,18 @@ protected abstract void ReadMultiPfx( X509Certificate2 expectedSingleCert, X509Certificate2[] expectedOrder, X509KeyStorageFlags nonExportFlags, - Action perCertOtherWork = null); + Action perCertOtherWork = null, + bool newOnly = false); private void ReadUnreadablePfx( byte[] pfxBytes, string bestPassword, // NTE_FAIL int win32Error = -2146893792, - int altWin32Error = 0) + int altWin32Error = 0, + int secondAltWin32Error = 0) { - ReadUnreadablePfx(pfxBytes, bestPassword, s_importFlags, win32Error, altWin32Error); + ReadUnreadablePfx(pfxBytes, bestPassword, s_importFlags, win32Error, altWin32Error, secondAltWin32Error); } protected abstract void ReadEmptyPfx(byte[] pfxBytes, string correctPassword); @@ -104,7 +108,8 @@ protected abstract void ReadUnreadablePfx( X509KeyStorageFlags importFlags, // NTE_FAIL int win32Error = -2146893792, - int altWin32Error = 0); + int altWin32Error = 0, + int secondAltWin32Error = 0); [Fact] public void EmptyPfx_NoMac() @@ -226,8 +231,10 @@ public void OneCert_EncryptedEmptyPassword_OneKey_EncryptedNullPassword_NoMac(bo 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); + // The new cert loader gets ERROR_INVALID_PASSWORD since it doesn't see both a + // success and a failure in the win32 layer. + ReadUnreadablePfx(pfxBytes, null, altWin32Error: -2146885630, secondAltWin32Error: -2147024810); + ReadUnreadablePfx(pfxBytes, string.Empty, altWin32Error: -2146885630, secondAltWin32Error: -2147024810); } else { @@ -1110,6 +1117,122 @@ public void TwoCerts_TwoKeys_ManySafeContentsValues(bool invertCertOrder, bool i } } + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "The PKCS#12 Exportable flag is not supported on iOS/MacCatalyst/tvOS")] + public void TwoCerts_TwoKeys_ManySafeContentsValues_UnencryptedAuthSafes_NoMac(bool invertCertOrder, bool invertKeyOrder) + { + string pw = invertCertOrder ? "" : null; + + 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: false); + AddContents(secondCertContents, builder, pw, encrypt: false); + AddContents(irrelevant, builder, pw, encrypt: false); + AddContents(firstKeyContents, builder, pw, encrypt: false); + AddContents(firstCertContents, builder, pw, encrypt: false); + AddContents(irrelevant, builder, pw, encrypt: false); + AddContents(secondKeyContents, builder, pw, encrypt: false); + AddContents(irrelevant, builder, pw, encrypt: false); + + builder.SealWithoutIntegrity(); + byte[] pfxBytes = builder.Encode(); + + X509Certificate2[] expectedOrder = { first, second }; + + Action 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, + "", + first, + expectedOrder, + followup, + newOnly: true); + + ReadMultiPfx( + pfxBytes, + null, + first, + expectedOrder, + followup, + newOnly: true); + } + } + } + private static void CheckKeyConsistency(X509Certificate2 cert) { byte[] data = { 2, 7, 4 }; diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_Collection.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_Collection.cs index f8c6f843800f2f..78debf84db3f91 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_Collection.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_Collection.cs @@ -20,6 +20,8 @@ protected override void ReadPfx( ReadPfx(pfxBytes, correctPassword, expectedCert, null, otherWork, nonExportFlags); ReadPfx(pfxBytes, correctPassword, expectedCert, null, otherWork, exportFlags); + ReadPfxFromNewLoader(pfxBytes, correctPassword, expectedCert, null, otherWork, nonExportFlags); + ReadPfxFromNewLoader(pfxBytes, correctPassword, expectedCert, null, otherWork, exportFlags); } protected override void ReadMultiPfx( @@ -28,9 +30,29 @@ protected override void ReadMultiPfx( X509Certificate2 expectedSingleCert, X509Certificate2[] expectedOrder, X509KeyStorageFlags nonExportFlags, - Action perCertOtherWork) + Action perCertOtherWork, + bool newOnly) { - ReadPfx( + if (!newOnly) + { + ReadPfx( + pfxBytes, + correctPassword, + expectedSingleCert, + expectedOrder, + perCertOtherWork, + nonExportFlags); + + ReadPfx( + pfxBytes, + correctPassword, + expectedSingleCert, + expectedOrder, + perCertOtherWork, + nonExportFlags | X509KeyStorageFlags.Exportable); + } + + ReadPfxFromNewLoader( pfxBytes, correctPassword, expectedSingleCert, @@ -38,7 +60,7 @@ protected override void ReadMultiPfx( perCertOtherWork, nonExportFlags); - ReadPfx( + ReadPfxFromNewLoader( pfxBytes, correctPassword, expectedSingleCert, @@ -73,6 +95,36 @@ private void ReadPfx( } } + private void ReadPfxFromNewLoader( + byte[] pfxBytes, + string correctPassword, + X509Certificate2 expectedCert, + X509Certificate2[] expectedOrder, + Action otherWork, + X509KeyStorageFlags flags) + { + X509Certificate2Collection coll = X509CertificateLoader.LoadPkcs12Collection( + pfxBytes, + correctPassword, + flags); + + using (ImportedCollection imported = new ImportedCollection(coll)) + { + Assert.Equal(expectedOrder?.Length ?? 1, coll.Count); + + Span testOrder = expectedOrder == null ? + MemoryMarshal.CreateSpan(ref expectedCert, 1) : + expectedOrder.AsSpan(); + + for (int i = 0; i < testOrder.Length; i++) + { + X509Certificate2 actual = coll[i]; + AssertCertEquals(testOrder[i], actual); + otherWork?.Invoke(actual); + } + } + } + protected override void ReadEmptyPfx(byte[] pfxBytes, string correctPassword) { X509Certificate2Collection coll = new X509Certificate2Collection(); @@ -96,7 +148,8 @@ protected override void ReadUnreadablePfx( string bestPassword, X509KeyStorageFlags importFlags, int win32Error, - int altWin32Error) + int altWin32Error, + int secondAltWin32Error) { X509Certificate2Collection coll = new X509Certificate2Collection(); @@ -105,14 +158,27 @@ protected override void ReadUnreadablePfx( if (OperatingSystem.IsWindows()) { - if (altWin32Error != 0 && ex.HResult != altWin32Error) + if (altWin32Error == 0 || ex.HResult != altWin32Error) { - Assert.Equal(win32Error, ex.HResult); + if (secondAltWin32Error == 0 || ex.HResult != secondAltWin32Error) + { + Assert.Equal(win32Error, ex.HResult); + } } } - else + + ex = Assert.ThrowsAny( + () => X509CertificateLoader.LoadPkcs12Collection(pfxBytes, bestPassword, importFlags)); + + if (OperatingSystem.IsWindows()) { - Assert.NotNull(ex.InnerException); + if (altWin32Error == 0 || ex.HResult != altWin32Error) + { + if (secondAltWin32Error == 0 || ex.HResult != secondAltWin32Error) + { + Assert.Equal(win32Error, ex.HResult); + } + } } } } diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_SingleCert.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_SingleCert.cs index 9e11b356069e20..b2a480d450e609 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_SingleCert.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_SingleCert.cs @@ -19,6 +19,8 @@ protected override void ReadPfx( ReadPfx(pfxBytes, correctPassword, expectedCert, otherWork, nonExportFlags); ReadPfx(pfxBytes, correctPassword, expectedCert, otherWork, exportFlags); + ReadPfxFromLoader(pfxBytes, correctPassword, expectedCert, otherWork, nonExportFlags); + ReadPfxFromLoader(pfxBytes, correctPassword, expectedCert, otherWork, exportFlags); } protected override void ReadMultiPfx( @@ -27,12 +29,19 @@ protected override void ReadMultiPfx( X509Certificate2 expectedSingleCert, X509Certificate2[] expectedOrder, X509KeyStorageFlags nonExportFlags, - Action perCertOtherWork) + Action perCertOtherWork, + bool newOnly) { X509KeyStorageFlags exportFlags = nonExportFlags | X509KeyStorageFlags.Exportable; - ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, nonExportFlags); - ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, exportFlags); + if (!newOnly) + { + ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, nonExportFlags); + ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, exportFlags); + } + + ReadPfxFromLoader(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, nonExportFlags); + ReadPfxFromLoader(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, exportFlags); } private void ReadPfx( @@ -49,12 +58,31 @@ private void ReadPfx( } } + private void ReadPfxFromLoader( + byte[] pfxBytes, + string correctPassword, + X509Certificate2 expectedCert, + Action otherWork, + X509KeyStorageFlags flags) + { + using (X509Certificate2 cert = X509CertificateLoader.LoadPkcs12(pfxBytes, correctPassword, flags)) + { + AssertCertEquals(expectedCert, cert); + otherWork?.Invoke(cert); + } + } + protected override void ReadEmptyPfx(byte[] pfxBytes, string correctPassword) { CryptographicException ex = Assert.Throws( () => new X509Certificate2(pfxBytes, correctPassword, s_importFlags)); AssertMessageContains("no certificates", ex); + + ex = Assert.Throws( + () => X509CertificateLoader.LoadPkcs12(pfxBytes, correctPassword, s_importFlags)); + + AssertMessageContains("no certificates", ex); } protected override void ReadWrongPassword(byte[] pfxBytes, string wrongPassword) @@ -64,6 +92,12 @@ protected override void ReadWrongPassword(byte[] pfxBytes, string wrongPassword) AssertMessageContains("password", ex); Assert.Equal(ErrorInvalidPasswordHResult, ex.HResult); + + ex = Assert.ThrowsAny( + () => X509CertificateLoader.LoadPkcs12(pfxBytes, wrongPassword, s_importFlags)); + + AssertMessageContains("password", ex); + Assert.Equal(ErrorInvalidPasswordHResult, ex.HResult); } protected override void ReadUnreadablePfx( @@ -71,21 +105,35 @@ protected override void ReadUnreadablePfx( string bestPassword, X509KeyStorageFlags importFlags, int win32Error, - int altWin32Error) + int altWin32Error, + int secondAltWin32Error) { CryptographicException ex = Assert.ThrowsAny( () => new X509Certificate2(pfxBytes, bestPassword, importFlags)); if (OperatingSystem.IsWindows()) { - if (altWin32Error != 0 && ex.HResult != altWin32Error) + if (altWin32Error == 0 || ex.HResult != altWin32Error) { - Assert.Equal(win32Error, ex.HResult); + if (secondAltWin32Error == 0 || ex.HResult != secondAltWin32Error) + { + Assert.Equal(win32Error, ex.HResult); + } } } - else + + ex = Assert.ThrowsAny( + () => X509CertificateLoader.LoadPkcs12(pfxBytes, bestPassword, importFlags)); + + if (OperatingSystem.IsWindows()) { - Assert.NotNull(ex.InnerException); + if (altWin32Error == 0 || ex.HResult != altWin32Error) + { + if (secondAltWin32Error == 0 || ex.HResult != secondAltWin32Error) + { + Assert.Equal(win32Error, ex.HResult); + } + } } } diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxIterationCountTests.CustomAppContextDataLimit.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxIterationCountTests.CustomAppContextDataLimit.cs index bed126455a0399..d950b9e619b07f 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxIterationCountTests.CustomAppContextDataLimit.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxIterationCountTests.CustomAppContextDataLimit.cs @@ -128,16 +128,10 @@ public void Import_AppContextDataWithValueMinusOne_IterationCountExceedingDefaul PfxInfo pfxInfo = s_certificatesDictionary[certName]; - if (OperatingSystem.IsWindows()) - { - // Opting-out with AppContext data value -1 will still give us error because cert is beyond Windows limit. - // But we will get the CryptoThrowHelper+WindowsCryptographicException. - PfxIterationCountTests.VerifyThrowsCryptoExButDoesNotThrowPfxWithoutPassword(() => Import(pfxInfo.Blob)); - } - else - { - Assert.NotNull(Import(pfxInfo.Blob)); - } + // The total iteration count filter is disabled, but individual items are + // still limited to 600k. + CryptographicException ce = Assert.Throws(() => Import(pfxInfo.Blob)); + Assert.Contains(PfxIterationCountTests.FwlinkId, ce.Message); }, name).Dispose(); } } diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs index c70260c9dc0db7..7a6a8db3567914 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs @@ -481,37 +481,6 @@ public static void CollectionPerphemeralImport_HasKeyName() } } - [ConditionalTheory] - [MemberData(memberName: nameof(PfxIterationCountTests.GetCertsWith_IterationCountNotExceedingDefaultLimit_AndNullOrEmptyPassword_MemberData), MemberType = typeof(PfxIterationCountTests))] - public static void TestIterationCounter(string name, bool usesPbes2, byte[] blob, int iterationCount, bool usesRC2) - { - _ = iterationCount; - - MethodInfo method = typeof(X509Certificate).GetMethod("GetIterationCount", BindingFlags.Static | BindingFlags.NonPublic); - GetIterationCountDelegate target = method.CreateDelegate(); - - if (usesPbes2 && !Pkcs12PBES2Supported) - { - throw new SkipTestException(name + " uses PBES2, which is not supported on this version."); - } - - if (usesRC2 && !PlatformSupport.IsRC2Supported) - { - throw new SkipTestException(name + " uses RC2, which is not supported on this platform."); - } - - try - { - long count = (long)target(blob, out int bytesConsumed); - Assert.Equal(iterationCount, count); - Assert.Equal(blob.Length, bytesConsumed); // we currently don't have any cert with trailing data. - } - catch (Exception e) - { - throw new Exception($"There's an error on certificate {name}, see inner exception for details", e); - } - } - internal static bool IsPkcs12IterationCountAllowed(long iterationCount, long allowedIterations) { if (allowedIterations == UnlimitedIterations) diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs index 0e0295ecf7f6e7..9233360c941420 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs @@ -7,6 +7,8 @@ namespace System.Security.Cryptography.X509Certificates.Tests { internal static class TestData { + internal const string PlaceholderPw = "Placeholder"; + public static byte[] MsCertificate = ( "308204ec308203d4a003020102021333000000b011af0a8bd03b9fdd00010000" + "00b0300d06092a864886f70d01010505003079310b3009060355040613025553" + @@ -4295,5 +4297,257 @@ internal static DSAParameters GetDSA1024Params() "0D6DB9939E88B998662A27F092634BBF21F58EEAAA").HexToByteArray(); internal static readonly byte[] EmptyPkcs7 = "300B06092A864886F70D010702".HexToByteArray(); + + // This is an RSA-1024 certificate, "CN=outer" which has an extension with + // the unregistered OID 0.0.1 that is a PEM-encoded ECDSA-secp521r1 + // certificate ("CN=inner"). + internal static readonly byte[] NestedCertificates = ( + "3082041930820382A00302010202084062D86F6A371DD7300D06092A864886F7" + + "0D01010B05003010310E300C060355040313056F75746572301E170D32343031" + + "32353031333630315A170D3234303132353031343630315A3010310E300C0603" + + "55040313056F7574657230819F300D06092A864886F70D010101050003818D00" + + "30818902818100AC24F75F85C4C38E6DBF57DE889AED7598DD01202FACC00EA7" + + "4EC449DD420E7CB49992E1BCCC69B3EAB8B2AEECA5B2BCFC1295DC82B83EDB71" + + "C1764191EA0F73D1BAB26D03B95F322EF8299B1DADAECCAADEBA052BD3BC2549" + + "D83AD1C2FB2DC556370306AC3CBCE09F3669448EDEF8FCA12164D793D5B456A7" + + "418393899E00590203010001A382027A3082027630820272060200010482026A" + + "0D0A2D2D2D2D2D424547494E2043455254494649434154452D2D2D2D2D0A4D49" + + "49426D6A43422F61414441674543416767312B7233664B4775457054414B4267" + + "6771686B6A4F50515144416A41514D5134774441594456515144457756700A62" + + "6D356C636A4165467730794E4441784D6A55774D544D324D444661467730794E" + + "4441784D6A55774D5451324D4446614D424178446A414D42674E5642414D540A" + + "42576C75626D56794D4947624D42414742797147534D34394167454742537542" + + "4241416A4134474741415142513143437A4448737A724E6839445456697A6E75" + + "0A4B38516F496B5649324138494E676B4B6F6B3649704158484C533058767333" + + "4E43745152396973486C5A376C707844366C6B72376449793363396756427063" + + "630A7131734242796B73534854745A4766337A2F6E526E5A3961684463755444" + + "2F486E71302B326A33476461423557594B6733336E65337730423258385A7377" + + "65390A46774F2B335A6954734D6D647038312B6B4D6C76463775453236597743" + + "6759494B6F5A497A6A304541774944675973414D494748416B4942454B6C4654" + + "4978560A434274324839636A30595042493632457734752F665A4B4A53657272" + + "7848707244476C67546B36635075585345723569395A397731505A3872654A51" + + "7A7367690A6C736268363653584B4F6C67466945435153587972457736633565" + + "734B587A6269484F7762564E4756305A4430424A6761316533667144507A4170" + + "75527734510A75656A636749684E723577504E7A4E696A464D57697944716D79" + + "6E586D4B6E627933457050396D370A2D2D2D2D2D454E44204345525449464943" + + "4154452D2D2D2D2D0D0A300D06092A864886F70D01010B05000381810032833C" + + "38E5F66D64880C191D247CE8B819899B20F284DD59CBDE1731A4D0FE09A95A52" + + "D5A7D049CA108BEB9673FD207C842B324DFD8086C9E1CDAB931D7403730E0521" + + "69943C58ECC3DA6E11C6ED4F16455152D6FF4D104C88976F9BA88120B0889563" + + "1378357F297A6B3E444296C06C636A589973250F2A096C39C1EDE5C1C9").HexToByteArray(); + + // This is a PFX that uses mixed algorithms and iteration counts. + // PFX { + // SC[0], encrypted, PBES2: AES128CBC, SHA256, 2000 iterations { + // cert, keyid=1 + // }, + // SC[1], plaintext { + // shrouded_key, PBES2: AES192CBC, SHA384, 2001 iterations, keyid=1 + // }, + // mac: SHA384, 39 iterations. + // } + // "Placeholder" + internal static readonly byte[] MixedIterationsPfx = ( + "30820A320201033082099206092A864886F70D010701A08209830482097F3082" + + "097B308203FA06092A864886F70D010706A08203EB308203E7020100308203E0" + + "06092A864886F70D010701305F06092A864886F70D01050D3052303106092A86" + + "4886F70D01050C302404101215FB90500DC30882E7D84625F97923020207D030" + + "0C06082A864886F70D020A0500301D06096086480165030401160410558E3FAE" + + "FE92ADEC4CF1405E7DE7C5118082037095F887CE32E748EC0D647062286EA9D0" + + "C3C04213ED9431FFE0F6028105D94D088684A55C997630D16EF7CE7D49729C30" + + "1F750D8864B988ABBAF551A8819ED7D17CBB810CA3DC39EBD804B3C388C73BA9" + + "320783DF3AABEBAD9F44E4ABFF931AAA95AE874E348B49F35749614D9EE94F5E" + + "EC7A5C597FE8CB6CCDB7A0721EE5836C8F839D83E1577A753F8B2B7E0C2C53BD" + + "D365322E211A62DE061FAC3ECF770CEBCF37AC2604A1CF318C4DB1BA83C85779" + + "3BE57C898EA80D52E0F83B17F877210C1A5E819F5C35DE6DB5EBACE1BFB3101C" + + "C2FE9FF4983BAB7B93CA5D466B855D973FB5D72D8DD109B61FBA238320C886C0" + + "8FC141168C43B946AAC28C114B20A667D189E473E9779432EF64C965459AE9C4" + + "6430B94E19BCEB4733D3038D9F65B85D79B7A1DA954887BAE1DB0A999135A09A" + + "25C38616DBADB9BE50F729925F23C107F899341F76B4B2161CCAF8C499DF7646" + + "F7DB16A21EFD90097D75EEF893B8B8115F6A8816B2D9628278F8ADAFD0E4AC7F" + + "14BED13FFB6DD7FEDE1EA1990CACE772DBFDACC2A4547F910D3F2B238FE86A4F" + + "1BAEE8AC282F918D3F82B8149DD3A99B78D433AB0F5592E7B7B25FBC4A726520" + + "A49CF2FDA60D747F19031B34F911C442E20BD91600A32F9A4FCC1E3DF330171A" + + "4E0721CD01A93CC20C2B770990029C30306945AFC7E3ABD9516D8C6F359D904B" + + "5F32F3BAEC8770A8BE15C89E4B836B1F20D29A7D3D3BB7491EFC9B88A161B598" + + "02819D2EC695000C2F83B1A363217CC2B0A70A4A226993BD32BE302DCD157EEE" + + "3F9DBFBDE0375A03DEA2761941F49D42A8F5029DC3B65E661B61DFBE5F2527D6" + + "C59927B5A0857FE7B9836A63D202895C1FA6FDE40E70107659CE3E4C7DDDBC9E" + + "04B85A46104EFDF6692BF898C565480F5A5163231167DB69F873D1341CDF8DF0" + + "7BD4473F58162A3EB653C6567C2E3120B0034B9E35D22838BBF02968651FC1F8" + + "873C405237B7F29297ED47CADEABF248386CE94D215C8D5731A503623C9CA916" + + "C5896B8F713A044ABBECA764D36D074DF523D989228B89589463DD3615A7E844" + + "E7E9E91BE0BFAD79218CD7B52A313458BBA8FC660B5DF8513464FE749EF3C201" + + "5C15B2EF15F63D5FDA726ED8E04B3B5E7DB8610367AB08EDF120DDEEA7382AC5" + + "1C778B8069E09F2EB68D19154DD10129A1E0DA0717AE1E108B2496C69904D851" + + "18B37E06FA95E1F5E75371D8C7EC3BC80C817F931744428BF03C1F51A0164C0A" + + "3082057906092A864886F70D010701A082056A04820566308205623082055E06" + + "0B2A864886F70D010C0A0102A082053930820535305F06092A864886F70D0105" + + "0D3052303106092A864886F70D01050C30240410B5D9DF064271CEFFDBC3C067" + + "F6DFEB74020207D1300C06082A864886F70D02090500301D0609608648016503" + + "0401020410DEC794EDB555B89CAFB3671FAB4A52E1048204D050B2A2E9E3D3C0" + + "84D5BFCAB1C7C27951C0B72FB45B52089605DBF5E60550521DEAB0A66EF2C182" + + "3AE2F4EC21A1B8BF65D240DF20759C66AB0C6311CABB30F9DE1290B81CF69AAE" + + "7B6F42A4E9BE7BB6F09058C7D0D6FBE6F24EC2E3457680907F000C5B457B1E7A" + + "E394A7A70E6289C3B009F522399D5CC41014F12A336926F3AB8E2A3AF5496BC3" + + "27B073206F20F131AFCB627A1E67B9B457C44DB6A6C10EEBB8856E0BAAF99D8A" + + "9D24D7C90AC5EA9EB14C66315E77F158948988BB729D8E0796741CAE29894DC9" + + "6614C2B06911013C168C7A4E6C46F09D1AB0D7933729FC88BA47A41BFAB0AB53" + + "C60FEAF6A93688E67039184B598BDF1CC95C3967F9ECC649745E265974713102" + + "E271ECFA6A067F10751C2A4A70A94FA39F37E944996C843809D82990A9D44C18" + + "9EE7B9DC8534F9B821A5C56A61DFAAF470444883200B3FFFAA23DCE84375937D" + + "D0149869B2683773F650B70003EB025A04E170DFD97D4F86D2913E91F757BC01" + + "5F1F85F497B1400052E0868ED6844E2390F71B036B524E824B3ED6381BCE2600" + + "71A5F6EB12E9C1C44BBE0664217C4A44EA07E440A33010A0CA46647E7FFA4F58" + + "74F1EFB9DD330DA2CD50AC01C489639520A1A27B603F44831EF235F0540817F2" + + "196AF45D3B9957D272B5549BEC5507F6BA443A37F54FD6D6AEBEBBB7AAE15F7F" + + "8C1C46D68CEEC2E95426D177A390E7C50B0122BDDA9EFC104294840F6D5374AD" + + "90952842C0EDF796AF48C6FDD4AA02C81AFCD606CD0E94F3DF2B06B649A47D9D" + + "7C8AE23E1A776E1D149CA25E4929238778B19E33DE05DE577B763305B4884B8A" + + "7DE31BB9712477C9CDC138B52FB2D59CFE42A1CDAEB0E205C70B38CC4B88E0A6" + + "ABCC41D29D11947FF5B03633A5E164ED22F10536AAD07DB2EB52A2C696EBB135" + + "B5220839D1E5A7DB6C2B8DEB09C883129BD5253F68169F9D5F5A2F3AB17C4921" + + "10636978B8573A5B4E1FFF5A4C3E75E2F03AD71AF874C544474E1B41969416D6" + + "D8FFFD595428EC7928BD17C652F67D9B6B150C6D4A9352C405BED162492A5FD5" + + "7EB6BA5F77AAC2BD7E4EEB6B0BD2E4329E2CA8A425F88F4743B25F259E292C04" + + "483472CA79FF52271A830AA6A27A52C3531E2B2503592C017A7CC00F91F63F73" + + "4E3E56746475B8B338440B7D5FAC87A90831EE78A2DE4FD6F60F1C66B31A520C" + + "44B73B09D5419451C4A32E8E1A5BB17E44B9FAABFB07021747093DBFE248BACB" + + "E2BB6C7F145DE7397A2B2AEAA083EE57F46C8DF85FA2DFF4582C2E3CE3CB2E91" + + "706395A63BA96343B0567E41A33FC964BC8C03CEDE5E3E1D7A8B285F745EEAF5" + + "CD1382C86DA82922DA1772158F8BCCFF70DA87A64602033560566F33A3793A4E" + + "5C404D2C69274EEE9E82B5B97B0760FD66067888711C572E84DB382491792CB2" + + "C7BFA472E6B0D70D529701C2A0B730F5E1E0A980EAD56EA323E0008C70D62F53" + + "5B9E0533F9A4B7CFA22319274E68C8B4737E5DAB5B1956C235E7EA548E24E23F" + + "F9FCA61D11DAFC6B90E0BE8E96A66B6973D5F025C0619D283CC92C3224000FEE" + + "F9BD002E7EFAD4C737C4CFEB42858DBDFBC489B1131AABDD1868C58EDCAD35C2" + + "AE1A42BBCF0A2A90E0557A7A5F79F2D92D19E39D505994163CC94F5EA56009F0" + + "5E9ABCAF24807130F90FCA606D5448C103489BB53090EC603394705B472132E4" + + "FAD50491069F44040A0D66F7D3D5C86593D61C9D37CDE3BBE5651EA2E104B324" + + "3272E665CDBB139E063112301006092A864886F70D0109153103040101308196" + + "304F300B060960864801650304020304405EDC86442FD573401BD2DF0F95356A" + + "1C1454F401231B7F772179626ABCB220C8096AC0ED6C27CACED7D94615768B61" + + "8BDDCF4B8A0996E019BD418423F79F173404406B32D0D889B85234D716C87F3D" + + "EEBD62B5DC14984FDB9EA9FC765B340F54D3E5203C6A9F4F23913B22605A32BD" + + "3D8D120CFFFE4ACB83BA4D488C67271E38CD40020127").HexToByteArray(); + + // This is a PFX that mixes encrypted and unencrypted certificates. + // PFX { + // SC[0], plaintext { + // cert, keyid=2 + // }, + // SC[1], encrypted, PBES2: AES128CBC, SHA384, 2000 iterations { + // cert, keyid=1 + // }, + // SC[2], plaintext { + // shrouded_key, PBES2: AES128CBC, SHA256, 2001 iterations, keyid=1, + // shrouded_key, PBES2: AES128CBC, SHA256, 27 iterations, keyid=2 + // }, + // mac: SHA384, 39 iterations. + // } + // "Placeholder" + internal static readonly byte[] TwoCertsPfx_OneEncrypted = ( + "30820CAB02010330820C0B06092A864886F70D010701A0820BFC04820BF83082" + + "0BF43082029406092A864886F70D010701A0820285048202813082027D308202" + + "79060B2A864886F70D010C0A0103A082025430820250060A2A864886F70D0109" + + "1601A08202400482023C30820238308201A1A003020102020900BD698EB46606" + + "F1E4300D06092A864886F70D01010B0500305E311E301C060355040A13154D69" + + "63726F736F667420436F72706F726174696F6E31173015060355040B130E2E4E" + + "4554204C6962726172696573312330210603550403131A506C61696E74657874" + + "2054657374204365727469666963617465301E170D3234303531303138303033" + + "375A170D3234303531303138313033375A305E311E301C060355040A13154D69" + + "63726F736F667420436F72706F726174696F6E31173015060355040B130E2E4E" + + "4554204C6962726172696573312330210603550403131A506C61696E74657874" + + "205465737420436572746966696361746530819F300D06092A864886F70D0101" + + "01050003818D0030818902818100A6638EFEFA9A1B364574D9A7BA4A85017E73" + + "61831606B717DCF8FC0F5B982583D5460BCD216E99FBC15ABF62B5C30FDC7CF7" + + "D13CDCF9E0A0A25C26AF0AC14D116569FAA6496CB87ECB0A3B87AAF624010630" + + "4E7F0DDB63FF7EC95396F2CF4E57A1F0356414C7D1032433831C327AC33DD264" + + "FC7B1BA567FBE360AF35446B63110203010001300D06092A864886F70D01010B" + + "0500038181001E2C2AC3FC484C42FBFC3A0D027E438BDBF6FDD4609BF1E11BA9" + + "CDCF50D121BAEDFA5361619B469D48F1CBA203F361A2E7D662A88239A500D056" + + "CB38D215197A3F5FFACA5AADDAC875D380C4435C0E8633243174623BF59836F0" + + "8DBA917C5B4A2270579574566FFA29A7A5FF0415D34E8CCFFB9EAD4BA8EC90BF" + + "B3A2F7041F6B3112301006092A864886F70D01091531030401023082031A0609" + + "2A864886F70D010706A082030B308203070201003082030006092A864886F70D" + + "010701305F06092A864886F70D01050D3052303106092A864886F70D01050C30" + + "24041078790CAC0DA9B3E9B0EE236CA8073CF3020207D0300C06082A864886F7" + + "0D020A0500301D06096086480165030401160410256DE9ED9807FD7D30597DB5" + + "51A5815080820290D8156A7DDCD344FE96B8D8BBA4303A373108D725BFC30071" + + "DBF716A7C1FD1B598BAE1FBC9A77CCBB7319646E83B747B59330760B6EBC29B0" + + "91E591FF0030ECB28626BA1594FEF6F0A01B60F2548AE1577FD05E7CAC5BCFA6" + + "2422E71F551FC2A8ACE488E871C03E1E02A9DB4ADA9DB335466EE1A6E7AE6B9C" + + "AE118AF4008879690C446F0D4A03740E31E4879B163E58FFA46394DF57CD98AE" + + "FA3C44E94BDD36D31A53EB2C9A74EA9F45718370106F205A81837A05952E7C17" + + "3460A2400195D658671BE62E76AAD565D848109BE41B6F06CA87A7B7676B9A5A" + + "525B6ADDE80A68A87FE82C8547D611F6DEC5C2AD617D354216CC7E724DA2F7A7" + + "BC67C336A68F7A8ED4F555E53330DDA1318332E170458D10E7A1CA60D87F61C5" + + "89B8BC4CDCAC26BA341B96BD20096B89977750B1E2BACDEE23130825B827F5CA" + + "E73373AD2258201B4DED998F003E1084BE969F5B1EE33B8443BE01C509927876" + + "1D69A1E48662EE91DF5211176DA5495AD54CB50EB2A2A3E077E93946958CAFE9" + + "5F2FB3450B6269F3541BB21B6F129288115E177594D6EC0DD516B8882100C9CA" + + "DB9874064B3A1F051FBB0B43257F0644A05C6D416C0652696E89DB6E43CE5E92" + + "94D05711CDBF7B304E076D73FEDA97A7E2DFF398761DE6425730CB576D26F49A" + + "2CCD93BDD0E410F70D4EF8DB6BCF221BD3E53C472CEA9C40E09A284923992BD2" + + "12B32E36BBB06EC3000261E8EC9D9E389F663DBAAFEBE0CF671E771A246D033E" + + "AFFDF400B94D8926E23FD660E0A8304C01D490EF54F868506EE8AA255F95E00A" + + "7E1AA1D18998B9AE8E0AA0472CD152A674536F5D2882502247D2B056137BFF97" + + "08CD8EC61A3339CCB1DF43DC60A0A75F9260905BB3D339580EA5B5B7BAA92374" + + "4A8850389A2E68B98FE6EF78D78207ACC858A97DEC55C0D03082063A06092A86" + + "4886F70D010701A082062B04820627308206233082030E060B2A864886F70D01" + + "0C0A0102A08202E9308202E5305F06092A864886F70D01050D3052303106092A" + + "864886F70D01050C3024041077CF0CA72DCB1B541E6F481D3A51470A020207D1" + + "300C06082A864886F70D02090500301D06096086480165030401020410503141" + + "7A0A42803706E84B8C7470CD2D048202807E3108B51EA4D80D2E7CF358E08C6E" + + "A30087AE0E9F80CF1B022A0DE0BBDA715733141C6877276864DE98D439D61650" + + "63ED517FBF206450AE6B29835F89914003B8D77F0F15D6FB073EF27F90E9D851" + + "ABE569D3DE6968E8F61EA184B484B4284C2FD3803D9AA4DE083F18A64B2F9320" + + "156004E49C4A179CF8800799451563200CD998CBA779032302CEA4227A78CD10" + + "8FC8BFA6C114F005A2937948F14DDDF05AEB543F4D2FB3C56FA7C1F14B7ACA57" + + "0CA8554A6EA4AC1C0FD356E3D1DD568F97D6EDBFD3B09C87BC2013A88C442993" + + "4C73F442A5D5DA3563FCB85EBEC41085843D14EE88E582151E6855741106CA1D" + + "D775A5793E8A0B4E036427E6D2FC9F0AA394085997E42875E4EF5742099141CD" + + "70B4AA887C40E3EDDB407C39F3C474F95D394D98DAA68BD1005F1223B6E58B52" + + "AA552FD25652AA871F03A53A58922953DB120BAFB44C29DCF9C9C2F96DEE3BEE" + + "7E0F7FF8F04C98ED631F6A845B8B89F30EFB956846BA87E9B3F6FCCB49FE0F21" + + "10139E855389C8EC5120983A09D5F4D655E64058ADD1E8C44F3B6A4936E6788B" + + "E939061221424D71666765D99FE6D883C9E8079992D585F73B0CD57947D38A54" + + "FE7CBE93BE117675A3E52F708B2A6BA6C390BD3BE01100243E7B7B5CB8A9F501" + + "CCCCD082F9867B4DA0A9FF3DC5BB0F0974D4EDEF9FF89F6EB355BCE07B00A32D" + + "AEE443D46EF19A96E6EB3EA4C0B141606A1FDB200D1BADBC11954951383E8935" + + "4603C10E241353FED1502D8111E42DA45198D1586CB0938947F3856CCC855F84" + + "231F131D636DFBB46394A3EBEDAC9A4DAD0DCB32163C57A9B5DD21BC56CA969A" + + "F574E38787DFA6EC2BA063E60375585ED5B9E8D5B6D1BC162D63036C52FF4473" + + "E0A4FD96AA055ED4014C3B0E4F7A098F553112301006092A864886F70D010915" + + "31030401013082030D060B2A864886F70D010C0A0102A08202E8308202E4305E" + + "06092A864886F70D01050D3051303006092A864886F70D01050C30230410DE7A" + + "16E6BCE889C8A5F823E7670FB9E302011B300C06082A864886F70D0209050030" + + "1D06096086480165030401020410683B6EE3E1287242ED73707ECE1F271F0482" + + "028018435588B8BEA17EB47E7947D4FE82D66F7C26850B840C0F06964DC99C56" + + "8391707178494F177EFB65E17D20D3C39E80A00342783B33164F371902D76658" + + "78A79802FE7D9EB31D21137B56692D1B7E2B38F54B00CEE506867968420CB4FA" + + "9EE135BA960C0D40EC22714F5D8576BAF03269D19384C246A60ED4B9F0F4268E" + + "2E135F7BE41536D844718B7339EE14A1CE261B8A27C2AA2AAA90C98F9437372B" + + "0448579A0C392B347C7E95A1A7617A47218F63D0C52C88A3F93AC47BB9493E2C" + + "67DE061F28E90FED463E392C6743C8E6F60524A2455C5E46218E357AFB31553D" + + "0AAD18A2784719D281B12560CB904527D912BA2B40790D9068661C01AA4B03E0" + + "B5316D6FEBBC38087B4DD46CF2CC0C98B6F488AA9940C6FA16BC8BB1853CD0B0" + + "E41D85F382F5BE36E5A821C54EBB8C34DD7CD7B970315FF65B657AED6CE2598A" + + "6DF4E96370815660B3711FDE0A2CA0AB5C2B234C95B699922CFC4E95B781F783" + + "F20373E8E23EB6D518C797A21B401A797DDAF8A823602ED38135F3FF3C555C7E" + + "7FA7B800F4535B781B4F6B21BF2147D8C20388CDD48DB3C17413DC4FBD347368" + + "809762C4851C28CAD52B4F916CAF53E2D5E05ACAC3E85BA1F63525F57F456059" + + "56B7830AFBC50D0CE517C282AE5DB5638635155073C567A82A7ACDEE6860D18A" + + "1CAACE341845A41470F47E7A440F21A3DA96CF986181E1F044B28FB3EEFA0E41" + + "82B93AA2FD50A23B045DABCD774519879CB824B9A58A1859ED553578508B5CF7" + + "85BE3D2A67F0FC508B94A738162455CD11A4BF0C3736914D8C5A99F9D5ACA959" + + "B1C4D5872BA852DD3F8B2B8C231017AEF373B3B5E7B65B6DF7115DB99D5EB11F" + + "E80E8CB3E1D3CE6F50DD7B255F103DC290B7287D6AD7AB12D5242E13092DB9E1" + + "ADE03112301006092A864886F70D0109153103040102308196304F300B060960" + + "8648016503040203044044A762FCCD979F096F1C0DE0C4C584E42C1848DB1138" + + "389DAC33048D6B81DBBCA7E031E8620B37C14F85257DA4A6F57FE58E939493C5" + + "928343622B80009D8C5B0440E74D33A5F7DA48801A4EF4FD65D0F442F26C845F" + + "A418D3E0D78FD0285A92A74B433661F516C6955EC40FE46DAB813B6AE940C2DE" + + "FE8F8F5E32E6B491C999D598020127").HexToByteArray(); } } From f41eae506965747b001c4004ed8eb6c11d8873ea Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Wed, 15 May 2024 12:52:35 -0700 Subject: [PATCH 02/19] Apply mechanical review feedback --- .../MemoryMappedFileMemoryManager.cs | 9 +- .../System/Security/Cryptography/Helpers.cs | 2 +- .../src/System/Security/Cryptography/Oids.cs | 2 + .../X509Certificates/Pkcs12LoaderLimits.cs | 59 ++++++--- .../X509CertificateLoader.Pkcs12.cs | 112 +++++++++--------- .../X509Certificates/X509CertificateLoader.cs | 5 +- .../X509CertificateLoaderPkcs12Tests.cs | 3 + .../X509CertificateLoaderTests.cs | 13 +- .../ref/Microsoft.Bcl.Cryptography.cs | 2 +- .../Security/Cryptography/NetStandardShims.cs | 2 - .../Cryptography/PbeParameters.netstandard.cs | 2 +- .../X509CertificateLoader.OpenSsl.cs | 2 +- .../X509CertificateLoader.iOS.cs | 1 - .../tests/X509Certificates/CertLoaderTests.cs | 22 +--- .../tests/X509Certificates/CtorTests.cs | 4 +- 15 files changed, 130 insertions(+), 110 deletions(-) diff --git a/src/libraries/Common/src/System/IO/MemoryMappedFiles/MemoryMappedFileMemoryManager.cs b/src/libraries/Common/src/System/IO/MemoryMappedFiles/MemoryMappedFileMemoryManager.cs index f30ea062aee425..10fb24e05443dd 100644 --- a/src/libraries/Common/src/System/IO/MemoryMappedFiles/MemoryMappedFileMemoryManager.cs +++ b/src/libraries/Common/src/System/IO/MemoryMappedFiles/MemoryMappedFileMemoryManager.cs @@ -70,14 +70,9 @@ protected override void Dispose(bool disposing) _length = -1; _accessor?.SafeMemoryMappedViewHandle.ReleasePointer(); _accessor?.Dispose(); - _mappedFile.Dispose(); + _mappedFile?.Dispose(); _accessor = null!; _mappedFile = null!; - - if (disposing) - { - GC.SuppressFinalize(this); - } } public override Span GetSpan() @@ -100,7 +95,7 @@ public override void Unpin() private void ThrowIfDisposed() { -#if NETCOREAPP +#if NET ObjectDisposedException.ThrowIf(_length < 0, this); #else if (_length < 0) diff --git a/src/libraries/Common/src/System/Security/Cryptography/Helpers.cs b/src/libraries/Common/src/System/Security/Cryptography/Helpers.cs index 9473047a0ce84f..341931377ddd8d 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Helpers.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Helpers.cs @@ -73,7 +73,7 @@ internal static void RngFill(byte[] destination) internal static void RngFill(Span destination) { -#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER +#if NET || NETSTANDARD2_1_OR_GREATER RandomNumberGenerator.Fill(destination); #else byte[] temp = CryptoPool.Rent(destination.Length); diff --git a/src/libraries/Common/src/System/Security/Cryptography/Oids.cs b/src/libraries/Common/src/System/Security/Cryptography/Oids.cs index b7364108222ad1..033c9c9db0d9f8 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Oids.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Oids.cs @@ -41,6 +41,7 @@ internal static partial class Oids internal const string SigningCertificate = "1.2.840.113549.1.9.16.2.12"; internal const string SigningCertificateV2 = "1.2.840.113549.1.9.16.2.47"; internal const string DocumentName = "1.3.6.1.4.1.311.88.2.1"; + internal const string Pkcs9FriendlyName = "1.2.840.113549.1.9.20"; internal const string LocalKeyId = "1.2.840.113549.1.9.21"; internal const string EnrollCertTypeExtension = "1.3.6.1.4.1.311.20.2"; internal const string UserPrincipalName = "1.3.6.1.4.1.311.20.2.3"; @@ -50,6 +51,7 @@ internal static partial class Oids internal const string OcspEndpoint = "1.3.6.1.5.5.7.48.1"; internal const string CertificateAuthorityIssuers = "1.3.6.1.5.5.7.48.2"; internal const string Pkcs9ExtensionRequest = "1.2.840.113549.1.9.14"; + internal const string MsPkcs12KeyProviderName = "1.3.6.1.4.1.311.17.1"; // Key wrap algorithms internal const string CmsRc2Wrap = "1.2.840.113549.1.9.16.3.7"; diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs index 0ac16d4f097310..fa37a42b4eb542 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace System.Security.Cryptography.X509Certificates { /// @@ -95,10 +97,12 @@ public Pkcs12LoaderLimits() /// public Pkcs12LoaderLimits(Pkcs12LoaderLimits copyFrom) { -#pragma warning disable CA1510 +#if NET + ArgumentNullException.ThrowIfNull(copyFrom); +#else if (copyFrom is null) throw new ArgumentNullException(nameof(copyFrom)); -#pragma warning restore CA1510 +#endif // Do not copy _isReadOnly. @@ -148,7 +152,7 @@ private void CheckReadOnly() /// /// Gets or sets the iteration limit for the MAC calculation. /// - /// The iteration limit for the MAC calculation. + /// The iteration limit for the MAC calculation, or for no limit. public int? MacIterationLimit { get => _macIterationLimit; @@ -166,16 +170,15 @@ public int? MacIterationLimit /// Gets or sets the iteration limit for the individual Key Derivation Function (KDF) calculations. /// /// - /// The iteration limit for the individual Key Derivation Function (KDF) calculations. + /// The iteration limit for the individual Key Derivation Function (KDF) calculations, + /// or for no limit. /// public int? IndividualKdfIterationLimit { get => _individualKdfIterationLimit; set { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - + CheckNonNegative(value); CheckReadOnly(); _individualKdfIterationLimit = value; } @@ -185,16 +188,15 @@ public int? IndividualKdfIterationLimit /// Gets or sets the total iteration limit for the Key Derivation Function (KDF) calculations. /// /// - /// The total iteration limit for the Key Derivation Function (KDF) calculations. + /// The total iteration limit for the Key Derivation Function (KDF) calculations, + /// or for no limit. /// public int? TotalKdfIterationLimit { get => _totalKdfIterationLimit; set { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - + CheckNonNegative(value); CheckReadOnly(); _totalKdfIterationLimit = value; } @@ -204,16 +206,14 @@ public int? TotalKdfIterationLimit /// Gets or sets the maximum number of keys permitted. /// /// - /// The maximum number of keys permitted. + /// The maximum number of keys permitted, or for no maximum. /// public int? MaxKeys { get => _maxKeys; set { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - + CheckNonNegative(value); CheckReadOnly(); _maxKeys = value; } @@ -223,16 +223,14 @@ public int? MaxKeys /// Gets or sets the maximum number of certificates permitted. /// /// - /// The maximum number of certificates permitted. + /// The maximum number of certificates permitted, or for no maximum. /// public int? MaxCertificates { get => _maxCertificates; set { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - + CheckNonNegative(value); CheckReadOnly(); _maxCertificates = value; } @@ -369,5 +367,28 @@ public bool IgnoreEncryptedAuthSafes _ignoreEncryptedAuthSafes = value; } } + + private static void CheckNonNegative( + int? value, + [CallerArgumentExpression(nameof(value))] string? paramName = null) + { + // Null turns to 0, 0 is non-negative, so null is non-negative. + CheckNonNegative(value.GetValueOrDefault(), paramName); + } + + private static void CheckNonNegative( + int value, + [CallerArgumentExpression(nameof(value))] string? paramName = null) + { +#if NET + ArgumentOutOfRangeException.ThrowIfNegative(value, paramName); +#else + if (value < 0) + { + throw new ArgumentOutOfRangeException(paramName, SR.ArgumentOutOfRange_NeedNonNegNum); + } +#endif + } + } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs index 8534e699d5b55c..75b953d20c19c5 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs @@ -17,7 +17,7 @@ public static partial class X509CertificateLoader private const int CRYPT_E_BAD_DECODE = unchecked((int)0x80092002); private const int ERROR_INVALID_PASSWORD = unchecked((int)0x80070056); -#if NETCOREAPP +#if NET private const int NTE_FAIL = unchecked((int)0x80090020); #endif @@ -242,8 +242,6 @@ private static void ReadCertsAndKeys( } else { - // Should there be an option here to preserve this? - // Ignore this? throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } @@ -294,7 +292,7 @@ private static void ProcessSafeContents( oid switch { Oids.LocalKeyId => true, - "1.2.840.113549.1.9.20" => limits.PreserveCertificateAlias, + Oids.Pkcs9FriendlyName => limits.PreserveCertificateAlias, _ => limits.PreserveUnknownAttributes, }); } @@ -346,8 +344,8 @@ private static void ProcessSafeContents( attrType switch { Oids.LocalKeyId => true, - "1.2.840.113549.1.9.20" => limits.PreserveKeyName, - "1.3.6.1.4.1.311.17.1" => limits.PreserveStorageProvider, + Oids.Pkcs9FriendlyName => limits.PreserveKeyName, + Oids.MsPkcs12KeyProviderName => limits.PreserveStorageProvider, _ => limits.PreserveUnknownAttributes, }); } @@ -662,7 +660,7 @@ internal ReadOnlyMemory DecryptSafeContents( _decryptBufferOffset = saveOffset; -#if NETCOREAPP +#if NET if (e.HResult != CRYPT_E_BAD_DECODE) { e.HResult = ConfirmedPassword ? NTE_FAIL : ERROR_INVALID_PASSWORD; @@ -817,7 +815,11 @@ internal void UnshroudKeys(ref ReadOnlySpan password) } } - internal ArraySegment ToPfx(ReadOnlySpan password) + internal +#if !NET + unsafe +#endif + ArraySegment ToPfx(ReadOnlySpan password) { Debug.Assert(_certBags is not null); Debug.Assert(_keyBags is not null); @@ -828,53 +830,66 @@ internal ArraySegment ToPfx(ReadOnlySpan password) }; AsnWriter writer = new AsnWriter(AsnEncodingRules.BER); - writer.PushOctetString(); - writer.PushSequence(); - for (int i = 0; i < _certCount; i++) + using (writer.PushOctetString()) + using (writer.PushSequence()) { - SafeBagAsn bag = _certBags[i]; - bag.Encode(writer); - } + for (int i = 0; i < _certCount; i++) + { + SafeBagAsn bag = _certBags[i]; + bag.Encode(writer); + } - for (int i = 0; i < _keyCount; i++) - { - SafeBagAsn bag = _keyBags[i]; - bag.Encode(writer); + for (int i = 0; i < _keyCount; i++) + { + SafeBagAsn bag = _keyBags[i]; + bag.Encode(writer); + } } - writer.PopSequence(); - writer.PopOctetString(); safeContents.Content = writer.Encode(); writer.Reset(); - writer.PushSequence(); - safeContents.Encode(writer); - writer.PopSequence(); + using (writer.PushSequence()) + { + safeContents.Encode(writer); + } + byte[] authSafe = writer.Encode(); writer.Reset(); + const int Sha1MacSize = 20; HashAlgorithmName hashAlgorithm = HashAlgorithmName.SHA1; - byte[] macKey = new byte[20]; - Span salt = stackalloc byte[macKey.Length]; - + Span salt = stackalloc byte[Sha1MacSize]; Helpers.RngFill(salt); - Pkcs12Kdf.DeriveMacKey( - password, - hashAlgorithm, - 1, - salt, - macKey); +#if NET + Span macKey = stackalloc byte[Sha1MacSize]; +#else + byte[] macKey = new byte[Sha1MacSize]; +#endif - using (IncrementalHash mac = IncrementalHash.CreateHMAC(hashAlgorithm, macKey)) + // Pin macKey (if it's on the heap), derive the key into it, then overwrite it with the MAC output. +#if !NET + fixed (byte* macKeyPin = macKey) +#endif { - mac.AppendData(authSafe); - - if (!mac.TryGetHashAndReset(macKey, out int bytesWritten) || bytesWritten != macKey.Length) + Pkcs12Kdf.DeriveMacKey( + password, + hashAlgorithm, + 1, + salt, + macKey); + + using (IncrementalHash mac = IncrementalHash.CreateHMAC(hashAlgorithm, macKey)) { - Debug.Fail($"TryGetHashAndReset wrote {bytesWritten} of {macKey.Length} bytes"); - throw new CryptographicException(); + mac.AppendData(authSafe); + + if (!mac.TryGetHashAndReset(macKey, out int bytesWritten) || bytesWritten != macKey.Length) + { + Debug.Fail($"TryGetHashAndReset wrote {bytesWritten} of {macKey.Length} bytes"); + throw new CryptographicException(); + } } } @@ -885,24 +900,20 @@ internal ArraySegment ToPfx(ReadOnlySpan password) // authSafe ContentInfo, // macData MacData OPTIONAL // } + using (writer.PushSequence()) { - writer.PushSequence(); - writer.WriteInteger(3); - writer.PushSequence(); + using (writer.PushSequence()) { writer.WriteObjectIdentifierForCrypto(Oids.Pkcs7Data); Asn1Tag contextSpecific0 = new Asn1Tag(TagClass.ContextSpecific, 0); - writer.PushSequence(contextSpecific0); + using (writer.PushSequence(contextSpecific0)) { writer.WriteOctetString(authSafe); - writer.PopSequence(contextSpecific0); } - - writer.PopSequence(); } // https://tools.ietf.org/html/rfc7292#section-4 @@ -914,25 +925,20 @@ internal ArraySegment ToPfx(ReadOnlySpan password) // -- Note: The default is for historical reasons and its use is // -- deprecated. // } - writer.PushSequence(); + using (writer.PushSequence()) { - writer.PushSequence(); + using (writer.PushSequence()) { - writer.PushSequence(); + using (writer.PushSequence()) { writer.WriteObjectIdentifierForCrypto(Oids.Sha1); - writer.PopSequence(); } writer.WriteOctetString(macKey); - writer.PopSequence(); } writer.WriteOctetString(salt); - writer.PopSequence(); } - - writer.PopSequence(); } byte[] ret = CryptoPool.Rent(writer.GetEncodedLength()); diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs index faaa775e4de52e..8468c8fbe7c064 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs @@ -7,7 +7,6 @@ using System.Formats.Asn1; using System.IO; using System.IO.MemoryMappedFiles; -using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -668,7 +667,7 @@ private static (byte[]?, int, MemoryManager?) ReadAllBytesIfBerSequence(st [DoesNotReturn] private static void ThrowWithHResult(string message, int hResult) { -#if NETCOREAPP +#if NET throw new CryptographicException(message) { HResult = hResult, @@ -689,7 +688,7 @@ private static void ThrowWithHResult(string message, int hResult) [DoesNotReturn] private static void ThrowWithHResult(string message, int hResult, Exception innerException) { -#if NETCOREAPP +#if NET throw new CryptographicException(message, innerException) { HResult = hResult, diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs index 60d8785679d5ef..53b53fba3ef7af 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs @@ -9,6 +9,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests { + [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support X.509 certificates")] public class X509CertificateLoaderPkcs12Tests_FromByteArray : X509CertificateLoaderPkcs12Tests { protected override void NullInputAssert(Action action) => @@ -53,6 +54,7 @@ protected override bool TryGetContentType(byte[] bytes, string path, out X509Con } } + [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support X.509 certificates")] public class X509CertificateLoaderPkcs12Tests_FromByteSpan : X509CertificateLoaderPkcs12Tests { protected override void NullInputAssert(Action action) => @@ -115,6 +117,7 @@ protected override bool TryGetContentType(byte[] bytes, string path, out X509Con } } + [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support X.509 certificates")] public class X509CertificateLoaderPkcs12Tests_FromFile : X509CertificateLoaderPkcs12Tests { protected override void NullInputAssert(Action action) => diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs index 3fef8720365fa1..86a8903d078a36 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs @@ -24,6 +24,8 @@ protected override X509Certificate2 LoadCertificateFileOnly(string path) => protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) { + // If the test data only provides data from a file, don't check the content type + // (it will be checked by the file variant). if (bytes is null) { contentType = X509ContentType.Unknown; @@ -61,6 +63,13 @@ protected override X509Certificate2 LoadCertificateFileOnly(string path) protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) { + // All test data is either from a byte[] or a file. + // If it comes from a byte[], it'll get verified by _FromByteArray; + // likewise with a file and _FromFile. + // + // Since there are no uniquely span inputs, and not all the applicable TFMs have + // a GetContentType(ReadOnlySpan), just always return false and skip the file + // format sanity test in the _FromByteSpan variant. contentType = X509ContentType.Unknown; return false; } @@ -97,6 +106,8 @@ protected override X509Certificate2 LoadCertificateNoFile(byte[] bytes) protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) { + // If the test data only provides data from a byte[], don't check the content type + // (it will be checked by the byte array variant). if (path is null) { contentType = X509ContentType.Unknown; @@ -269,7 +280,7 @@ public void LoadCertificate_DER_WithTrailingData() internal static void AssertRawDataEquals(byte[] expected, X509Certificate2 cert) { -#if NETCOREAPP +#if NET AssertExtensions.SequenceEqual(TestData.MsCertificate, cert.RawDataMemory.Span); #else AssertExtensions.SequenceEqual(TestData.MsCertificate, cert.RawData); diff --git a/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.cs b/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.cs index a3b9cba6a80087..e13db0f1f5e4ec 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.cs +++ b/src/libraries/Microsoft.Bcl.Cryptography/ref/Microsoft.Bcl.Cryptography.cs @@ -54,7 +54,7 @@ public sealed partial class Pkcs12LoadLimitExceededException : System.Security.C { public Pkcs12LoadLimitExceededException(string propertyName) { } } -#if NETCOREAPP +#if NET [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] #endif public static partial class X509CertificateLoader diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/NetStandardShims.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/NetStandardShims.cs index 9766bcdeadb65b..1e81d7a01b023c 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/NetStandardShims.cs +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/NetStandardShims.cs @@ -95,7 +95,6 @@ internal static void AppendData(this IncrementalHash hash, ReadOnlySpan da } } -#if NETSTANDARD2_0 internal static bool TryGetHashAndReset( this IncrementalHash hash, Span destination, @@ -124,7 +123,6 @@ internal static bool TryGetHashAndReset( bytesWritten = actual.Length; return true; } -#endif } internal static class CryptographicOperations diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeParameters.netstandard.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeParameters.netstandard.cs index 197baaafb9ed99..890f5b32c4934f 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeParameters.netstandard.cs +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeParameters.netstandard.cs @@ -3,7 +3,7 @@ namespace System.Security.Cryptography { -#if !NETCOREAPP +#if !NET internal sealed class PbeParameters { public PbeEncryptionAlgorithm EncryptionAlgorithm { get; } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs index c725ce79b82698..e5eec17e57b69c 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs @@ -32,7 +32,7 @@ private static partial ICertificatePal LoadCertificatePalFromFile(string path) if (!OpenSslX509CertificateReader.TryReadX509Der(fileBio, out pal)) { - Interop.Crypto.BioSeek(fileBio, bioPosition); + OpenSslX509CertificateReader.RewindBio(fileBio, bioPosition); if (!OpenSslX509CertificateReader.TryReadX509Pem(fileBio, out pal)) { diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs index 097f2d49dfb8f7..11e253150053cf 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs @@ -19,7 +19,6 @@ private static partial ICertificatePal LoadCertificatePal(ReadOnlySpan dat if (X509Certificate2.GetCertContentType(data) != X509ContentType.Cert) { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs index c4c9bc2550c834..176f202c3258f9 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs @@ -24,16 +24,9 @@ public class NewLoaderFromFile : CommonTests { protected override X509Certificate2 LoadCertificate(byte[] data) { - string path = Path.GetTempFileName(); - - try - { - File.WriteAllBytes(path, data); - return X509CertificateLoader.LoadCertificateFromFile(path); - } - finally + using (TempFileHolder holder = new TempFileHolder(data)) { - File.Delete(path); + return X509CertificateLoader.LoadCertificateFromFile(holder.FilePath); } } } @@ -49,16 +42,9 @@ public class LegacyLoaderFromFile : CommonTests { protected override X509Certificate2 LoadCertificate(byte[] data) { - string path = Path.GetTempFileName(); - - try - { - File.WriteAllBytes(path, data); - return new X509Certificate2(path); - } - finally + using (TempFileHolder holder = new TempFileHolder(data)) { - File.Delete(path); + return new X509Certificate2(holder.FilePath); } } } diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs index 76a9e78cd74280..160c2a722f6c56 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs @@ -211,8 +211,8 @@ public static void TestCopyConstructor_Lifetime_Independent() [Fact] public static void Loader_Lifetime_Independent() { - var c1 = X509CertificateLoader.LoadPkcs12(TestData.PfxData, TestData.PfxDataPassword); - using (var c2 = X509CertificateLoader.LoadPkcs12(TestData.PfxData, TestData.PfxDataPassword)) + X509Certificate2 c1 = X509CertificateLoader.LoadPkcs12(TestData.PfxData, TestData.PfxDataPassword); + using (X509Certificate2 c2 = X509CertificateLoader.LoadPkcs12(TestData.PfxData, TestData.PfxDataPassword)) { RSA rsa = c2.GetRSAPrivateKey(); byte[] hash = new byte[SHA256.HashSizeInBytes]; From c704627ca5a87a36a189b1d09fd2b5c0372b04ac Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 6 Jun 2024 16:22:41 -0700 Subject: [PATCH 03/19] Respond to further feedback --- .../Cryptography/PasswordBasedEncryption.cs | 4 +- .../X509Certificates/Pkcs12LoaderLimits.cs | 4 +- .../X509Certificates/TestData.cs | 0 .../PbeEncryptionAlgorithm.netstandard.cs | 2 +- .../tests/X509Certificates/TestData.cs | 1222 ----------------- .../X509Certificate.LegacyLimits.cs | 13 +- .../System.Security.Cryptography.Tests.csproj | 3 +- 7 files changed, 10 insertions(+), 1238 deletions(-) rename src/libraries/{System.Security.Cryptography/tests => Common/tests/System/Security/Cryptography}/X509Certificates/TestData.cs (100%) delete mode 100644 src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestData.cs diff --git a/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs b/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs index 9193bfe4146ff5..0e931591200a77 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/PasswordBasedEncryption.cs @@ -1116,7 +1116,7 @@ private static byte[] DeriveKey( HashAlgorithmName prf, int outputLength) { -#if NETCOREAPP +#if NET return Rfc2898DeriveBytes.Pbkdf2(password, salt, iterationCount, prf, outputLength); #else using (Rfc2898DeriveBytes pbkdf2 = OpenPbkdf2(password, salt.ToArray(), iterationCount, prf)) @@ -1132,7 +1132,7 @@ private static Rfc2898DeriveBytes OpenPbkdf2( int iterationCount, HashAlgorithmName prf) { -#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER || NET472_OR_GREATER +#if NET || NETSTANDARD2_1_OR_GREATER || NET472_OR_GREATER #pragma warning disable CA5379 return new Rfc2898DeriveBytes( password, diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs index fa37a42b4eb542..3200771cee0adb 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs @@ -158,9 +158,7 @@ public int? MacIterationLimit get => _macIterationLimit; set { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - + CheckNonNegative(value); CheckReadOnly(); _macIterationLimit = value; } diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TestData.cs similarity index 100% rename from src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs rename to src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TestData.cs diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeEncryptionAlgorithm.netstandard.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeEncryptionAlgorithm.netstandard.cs index 2669c9378f0677..f73df6b4e081a0 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeEncryptionAlgorithm.netstandard.cs +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/PbeEncryptionAlgorithm.netstandard.cs @@ -3,7 +3,7 @@ namespace System.Security.Cryptography { -#if !NETCOREAPP +#if !NET internal enum PbeEncryptionAlgorithm { Unknown = 0, diff --git a/src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestData.cs b/src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestData.cs deleted file mode 100644 index 2826ce1468eb81..00000000000000 --- a/src/libraries/Microsoft.Bcl.Cryptography/tests/X509Certificates/TestData.cs +++ /dev/null @@ -1,1222 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Test.Cryptography; - -namespace System.Security.Cryptography.X509Certificates.Tests -{ - internal static class TestData - { - internal const string PlaceholderPw = "Placeholder"; - - public static byte[] MsCertificate = ( - "308204ec308203d4a003020102021333000000b011af0a8bd03b9fdd00010000" + - "00b0300d06092a864886f70d01010505003079310b3009060355040613025553" + - "311330110603550408130a57617368696e67746f6e3110300e06035504071307" + - "5265646d6f6e64311e301c060355040a13154d6963726f736f667420436f7270" + - "6f726174696f6e312330210603550403131a4d6963726f736f667420436f6465" + - "205369676e696e6720504341301e170d3133303132343232333333395a170d31" + - "34303432343232333333395a308183310b300906035504061302555331133011" + - "0603550408130a57617368696e67746f6e3110300e060355040713075265646d" + - "6f6e64311e301c060355040a13154d6963726f736f667420436f72706f726174" + - "696f6e310d300b060355040b13044d4f5052311e301c060355040313154d6963" + - "726f736f667420436f72706f726174696f6e30820122300d06092a864886f70d" + - "01010105000382010f003082010a0282010100e8af5ca2200df8287cbc057b7f" + - "adeeeb76ac28533f3adb407db38e33e6573fa551153454a5cfb48ba93fa837e1" + - "2d50ed35164eef4d7adb137688b02cf0595ca9ebe1d72975e41b85279bf3f82d" + - "9e41362b0b40fbbe3bbab95c759316524bca33c537b0f3eb7ea8f541155c0865" + - "1d2137f02cba220b10b1109d772285847c4fb91b90b0f5a3fe8bf40c9a4ea0f5" + - "c90a21e2aae3013647fd2f826a8103f5a935dc94579dfb4bd40e82db388f12fe" + - "e3d67a748864e162c4252e2aae9d181f0e1eb6c2af24b40e50bcde1c935c49a6" + - "79b5b6dbcef9707b280184b82a29cfbfa90505e1e00f714dfdad5c238329ebc7" + - "c54ac8e82784d37ec6430b950005b14f6571c50203010001a38201603082015c" + - "30130603551d25040c300a06082b06010505070303301d0603551d0e04160414" + - "5971a65a334dda980780ff841ebe87f9723241f230510603551d11044a3048a4" + - "463044310d300b060355040b13044d4f5052313330310603550405132a333135" + - "39352b34666166306237312d616433372d346161332d613637312d3736626330" + - "35323334346164301f0603551d23041830168014cb11e8cad2b4165801c9372e" + - "331616b94c9a0a1f30560603551d1f044f304d304ba049a0478645687474703a" + - "2f2f63726c2e6d6963726f736f66742e636f6d2f706b692f63726c2f70726f64" + - "756374732f4d6963436f645369675043415f30382d33312d323031302e63726c" + - "305a06082b06010505070101044e304c304a06082b06010505073002863e6874" + - "74703a2f2f7777772e6d6963726f736f66742e636f6d2f706b692f6365727473" + - "2f4d6963436f645369675043415f30382d33312d323031302e637274300d0609" + - "2a864886f70d0101050500038201010031d76e2a12573381d59dc6ebf93ad444" + - "4d089eee5edf6a5bb779cf029cbc76689e90a19c0bc37fa28cf14dba9539fb0d" + - "e0e19bf45d240f1b8d88153a7cdbadceb3c96cba392c457d24115426300d0dff" + - "47ea0307e5e4665d2c7b9d1da910fa1cb074f24f696b9ea92484daed96a0df73" + - "a4ef6a1aac4b629ef17cc0147f48cd4db244f9f03c936d42d8e87ce617a09b68" + - "680928f90297ef1103ba6752adc1e9b373a6d263cd4ae23ee4f34efdffa1e0bb" + - "02133b5d20de553fa3ae9040313875285e04a9466de6f57a7940bd1fcde845d5" + - "aee25d3ef575c7e6666360ccd59a84878d2430f7ef34d0631db142674a0e4bbf" + - "3a0eefb6953aa738e4259208a6886682").HexToByteArray(); - - // This pfx was generated by new X509Certificate(MsCertificate).Export(X509ContentType.Pfx) - // and was choosen when the padding was 01 and caused a false-positive on decryption. - public static byte[] MsCertificateExportedToPfx_NullPassword = Convert.FromBase64String( - /* [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="This PKCS#12 blob only contains public info.")] */ @" -MIIFxAIBAzCCBYoGCSqGSIb3DQEHAaCCBXsEggV3MIIFczCCBW8GCSqGSIb3DQEH -BqCCBWAwggVcAgEAMIIFVQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIKpCU -u5nlxAACAggAgIIFKG/SLlS1TJmxGUiXBPJ1r4yV+JMehwo6RYPMkCSnpKGaiLyA -TLo/LPv2pwbna9R6eWMJqHUUhYKidx7Wh2gRVdDQ53FbKdbAdBPOKC+bZAQpjnHq -YRIEpnkfsee4of0O3hUCC2FPqrtnwGvyrcjLCzKZGzdLAtT4DGs7AJ3Y9YXDMy1E -MYjJLzR8Gg0teHXVgx2/QFNWclO9MYxRHzCYQkDCB3A67/gH8qrApks+3gmPTrFd -IyIjvuUmEbqeLRh/4JTIJZp4sQEgiyfk7eM5kffvWYGzoTgkYayRQYmrgRL1vkWg -hr/AXBvYzB5WY/7thp6WYilK7njVwbu3cEbGHchzhJj9OpQb5mEu9Tmx9fgFSc4l -a7lizHUwaHqH74/KEykDrlezaKp3xp0SO9b/rL7t4mkbbBe5QTJVgrgtGATBEBA9 -h4/gGiO8VWQSc0+nRzsiqkw63xLPb4dXK+ILg9NoFvIJ3xq6/HriwuGs8cyndflL -o12M4I7uzdeCTuJbIcxzI/4jF8U07n62hj39+n5IgtGPc63cSmHwKynnRLXgljSS -nBlU4q2LpXnjbCgrczyLDoRn1p1NHq0xaslFSKiV0yqO5aZ5cf6k0y+uUD84kHoK -uxbRFrcBtFIDIN0cHLKpGThiRSTBAZXLBWO+VVtZrE3yJtIjVIDhdyItt+F57rJV -7zFrzvytf1nnh8tZwLMOdwoi/X0F3WScWCscHtd6FUYDKCBgCauXW14dDX+qZE1j -CJE/zyJQMd3VOxd1m83tFJy+yC7pd+6m3CeTY8gnFAhuDECwOIhDSiROIDI7YUIh -PNOoF7/10WWgINGnKJdsPVWTP8TAMK9ePgQXyrzMpzHxko5GmLcEe5f55YqXaTwZ -JGL/MdS88YoL6GNbNpCm5mSbM3fcg72YE5c328sPDkGh4EBvWBFqu1pTYM0bm8sJ -qGjoFzVcl16O45saaVCApPihYflIxO5f48P+XcUq9RMEY11wQxLEjWJLm0EnSV0Y -GTuFT0bs8AwPJC/DJroTx/A259KkU96WpNC6icczdmGAB4MRJ8O22o7tsATYXPbU -69sLxbSK0tZGG01T0QwPwxq7vnFP5oHQDRvopQsmFOKy8j1wrABs9z646QIhs1fe -hQUPmyD3T1DvEe7p5agbksAspzlHz0rr9aP6VQPOg2M+548iTdNVaNRYxgZAL8Ew -W4x/2LANFs/wFlO6ecmlyeHzBSvMpwbYuWS5buW1iXnwHywpZt0Zv3auafGDQOqx -KeMYn+FJzvvkX8n34oWiVnJTrt+uX2Z3Q7BmZ4hRUdE/uSApoFejVTs67W8LDnL7 -VKOajQpN0IRMQv3ab34hFMmnxhs0b1B66atDlybEBsJl9xj3lzSZs+LX7tPVd+Ac -/2bRUxGgwJBK6mk7v8JikgqYCGrzT6kLvBnCkEeQa8uEPohe8aOxe4+juqjz6GqD -95YoDfEXCdF7G0rAS6pFD1YnI3DyBhm33POjeQGCverS+D8clDRzBa4ei56lzaUx -VqbMLxcxARYZw8L+fSMnhpYQgUuEfq8177USSETpUvnpD/qnMwfHwblmEaW2vsHO -AG4Sj4DcWxhRg5MnIA9fkwo6n/aQWAouv0ErNuzR1TjBM3qxrTrVM2bYoyBD9kf0 -OZ0b9sU+hiJ0LYif2/CczJwouCt58nDeYWzS4w9U/QqsaVALqMiMAgVfpIF5PYNx -9z45Sobpl+L5m5OVOe6mhKIJ7A7CnPNzKfF+mvb2Sg1kcPgwUDAxMCEwCQYFKw4D -AhoFAAQUUW5vGmwJT06SIGY9OIMn5kaZfy4ECDQi49YQi3auAgIIAA=="); - - public static readonly byte[] MsCertificatePemBytes = ByteUtils.AsciiBytes( - @"-----BEGIN CERTIFICATE----- -MIIE7DCCA9SgAwIBAgITMwAAALARrwqL0Duf3QABAAAAsDANBgkqhkiG9w0BAQUF -ADB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH -UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSMwIQYDVQQD -ExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQTAeFw0xMzAxMjQyMjMzMzlaFw0x -NDA0MjQyMjMzMzlaMIGDMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv -bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 -aW9uMQ0wCwYDVQQLEwRNT1BSMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRp -b24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDor1yiIA34KHy8BXt/ -re7rdqwoUz8620B9s44z5lc/pVEVNFSlz7SLqT+oN+EtUO01Fk7vTXrbE3aIsCzw -WVyp6+HXKXXkG4Unm/P4LZ5BNisLQPu+O7q5XHWTFlJLyjPFN7Dz636o9UEVXAhl -HSE38Cy6IgsQsRCddyKFhHxPuRuQsPWj/ov0DJpOoPXJCiHiquMBNkf9L4JqgQP1 -qTXclFed+0vUDoLbOI8S/uPWenSIZOFixCUuKq6dGB8OHrbCryS0DlC83hyTXEmm -ebW22875cHsoAYS4KinPv6kFBeHgD3FN/a1cI4Mp68fFSsjoJ4TTfsZDC5UABbFP -ZXHFAgMBAAGjggFgMIIBXDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU -WXGmWjNN2pgHgP+EHr6H+XIyQfIwUQYDVR0RBEowSKRGMEQxDTALBgNVBAsTBE1P -UFIxMzAxBgNVBAUTKjMxNTk1KzRmYWYwYjcxLWFkMzctNGFhMy1hNjcxLTc2YmMw -NTIzNDRhZDAfBgNVHSMEGDAWgBTLEejK0rQWWAHJNy4zFha5TJoKHzBWBgNVHR8E -TzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9k -dWN0cy9NaWNDb2RTaWdQQ0FfMDgtMzEtMjAxMC5jcmwwWgYIKwYBBQUHAQEETjBM -MEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRz -L01pY0NvZFNpZ1BDQV8wOC0zMS0yMDEwLmNydDANBgkqhkiG9w0BAQUFAAOCAQEA -MdduKhJXM4HVncbr+TrURE0Inu5e32pbt3nPApy8dmiekKGcC8N/oozxTbqVOfsN -4OGb9F0kDxuNiBU6fNutzrPJbLo5LEV9JBFUJjANDf9H6gMH5eRmXSx7nR2pEPoc -sHTyT2lrnqkkhNrtlqDfc6TvahqsS2Ke8XzAFH9IzU2yRPnwPJNtQtjofOYXoJto -aAko+QKX7xEDumdSrcHps3Om0mPNSuI+5PNO/f+h4LsCEztdIN5VP6OukEAxOHUo -XgSpRm3m9Xp5QL0fzehF1a7iXT71dcfmZmNgzNWahIeNJDD37zTQYx2xQmdKDku/ -Og7vtpU6pzjkJZIIpohmgg== ------END CERTIFICATE----- -"); - - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test password.")] - public const string PfxDataPassword = "12345"; - - private static readonly byte[] PfxData_RC2ContentEncryption = ( - "3082063A020103308205F606092A864886F70D010701A08205E7048205E33082" + - "05DF3082035806092A864886F70D010701A08203490482034530820341308203" + - "3D060B2A864886F70D010C0A0102A08202B6308202B2301C060A2A864886F70D" + - "010C0103300E04085052002C7DA2C2A6020207D0048202907D485E3BFC6E6457" + - "C811394C145D0E8A18325646854E4FF0097BC5A98547F5AD616C8EFDA8505AA8" + - "7564ED4800A3139759497C60C6688B51F376ACAE906429C8771CB1428226B68A" + - "6297207BCC9DD7F9563478DD83880AAB2304B545759B2275305DF4EFF9FAC24A" + - "3CC9D3B2D672EFE45D8F48E24A16506C1D7566FC6D1B269FBF201B3AC3309D3E" + - "BC6FD606257A7A707AA2F790EA3FE7A94A51138540C5319010CBA6DE9FB9D85F" + - "CDC78DA60E33DF2F21C46FB9A8554B4F82E0A6EDBA4DB5585D77D331D35DAAED" + - "51B6A5A3E000A299880FB799182C8CA3004B7837A9FEB8BFC76778089993F3D1" + - "1D70233608AF7C50722D680623D2BF54BD4B1E7A604184D9F44E0AF8099FFA47" + - "1E5536E7902793829DB9902DDB61264A62962950AD274EA516B2D44BE9036530" + - "016E607B73F341AEEFED2211F6330364738B435B0D2ED6C57747F6C8230A053F" + - "78C4DD65DB83B26C6A47836A6CBBAB92CBB262C6FB6D08632B4457F5FA8EABFA" + - "65DB34157E1D301E9085CC443582CDD15404314872748545EB3FC3C574882655" + - "8C9A85F966E315775BBE9DA34D1E8B6DADC3C9E120C6D6A2E1CFFE4EB014C3CE" + - "FBC19356CE33DAC60F93D67A4DE247B0DAE13CD8B8C9F15604CC0EC9968E3AD7" + - "F57C9F53C45E2ECB0A0945EC0BA04BAA15B48D8596EDC9F5FE9165A5D21949FB" + - "5FE30A920AD2C0F78799F6443C300629B8CA4DCA19B9DBF1E27AAB7B12271228" + - "119A95C9822BE6439414BEEAE24002B46EB97E030E18BD810ADE0BCF4213A355" + - "038B56584B2FBCC3F5EA215D0CF667FFD823EA03AB62C3B193DFB4450AABB50B" + - "AF306E8088EE7384FA2FDFF03E0DD7ACD61832223E806A94D46E196462522808" + - "3163F1CAF333FDBBE2D54CA86968867CE0B6DD5E5B7F0633C6FAB4A19CC14F64" + - "5EC14D0B1436F7623174301306092A864886F70D010915310604040100000030" + - "5D06092B060104018237110131501E4E004D006900630072006F0073006F0066" + - "00740020005300740072006F006E0067002000430072007900700074006F0067" + - "007200610070006800690063002000500072006F007600690064006500723082" + - "027F06092A864886F70D010706A08202703082026C0201003082026506092A86" + - "4886F70D010701301C060A2A864886F70D010C0106300E0408E0C117E67A75D8" + - "EB020207D080820238292882408B31826F0DC635F9BBE7C199A48A3B4FEFC729" + - "DBF95508D6A7D04805A8DD612427F93124F522AC7D3C6F4DDB74D937F57823B5" + - "B1E8CFAE4ECE4A1FFFD801558D77BA31985AA7F747D834CBE84464EF777718C9" + - "865C819D6C9DAA0FA25E2A2A80B3F2AAA67D40E382EB084CCA85E314EA40C3EF" + - "3ED1593904D7A16F37807C99AF06C917093F6C5AAEBB12A6C58C9956D4FBBDDE" + - "1F1E389989C36E19DD38D4B978D6F47131E458AB68E237E40CB6A87F21C8773D" + - "E845780B50995A51F041106F47C740B3BD946038984F1AC9E91230616480962F" + - "11B0683F8802173C596C4BD554642F51A76F9DFFF9053DEF7B3C3F759FC7EEAC" + - "3F2386106C4B8CB669589E004FB235F0357EA5CF0B5A6FC78A6D941A3AE44AF7" + - "B601B59D15CD1EC61BCCC481FBB83EAE2F83153B41E71EF76A2814AB59347F11" + - "6AB3E9C1621668A573013D34D13D3854E604286733C6BAD0F511D7F8FD6356F7" + - "C3198D0CB771AF27F4B5A3C3B571FDD083FD68A9A1EEA783152C436F7513613A" + - "7E399A1DA48D7E55DB7504DC47D1145DF8D7B6D32EAA4CCEE06F98BB3DDA2CC0" + - "D0564A962F86DFB122E4F7E2ED6F1B509C58D4A3B2D0A68788F7E313AECFBDEF" + - "456C31B96FC13586E02AEB65807ED83BB0CB7C28F157BC95C9C593C919469153" + - "9AE3C620ED1D4D4AF0177F6B9483A5341D7B084BC5B425AFB658168EE2D8FB2B" + - "FAB07A3BA061687A5ECD1F8DA9001DD3E7BE793923094ABB0F2CF4D24CB071B9" + - "E568B18336BB4DC541352C9785C48D0F0E53066EB2009EFCB3E5644ED12252C1" + - "BC303B301F300706052B0E03021A04144DEAB829B57A3156AEBC8239C0E7E884" + - "EFD96E680414E147930B932899741C92D7652268938770254A2B020207D0").HexToByteArray(); - - private static readonly byte[] PfxData_TripleDESContentEncryption = ( - "308206980201033082065406092A864886F70D010701A0820645048206413082" + - "063D308203B606092A864886F70D010701A08203A7048203A33082039F308203" + - "9B060B2A864886F70D010C0A0102A08202B6308202B2301C060A2A864886F70D" + - "010C0103300E04083A55E19A8875CF7E020207D004820290C39707B6E15BC075" + - "62B296E37D44A4C3654F398528F51988932D4E78C96AC7E783A21DD306AEB4BB" + - "7490551F5CCB21DBF883B3D01D3CCFEB0148CC79C8C15AD7266BC0C0D23563FF" + - "A879DDB05ACC5B1F9A9BA5F8E32ECE05458A55DC91882750B0573315D843524D" + - "72E400C14E24B530A228B477DF60F01A1CBA22085350C72B8E871AEF219D1457" + - "3A660D71064D4226814B8D360A84FDF9B24578E61D62119C893DF1CB97C733BC" + - "D9CCA8C3FBA40B3E35F305AFCF1E24E720379FC94FB7103A27F817D98AF70D12" + - "0A357EB5840720F48AE6ED59990E80541CCA08B2B3F9FAE0F7554F491616E72B" + - "E2383478709947F5BD5AC8D2F74550A2C4260DAA789C66559DBC25D0D5657DEF" + - "FF491287E9E982C870732E6C1494F25FED513D2DF246EDABA1DEF8FE05A1C283" + - "676A0722961FBAD4B47E8D27D08E4129FACE86CAAB657A1899C7F9286DD534AC" + - "3AE6B1A100C90207B9A39857C1D9B7A061A5E8496F0E099F1323FA56DE85BF34" + - "96DBBE6FFEAAC0321F65A40BAE63503BB704553F1027700D15D12A20EC3A8FB9" + - "8D62D902FBDBD1BA9984242A6413CF243FE1C5A5A42083EA6E821D302CC4BE4D" + - "23C0D92247C027A6D5AE3D645F378916ACBD068F52A7772571F51F4AF46652E6" + - "D9821A24267F6A8CF9D69B3EF5FF33702B71CCDA87A3704DB815685F83E22C64" + - "B60D9B659EEA517BCACDD638DB2EF171692AA17176891901F2BB97F574908CCA" + - "943743C1E8D2520FE7B5E04C10E748A666EAAE81EF5DB3577F669E049BE57CCF" + - "7FFCF7583A9ABE0BC44F0D33EBE7F3BFC2F1F4012F588D0FC8CB0FFDF3BF1A43" + - "A962B0F9059FAAC0BD68B23275584180EDDE1F1703BFE977456079FF0890BFE5" + - "ED9CA93ABECF335C02DEBB7DB1A1E60B5B7746F6F152299A1DB983ACE6D9D5B9" + - "DBAF68A3DEDCCC013181D1301306092A864886F70D0109153106040401000000" + - "305B06092A864886F70D010914314E1E4C007B00430035004600430030003800" + - "350032002D0038004200410033002D0034003700460041002D00410045003400" + - "33002D004200440039003300420044003200350041004300440042007D305D06" + - "092B060104018237110131501E4E004D006900630072006F0073006F00660074" + - "0020005300740072006F006E0067002000430072007900700074006F00670072" + - "00610070006800690063002000500072006F007600690064006500723082027F" + - "06092A864886F70D010706A08202703082026C0201003082026506092A864886" + - "F70D010701301C060A2A864886F70D010C0103300E0408812EAA0D26B0151B02" + - "0207D080820238A471B31EBE90A6B540889297C09BFC73185C2C2676665DBAD2" + - "BC5759464079976B4C82DCB425289BCFABF9CD11AABF18B3CFA1879A958919E5" + - "05465E5B2DBCA5039990650F7D7490D322F2CCCD444ACE4625BE3C361FA5C096" + - "615ACDA3123A478340332961655C973D74EE64F32015194558F4B8148DED4671" + - "7EE0381E8A53B582F176DB914475FB57C2648DCC709D6153825F33F082B91EAB" + - "BB2236343CA919065B9D9112D301E6700CE8104035469EDF7874246373351E13" + - "DC1DAB74DF1DE1E076FE03869F494C0427DE55CA7C17536DEF40D0E3F1B7E5FF" + - "5A2050175624A1F2D9EF4069186424CC956B84997768E589E38D3D836592CF27" + - "881DB2EE51AB682B4F4C58ED14585C51B72CCF156BE3CD2EEC538AD15A598E84" + - "0CFCDF6CD4A151B7EA4BE5388EA903298B4D1A86E7371300A6242DD7A654E430" + - "2B4F849650230F467042CD328A4FD4B33803213C807C37F98024A718B40AD0BD" + - "3AC6279E47FBD27E50D743BC8F595B78F8805D8143EBB3119B919DFFBFAF606F" + - "A858C77C5E0526646B6AACC536A3B2FEA1E0A67CBAE166A8F85913F3600C55F7" + - "850EF48619B9E93FB15F21994BC9ABA8FF7156B68B1DAD472ED2FA3917E1F84D" + - "AD4EB34992493B7084F6490FB326697A0A52BA08BF7F537B3BDC09B421A0D47F" + - "DCB8BD0271963EC4CE4F9C21A45AC382E8AD7ED8D69824BD5C00BCEAF56D88B3" + - "F2D0D5A81CF1C14884040A8E522428154A9C752C0E64204FA849689739E5138F" + - "0F96C471C5FE026EA7EA68AA42DF0A693213ECF06555804A191637A8F7858A30" + - "3B301F300706052B0E03021A04146B4EDA4227EAFC85EDA331AC88761DD06D54" + - "60FD0414C8A2B10C67EAECB3D48BEC69616133185721613D020207D0").HexToByteArray(); - - public static readonly byte[] PfxData = - PlatformSupport.IsRC2Supported - ? PfxData_RC2ContentEncryption - : PfxData_TripleDESContentEncryption; - - public static byte[] StoreSavedAsSerializedCerData = ( - "0200000001000000bc0000001c0000006c000000010000000000000000000000" + - "00000000020000007b00370037004500420044003000320044002d0044003800" + - "440045002d0034003700350041002d0038003800360037002d00440032003000" + - "4200300030003600340045003400390046007d00000000004d00690063007200" + - "6f0073006f006600740020005300740072006f006e0067002000430072007900" + - "700074006f0067007200610070006800690063002000500072006f0076006900" + - "64006500720000002000000001000000e9010000308201e530820152a0030201" + - "020210d5b5bc1c458a558845bff51cb4dff31c300906052b0e03021d05003011" + - "310f300d060355040313064d794e616d65301e170d3130303430313038303030" + - "305a170d3131303430313038303030305a3011310f300d060355040313064d79" + - "4e616d6530819f300d06092a864886f70d010101050003818d00308189028181" + - "00b11e30ea87424a371e30227e933ce6be0e65ff1c189d0d888ec8ff13aa7b42" + - "b68056128322b21f2b6976609b62b6bc4cf2e55ff5ae64e9b68c78a3c2dacc91" + - "6a1bc7322dd353b32898675cfb5b298b176d978b1f12313e3d865bc53465a11c" + - "ca106870a4b5d50a2c410938240e92b64902baea23eb093d9599e9e372e48336" + - "730203010001a346304430420603551d01043b3039801024859ebf125e76af3f" + - "0d7979b4ac7a96a1133011310f300d060355040313064d794e616d658210d5b5" + - "bc1c458a558845bff51cb4dff31c300906052b0e03021d0500038181009bf6e2" + - "cf830ed485b86d6b9e8dffdcd65efc7ec145cb9348923710666791fcfa3ab59d" + - "689ffd7234b7872611c5c23e5e0714531abadb5de492d2c736e1c929e648a65c" + - "c9eb63cd84e57b5909dd5ddf5dbbba4a6498b9ca225b6e368b94913bfc24de6b" + - "2bd9a26b192b957304b89531e902ffc91b54b237bb228be8afcda26476").HexToByteArray(); - - public static byte[] StoreSavedAsSerializedStoreData = ( - "00000000434552540200000001000000bc0000001c0000006c00000001000000" + - "000000000000000000000000020000007b003700370045004200440030003200" + - "44002d0044003800440045002d0034003700350041002d003800380036003700" + - "2d004400320030004200300030003600340045003400390046007d0000000000" + - "4d006900630072006f0073006f006600740020005300740072006f006e006700" + - "2000430072007900700074006f00670072006100700068006900630020005000" + - "72006f007600690064006500720000002000000001000000e9010000308201e5" + - "30820152a0030201020210d5b5bc1c458a558845bff51cb4dff31c300906052b" + - "0e03021d05003011310f300d060355040313064d794e616d65301e170d313030" + - "3430313038303030305a170d3131303430313038303030305a3011310f300d06" + - "0355040313064d794e616d6530819f300d06092a864886f70d01010105000381" + - "8d0030818902818100b11e30ea87424a371e30227e933ce6be0e65ff1c189d0d" + - "888ec8ff13aa7b42b68056128322b21f2b6976609b62b6bc4cf2e55ff5ae64e9" + - "b68c78a3c2dacc916a1bc7322dd353b32898675cfb5b298b176d978b1f12313e" + - "3d865bc53465a11cca106870a4b5d50a2c410938240e92b64902baea23eb093d" + - "9599e9e372e48336730203010001a346304430420603551d01043b3039801024" + - "859ebf125e76af3f0d7979b4ac7a96a1133011310f300d060355040313064d79" + - "4e616d658210d5b5bc1c458a558845bff51cb4dff31c300906052b0e03021d05" + - "00038181009bf6e2cf830ed485b86d6b9e8dffdcd65efc7ec145cb9348923710" + - "666791fcfa3ab59d689ffd7234b7872611c5c23e5e0714531abadb5de492d2c7" + - "36e1c929e648a65cc9eb63cd84e57b5909dd5ddf5dbbba4a6498b9ca225b6e36" + - "8b94913bfc24de6b2bd9a26b192b957304b89531e902ffc91b54b237bb228be8" + - "afcda264762000000001000000f0040000308204ec308203d4a0030201020213" + - "33000000b011af0a8bd03b9fdd0001000000b0300d06092a864886f70d010105" + - "05003079310b3009060355040613025553311330110603550408130a57617368" + - "696e67746f6e3110300e060355040713075265646d6f6e64311e301c06035504" + - "0a13154d6963726f736f667420436f72706f726174696f6e3123302106035504" + - "03131a4d6963726f736f667420436f6465205369676e696e6720504341301e17" + - "0d3133303132343232333333395a170d3134303432343232333333395a308183" + - "310b3009060355040613025553311330110603550408130a57617368696e6774" + - "6f6e3110300e060355040713075265646d6f6e64311e301c060355040a13154d" + - "6963726f736f667420436f72706f726174696f6e310d300b060355040b13044d" + - "4f5052311e301c060355040313154d6963726f736f667420436f72706f726174" + - "696f6e30820122300d06092a864886f70d01010105000382010f003082010a02" + - "82010100e8af5ca2200df8287cbc057b7fadeeeb76ac28533f3adb407db38e33" + - "e6573fa551153454a5cfb48ba93fa837e12d50ed35164eef4d7adb137688b02c" + - "f0595ca9ebe1d72975e41b85279bf3f82d9e41362b0b40fbbe3bbab95c759316" + - "524bca33c537b0f3eb7ea8f541155c08651d2137f02cba220b10b1109d772285" + - "847c4fb91b90b0f5a3fe8bf40c9a4ea0f5c90a21e2aae3013647fd2f826a8103" + - "f5a935dc94579dfb4bd40e82db388f12fee3d67a748864e162c4252e2aae9d18" + - "1f0e1eb6c2af24b40e50bcde1c935c49a679b5b6dbcef9707b280184b82a29cf" + - "bfa90505e1e00f714dfdad5c238329ebc7c54ac8e82784d37ec6430b950005b1" + - "4f6571c50203010001a38201603082015c30130603551d25040c300a06082b06" + - "010505070303301d0603551d0e041604145971a65a334dda980780ff841ebe87" + - "f9723241f230510603551d11044a3048a4463044310d300b060355040b13044d" + - "4f5052313330310603550405132a33313539352b34666166306237312d616433" + - "372d346161332d613637312d373662633035323334346164301f0603551d2304" + - "1830168014cb11e8cad2b4165801c9372e331616b94c9a0a1f30560603551d1f" + - "044f304d304ba049a0478645687474703a2f2f63726c2e6d6963726f736f6674" + - "2e636f6d2f706b692f63726c2f70726f64756374732f4d6963436f6453696750" + - "43415f30382d33312d323031302e63726c305a06082b06010505070101044e30" + - "4c304a06082b06010505073002863e687474703a2f2f7777772e6d6963726f73" + - "6f66742e636f6d2f706b692f63657274732f4d6963436f645369675043415f30" + - "382d33312d323031302e637274300d06092a864886f70d010105050003820101" + - "0031d76e2a12573381d59dc6ebf93ad4444d089eee5edf6a5bb779cf029cbc76" + - "689e90a19c0bc37fa28cf14dba9539fb0de0e19bf45d240f1b8d88153a7cdbad" + - "ceb3c96cba392c457d24115426300d0dff47ea0307e5e4665d2c7b9d1da910fa" + - "1cb074f24f696b9ea92484daed96a0df73a4ef6a1aac4b629ef17cc0147f48cd" + - "4db244f9f03c936d42d8e87ce617a09b68680928f90297ef1103ba6752adc1e9" + - "b373a6d263cd4ae23ee4f34efdffa1e0bb02133b5d20de553fa3ae9040313875" + - "285e04a9466de6f57a7940bd1fcde845d5aee25d3ef575c7e6666360ccd59a84" + - "878d2430f7ef34d0631db142674a0e4bbf3a0eefb6953aa738e4259208a68866" + - "82000000000000000000000000").HexToByteArray(); - - internal static readonly byte[] EmptyPfx = ( - "304F020103301106092A864886F70D010701A004040230003037301F30070605" + - "2B0E03021A0414822078BC83E955E314BDA908D76D4C5177CC94EB0414711018" + - "F2897A44A90E92779CB655EA11814EC598").HexToByteArray(); - - internal const string ChainPfxPassword = "test"; - - private static readonly byte[] ChainPfxBytes_RC2ContentEncryption = ( - "308213790201033082133506092A864886F70D010701A0821326048213223082" + - "131E3082036706092A864886F70D010701A08203580482035430820350308203" + - "4C060B2A864886F70D010C0A0102A08202B6308202B2301C060A2A864886F70D" + - "010C0103300E040811E8B9808BA6E96C020207D004820290D11DA8713602105C" + - "95792D65BCDFC1B7E3708483BF6CD83008082F89DAE4D003F86081B153BD4D4A" + - "C122E802752DEA29F07D0B7E8F0FB8A762B4CAA63360F9F72CA5846771980A6F" + - "AE2643CD412E6E4A101625371BBD48CC6E2D25191D256B531B06DB7CDAC04DF3" + - "E10C6DC556D5FE907ABF32F2966A561C988A544C19B46DF1BE531906F2CC2263" + - "A301302A857075C7A9C48A395241925C6A369B60D176419D75E320008D5EFD91" + - "5257B160F6CD643953E85F19EBE4E4F72B9B787CF93E95F819D1E43EF01CCFA7" + - "48F0E7260734EA9BC6039BA7557BE6328C0149718A1D9ECF3355082DE697B6CD" + - "630A9C224D831B7786C7E904F1EF2D9D004E0E825DD74AC4A576CDFCA7CECD14" + - "D8E2E6CCAA3A302871AE0BA979BB25559215D771FAE647905878E797BBA9FC62" + - "50F30F518A8008F5A12B35CE526E31032B56EFE5A4121E1E39DC7339A0CE8023" + - "24CDDB7E9497BA37D8B9F8D826F901C52708935B4CA5B0D4D760A9FB33B0442D" + - "008444D5AEB16E5C32187C7038F29160DD1A2D4DB1F9E9A6C035CF5BCED45287" + - "C5DEBAB18743AAF90E77201FEA67485BA3BBCE90CEA4180C447EE588AC19C855" + - "638B9552D47933D2760351174D9C3493DCCE9708B3EFE4BE398BA64051BF52B7" + - "C1DCA44D2D0ED5A6CFB116DDA41995FA99373C254F3F3EBF0F0049F1159A8A76" + - "4CFE9F9CC56C5489DD0F4E924158C9B1B626030CB492489F6AD0A9DCAF3E141D" + - "B4D4821B2D8A384110B6B0B522F62A9DC0C1315A2A73A7F25F96C530E2F700F9" + - "86829A839B944AE6758B8DD1A1E9257F91C160878A255E299C18424EB9983EDE" + - "6DD1C5F4D5453DD5A56AC87DB1EFA0806E3DBFF10A9623FBAA0BAF352F50AB5D" + - "B16AB1171145860D21E2AB20B45C8865B48390A66057DE3A1ABE45EA65376EF6" + - "A96FE36285C2328C318182301306092A864886F70D0109153106040401000000" + - "306B06092B0601040182371101315E1E5C004D006900630072006F0073006F00" + - "66007400200045006E00680061006E0063006500640020004300720079007000" + - "74006F0067007200610070006800690063002000500072006F00760069006400" + - "650072002000760031002E003030820FAF06092A864886F70D010706A0820FA0" + - "30820F9C02010030820F9506092A864886F70D010701301C060A2A864886F70D" + - "010C0106300E0408FFCC41FD8C8414F6020207D080820F68092C6010873CF9EC" + - "54D4676BCFB5FA5F523D03C981CB4A3DC096074E7D04365DDD1E80BF366B8F9E" + - "C4BC056E8CE0CAB516B9C28D17B55E1EB744C43829D0E06217852FA99CCF5496" + - "176DEF9A48967C1EEB4A384DB7783E643E35B5B9A50533B76B8D53581F02086B" + - "782895097860D6CA512514E10D004165C85E561DF5F9AEFD2D89B64F178A7385" + - "C7FA40ECCA899B4B09AE40EE60DAE65B31FF2D1EE204669EFF309A1C7C8D7B07" + - "51AE57276D1D0FB3E8344A801AC5226EA4ED97FCD9399A4EB2E778918B81B17F" + - "E4F65B502595195C79E6B0E37EB8BA36DB12435587E10037D31173285D45304F" + - "6B0056512B3E147D7B5C397709A64E1D74F505D2BD72ED99055161BC57B6200F" + - "2F48CF128229EFBEBFC2707678C0A8C51E3C373271CB4FD8EF34A1345696BF39" + - "50E8CE9831F667D68184F67FE4D30332E24E5C429957694AF23620EA7742F08A" + - "38C9A517A7491083A367B31C60748D697DFA29635548C605F898B64551A48311" + - "CB2A05B1ACA8033128D48E4A5AA263D970FE59FBA49017F29049CF80FFDBD192" + - "95B421FEFF6036B37D2F8DC8A6E36C4F5D707FB05274CC0D8D94AFCC8C6AF546" + - "A0CF49FBD3A67FB6D20B9FE6FDA6321E8ABF5F7CC794CFCC46005DC57A7BAFA8" + - "9954E43230402C8100789F11277D9F05C78DF0509ECFBF3A85114FD35F4F17E7" + - "98D60C0008064E2557BA7BF0B6F8663A6C014E0220693AE29E2AB4BDE5418B61" + - "0889EC02FF5480BD1B344C87D73E6E4DB98C73F881B22C7D298059FE9D7ADA21" + - "92BB6C87F8D25F323A70D234E382F6C332FEF31BB11C37E41903B9A59ADEA5E0" + - "CBAB06DFB835257ABC179A897DEAD9F19B7DF861BE94C655DC73F628E065F921" + - "E5DE98FFCBDF2A54AC01E677E365DD8B932B5BDA761A0032CE2127AB2A2B9DCB" + - "63F1EA8A51FC360AB5BC0AD435F21F9B6842980D795A6734FDB27A4FA8209F73" + - "62DD632FC5FB1F6DE762473D6EA68BFC4BCF983865E66E6D93159EFACC40AB31" + - "AA178806CF893A76CAAA3279C988824A33AF734FAF8E21020D988640FAB6DB10" + - "DF21D93D01776EEA5DAECF695E0C690ED27AD386E6F2D9C9482EA38946008CCB" + - "8F0BD08F9D5058CF8057CA3AD50BB537116A110F3B3ACD9360322DB4D242CC1A" + - "6E15FA2A95192FC65886BE2672031D04A4FB0B1F43AE8476CF82638B61B416AA" + - "97925A0110B736B4D83D7977456F35D947B3D6C9571D8E2DA0E9DEE1E665A844" + - "259C17E01E044FAB898AA170F99157F7B525D524B01BD0710D23A7689A615703" + - "8A0697BD48FFE0253ABD6F862093574B2FC9BA38E1A6EC60AF187F10D79FF71F" + - "7C50E87A07CC0A51099899F7336FE742ADEF25E720B8E0F8781EC7957D414CF5" + - "D44D6998E7E35D2433AFD86442CCA637A1513BE3020B5334614277B3101ED7AD" + - "22AFE50DE99A2AD0E690596C93B881E2962D7E52EE0A770FAF6917106A8FF029" + - "8DF38D6DE926C30834C5D96854FFD053BDB020F7827FB81AD04C8BC2C773B2A5" + - "9FDD6DDF7298A052B3486E03FECA5AA909479DDC7FED972192792888F49C40F3" + - "910140C5BE264D3D07BEBF3275117AF51A80C9F66C7028A2C3155414CF939997" + - "268A1F0AA9059CC3AA7C8BBEF880187E3D1BA8978CBB046E43289A020CAE11B2" + - "5140E2247C15A32CF70C7AA186CBB68B258CF2397D2971F1632F6EBC4846444D" + - "E445673B942F1F110C7D586B6728ECA5B0A62D77696BF25E21ED9196226E5BDA" + - "5A80ECCC785BEEDE917EBC6FFDC2F7124FE8F719B0A937E35E9A720BB9ED72D2" + - "1213E68F058D80E9F8D7162625B35CEC4863BD47BC2D8D80E9B9048811BDD8CB" + - "B70AB215962CD9C40D56AE50B7003630AE26341C6E243B3D12D5933F73F78F15" + - "B014C5B1C36B6C9F410A77CA997931C8BD5CCB94C332F6723D53A4CCC630BFC9" + - "DE96EFA7FDB66FA519F967D6A2DB1B4898BB188DEB98A41FFA7907AE7601DDE2" + - "30E241779A0FDF551FB84D80AAEE3D979F0510CD026D4AE2ED2EFB7468418CCD" + - "B3BD2A29CD7C7DC6419B4637412304D5DA2DC178C0B4669CA8330B9713A812E6" + - "52E812135D807E361167F2A6814CEF2A8A9591EFE2C18216A517473B9C3BF2B7" + - "51E47844893DA30F7DCD4222D1A55D570C1B6F6A99AD1F9213BA8F84C0B14A6D" + - "ED6A26EAFF8F89DF733EEB44117DF0FD357186BA4A15BD5C669F60D6D4C34028" + - "322D4DDF035302131AB6FD08683804CC90C1791182F1AE3281EE69DDBBCC12B8" + - "1E60942FD082286B16BE27DC11E3BB0F18C281E02F3BA66E48C5FD8E8EA3B731" + - "BDB12A4A3F2D9E1F833DD204372003532E1BB11298BDF5092F2959FC439E6BD2" + - "DC6C37E3E775DCBE821B9CBB02E95D84C15E736CEA2FDDAD63F5CD47115B4AD5" + - "5227C2A02886CD2700540EBFD5BF18DC5F94C5874972FD5424FE62B30500B1A8" + - "7521EA3798D11970220B2BE7EFC915FCB7A6B8962F09ABA005861E839813EDA3" + - "E59F70D1F9C277B73928DFFC84A1B7B0F78A8B001164EB0824F2510885CA269F" + - "DCBB2C3AE91BDE91A8BBC648299A3EB626E6F4236CCE79E14C803498562BAD60" + - "28F5B619125F80925A2D3B1A56790795D04F417003A8E9E53320B89D3A3109B1" + - "9BB17B34CC9700DA138FABB5997EC34D0A44A26553153DBCFF8F6A1B5432B150" + - "58F7AD87C6B37537796C95369DAD53BE5543D86D940892F93983153B4031D4FA" + - "B25DAB02C1091ACC1DAE2118ABD26D19435CD4F1A02BDE1896236C174743BCA6" + - "A33FB5429E627EB3FD9F513E81F7BD205B81AAE627C69CF227B043722FA05141" + - "39347D202C9B7B4E55612FC27164F3B5F287F29C443793E22F6ED6D2F353ED82" + - "A9F33EDBA8F5F1B2958F1D6A3943A9614E7411FDBCA597965CD08A8042307081" + - "BAC5A070B467E52D5B91CA58F986C5A33502236B5BAE6DB613B1A408D16B29D3" + - "560F1E94AD840CFA93E83412937A115ABF68322538DA8082F0192D19EAAA41C9" + - "299729D487A9404ECDB6396DDA1534841EAE1E7884FA43574E213AE656116D9E" + - "F7591AA7BDE2B44733DFE27AA59949E5DC0EE00FDF42130A748DDD0FB0053C1A" + - "55986983C8B9CEAC023CAD7EDFFA1C20D3C437C0EF0FC9868D845484D8BE6538" + - "EAADA6365D48BA776EE239ED045667B101E3798FE53E1D4B9A2ACBBE6AF1E5C8" + - "8A3FB03AD616404013E249EC34458F3A7C9363E7772151119FE058BD0939BAB7" + - "64A2E545B0B2FDAA650B7E849C8DD4033922B2CAE46D0461C04A2C87657CB4C0" + - "FFBA23DED69D097109EC8BFDC25BB64417FEEB32842DE3EFEF2BF4A47F08B9FC" + - "D1907BC899CA9DA604F5132FB420C8D142D132E7E7B5A4BD0EF4A56D9E9B0ACD" + - "88F0E862D3F8F0440954879FFE3AA7AA90573C6BFDC6D6474C606ACA1CD94C1C" + - "3404349DD83A639B786AFCDEA1779860C05400E0479708F4A9A0DD51429A3F35" + - "FBD5FB9B68CECC1D585F3E35B7BBFC469F3EAEEB8020A6F0C8E4D1804A3EB32E" + - "B3909E80B0A41571B23931E164E0E1D0D05379F9FD3BF51AF04D2BE78BDB84BD" + - "787D419E85626297CB35FCFB6ED64042EAD2EBC17BB65677A1A33A5C48ADD280" + - "237FB2451D0EFB3A3C32354222C7AB77A3C92F7A45B5FB10092698D88725864A" + - "3685FBDD0DC741424FCCD8A00B928F3638150892CAAB535CC2813D13026615B9" + - "9977F7B8240E914ACA0FF2DCB1A9274BA1F55DF0D24CCD2BAB7741C9EA8B1ECD" + - "E97477C45F88F034FDF73023502944AEE1FF370260C576992826C4B2E5CE9924" + - "84E3B85170FCCAC3413DC0FF6F093593219E637F699A98BD29E8EE4550C128CA" + - "182680FDA3B10BC07625734EE8A8274B43B170FC3AEC9AA58CD92709D388E166" + - "AB4ADFD5A4876DC47C17DE51FDD42A32AF672515B6A81E7ABECFE748912B321A" + - "FD0CBF4880298DD79403900A4002B5B436230EB6E49192DF49FAE0F6B60EBA75" + - "A54592587C141AD3B319129006367E9532861C2893E7A2D0D2832DF4377C3184" + - "5CB02A1D020282C3D2B7F77221F71FEA7FF0A988FEF15C4B2F6637159EEC5752" + - "D8A7F4AB971117666A977370E754A4EB0DC52D6E8901DC60FCD87B5B6EF9A91A" + - "F8D9A4E11E2FFDAB55FC11AF6EEB5B36557FC8945A1E291B7FF8931BE4A57B8E" + - "68F04B9D4A9A02FC61AE913F2E2DDBEE42C065F4D30F568834D5BB15FDAF691F" + - "197EF6C25AE87D8E968C6D15351093AAC4813A8E7B191F77E6B19146F839A43E" + - "2F40DE8BE28EB22C0272545BADF3BD396D383B8DA8388147100B347999DDC412" + - "5AB0AA1159BC6776BD2BF51534C1B40522D41466F414BDE333226973BAD1E6D5" + - "7639D30AD94BEA1F6A98C047F1CE1294F0067B771778D59E7C722C73C2FF100E" + - "13603206A694BF0ED07303BE0655DC984CA29893FD0A088B122B67AABDC803E7" + - "3E5729E868B1CA26F5D05C818D9832C70F5992E7D15E14F9775C6AD24907CF2F" + - "211CF87167861F94DCF9E3D365CB600B336D93AD44B8B89CA24E59C1F7812C84" + - "DBE3EE57A536ED0D4BF948F7662E5BCBBB388C72243CFCEB720852D5A4A52F01" + - "8C2C087E4DB43410FE9ABA3A8EF737B6E8FFDB1AB9832EBF606ED5E4BD62A86B" + - "BCAE115C67682EDEA93E7845D0D6962C146B411F7784545851D2F327BEC7E434" + - "4D68F137CDA217A3F0FF3B752A34C3B5339C79CB8E1AC690C038E85D6FC13379" + - "090198D3555394D7A2159A23BD5EEF06EB0BCC729BB29B5BE911D02DA78FDA56" + - "F035E508C722139AD6F25A6C84BED0E98893370164B033A2B52BC40D9BF5163A" + - "F9650AB55EABB23370492A7D3A87E17C11B4D07A7296273F33069C835FD208BA" + - "8F989A3CF8659054E2CCCFB0C983531DC6590F27C4A1D2C3A780FE945F7E52BB" + - "9FFD2E324640E3E348541A620CD62605BBDB284AF97C621A00D5D1D2C31D6BD6" + - "1149137B8A0250BC426417A92445A52574E999FB9102C16671914A1542E92DDE" + - "541B2A0457112AF936DA84707CADFEA43BFEDAE5F58859908640420948086E57" + - "FFD1B867C241D40197CB0D4AD58BB69B3724772E0079406A1272858AAA620668" + - "F696955102639F3E95CFFC637EAF8AB54F0B5B2131AB292438D06E15F3826352" + - "DEDC653DA5A4AACE2BB97061A498F3B6789A2310471B32F91A6B7A9944DDBB70" + - "31525B3AE387214DC85A1C7749E9168F41272680D0B3C331D61175F23B623EEC" + - "40F984C35C831268036680DE0821E5DEE5BB250C6984775D49B7AF94057371DB" + - "72F81D2B0295FC6A51BCD00A697649D4346FDD59AC0DFAF21BFCC942C23C6134" + - "FFBA2ABABC141FF700B52C5B26496BF3F42665A5B71BAC7F0C19870BD9873890" + - "239C578CDDD8E08A1B0A429312FB24F151A11E4D180359A7FA043E8155453F67" + - "265CB2812B1C98C144E7675CFC86413B40E35445AE7710227D13DC0B5550C870" + - "10B363C492DA316FB40D3928570BF71BF47638F1401549369B1255DB080E5DFA" + - "18EA666B9ECBE5C9768C06B3FF125D0E94B98BB24B4FD44E770B78D7B336E021" + - "4FD72E77C1D0BE9F313EDCD147957E3463C62E753C10BB98584C85871AAEA9D1" + - "F397FE9F1A639ADE31D40EAB391B03B588B8B031BCAC6C837C61B06E4B745052" + - "474D33531086519C39EDD6310F3079EB5AC83289A6EDCBA3DC97E36E837134F7" + - "303B301F300706052B0E03021A0414725663844329F8BF6DECA5873DDD8C96AA" + - "8CA5D40414DF1D90CD18B3FBC72226B3C66EC2CB1AB351D4D2020207D0").HexToByteArray(); - - private static readonly byte[] ChainPfxBytes_TripleDESContentEncryption = ( - "30821306020103308212C206092A864886F70D010701A08212B3048212AF3082" + - "12AB308203C406092A864886F70D010701A08203B5048203B1308203AD308203" + - "A9060B2A864886F70D010C0A0102A08202B6308202B2301C060A2A864886F70D" + - "010C0103300E0408B43CDBD7536FD7E2020207D0048202904EC18175B7B959AD" + - "8BF52CCEE41D754471CD84C99404E07672BA2F1BF60C59AFE7D32B64EF1EDB60" + - "326F06991D20797C598F2BD192C258C0ED726382C1B7B8A12781CA64AB72320F" + - "CAFD864D9E3CA580BA961DEF71477BA4463F8E56C9FDE471B7E0D252988256CE" + - "8780A6E843B46D9D829EF20321F8B207C1410D85E04F0CDD9CA2E7C25ED661F2" + - "820C76495A900A83D5485D0CA93E6B89D3088C727EF6A702F63C648C9271C292" + - "FCF716E755F23EB65376E1F413E12946E3A43B6AB419E5076ED523CE4DC6DF9B" + - "2C8E461A3243708764B8655AA2763A55011A558778E2A31072B33DDD4D04A86E" + - "89C416F4CFA65E6FADF390F2EE7DC8764428F85C3BB7553478A03F4807949722" + - "77077B65A799E862C7E67A4EEF0AAEC1C36ED7F1408E3B0B53DB24A76CE80966" + - "2DD472121B64740EDC3306AED1CD9E200011999B31A3EEB56F7B34880FD5918C" + - "7116607366E6C2514BAD741BBF25B10124B01F848162F405572C3781049E9AED" + - "A5294FB5AC6171E885311E7CB7001A8B83A86964B6D407096309322051ED60AF" + - "1E2D76A534D44ECC0895BF4984FA2F05625F2AF2B05B82794007705DE256914E" + - "F66AB1DD8726CE09E7950B8CE37D5138951620EEE7DF1F6957B62610BBCC53CA" + - "DE6FB33DBA5709DAC3D83E92028391132535B2C8A1B96E153FBF18D008A9F495" + - "E022B8B1DD77FE3456388D450E7026246F0246408B96F995C60930F912463E34" + - "45B6803F5BDBAC95C6CFF02893C8D57FFE091E7C520B971D24F6050D0ADA8E52" + - "F9F64D68DA38891A36AAF0231B35873CD90894393835623D6D3E898361AD17E3" + - "BC99FDBBDEF9F82763740468DE692A3FA094E1683E651DAEAD3267DBD030C930" + - "5C3A4E4AF5AB61B483BFFA4998C5F708FE4CF8D3435EA38B1512D56310ED58CD" + - "65AAADB6A64C7C623181DF301306092A864886F70D0109153106040401000000" + - "305B06092A864886F70D010914314E1E4C007B00340031003700380032003500" + - "340041002D0036003300440042002D0034003400380045002D00410030003700" + - "41002D003200320033003600420038003900450041003800350045007D306B06" + - "092B0601040182371101315E1E5C004D006900630072006F0073006F00660074" + - "00200045006E00680061006E006300650064002000430072007900700074006F" + - "0067007200610070006800690063002000500072006F00760069006400650072" + - "002000760031002E003030820EDF06092A864886F70D010706A0820ED030820E" + - "CC02010030820EC506092A864886F70D010701301C060A2A864886F70D010C01" + - "03300E040894BE093240AADB8A020207D080820E989C5F83126495753056D861" + - "A825CCB15575B04D71869B2A3685B26E535C4748F62EE23ED1C6ADC3D35DC39D" + - "0EB75C11742BBB7F36A7BA52D63EEA6F1398BBF1361EB98CC06C9EF405040888" + - "35B4063525FB2AA0715E88E62D44746404681304F38E5E2CD27BBB14571885CA" + - "DEAE50AB9F3956B3332B0C4CED90F0C8A7421532D938015861AC6EDF0F3007E7" + - "FFB9786DC56E299D5D9277BB7A29902863CD606CED8494932607AA202ECACB5F" + - "BB23AB7E824FEBED60CD76B462417ED099CAF56CE946FA5B5906341618ADA2EB" + - "3C739E332EE43ED6D16A971D2F0AF47941BABB3251250108BA6E4C0909B27808" + - "6CAA4509DFE267F9ED352330576EC22D28F11607B9805D5A82CF5A4B45C702F1" + - "A55B52D9A0E1A70543746ED457E39CAA42D66BF1EA8EE7C4DEE500E8B35849F4" + - "797583651465B5088C8F83665D05087DB9E68A5D6AFB0C489BA255A1E639E540" + - "977C53CA9F250E2C5F2B5822A7F706BD3C45DC8BCB546CB1184D7CB088701D0B" + - "53845736CCDC4BAD654108459415CC5DA8718E15E319473E5EF8107709B4EF68" + - "395E164987D467D39EAD4A049EB314F30505A072D52FB9902AF3FDC76F607D4C" + - "5068F4AD2CDB0E734E5EC18B72A44DD009EB02AB3C9531CF2BFBD6E3B40B3A4C" + - "2AE0AF8B04F6D2038102869A3952FEB7F4B82A03BC180D201FAC040785E02DA7" + - "261E1A2D74BBE95D13DA0C3F07744D2A159E4BBACB8FF4D7CC6306D0DE3E1D12" + - "6A9BDE0DFB9ED71D2C0B3827B0087D1FFBAE639AFD6EBB3C3C7138602C582F01" + - "404D997BB66EDC324CF43EB0315C13C8917EB88C318351E77CD5168EB8D05F04" + - "1F375ED6149E797497DDB6B549CDC7E24375E90746143F95474FC5A72E1B097C" + - "85C61BA7C5CCFC573B0DC308EA08BC1E21930DE314934CC4A7BBCB6C83165B1A" + - "2D7A34C94B165879FABCEA245919A9CE9681DB0FA6B4278B6E757047661C6604" + - "CB66E95BE6C5BE1F43CD6CCBC6CD9687310DB42C64E6BAE6224A219AD34E4918" + - "81007A7A69BCD4FC7077D0B469E90F4FE7FA584D8F7384C2013C94502AB0D61E" + - "24341FFC2894FBB0C807D8F9228E15DDE47E20C909B6BA802C1C3C4FF4E6FFE6" + - "7EAAC94290681A250FD681305C77FF881142C04FA1262D0B29E233AEB0DBAF7A" + - "70C8EAB8257BF553CDCCB5E3D06617410E6CABF19B24F6943597FA353D8861BE" + - "BF728864E17A54B37BE81FBF25FA5C3F5EDDADBD633274D226A6DD9BB2D427DC" + - "B283A29A691BC89BADFF80A5B852EA64521704974CDDC0599E94EF1BC259EC23" + - "3A1310F791878294DB5F8D7B2C890B6EBAE3E517EB1138D0A00AC762BD27D273" + - "2D66BDF971C8E87ED10CE4880D0460C9EFEBF58DE7C65AE59BF6FBAD9B5B32A2" + - "49DD6B9B3F9821F584004EA9DFB54C94B54B7A756D531F565A84A60747A47E6D" + - "D2A5A0F2F16BA15D4F5AD0CA110D26E629AD7CF4E6812948138FEB7DF3104AD1" + - "CFDC9F6FD6F64EB6A009D27E4D63C9A212ABF61CABEE2E290691913AE20E4966" + - "D0B8FF6E97342A6297A07A72796D7466AAA8E9336F8F7796EEB4CE624E83335C" + - "F3DC78C97A67BD868C9EE8C8CA3B756ACF93B7798DF470FAB7DE524C751631BC" + - "4040483C2C53CC691E13122C1BFDC36D83F0FEFD1DDC1ED9042613406FA476A0" + - "F97969213DE68568FA15F04F1AED1481BBDAC0BD9FDFF6FAB9C2E130024CEB6F" + - "358809FBCB94EC1454E46D2E369923D1B93F18F8B584B6CC2820E26C605C7BB1" + - "F50595DD9F0E724DB23359E9B7045DE35E46ACFE07AF27E1D111551689CC0352" + - "AD474F0159B8645408512B03E5EEDE3302513FD665666FB7D1D210F426E18E1E" + - "2B5616EBAB322A306338C784CAECA267F3592D217CE17D318A0A68A6D19CE60D" + - "90BEAC809F8F3DCEDB427B51CE604CBE814597A71311C9E650D64FA1BFECB801" + - "D8EE3C1607E02F641C31212AC6ABA56922A912A6E0C5284C547CA80A94B59C46" + - "443510EDEAB9388D422014C472394EAE7DC125C3A6C8B707AC3552EE52153B65" + - "F7FC989093721329910D4B5C92398FB13644A8C784380E82E73AF03A478C7875" + - "1A7E8E9B6E0D1ED589190ADF388C7A82BC451BAC4AD363C5B6889EED91DF2FB8" + - "A8DCC6810D188570CADDBA85BA935E272026AF23611D1FEE694E4475BF9E68E0" + - "8E8A88F2202A153259206FADAE4346D3BAEB59387715FC5D05E7D5F915184A38" + - "81609998068E7EB6A843A719A5ACD41A82364358F4B6867F6B9EA0175703A431" + - "00742F88AC0769B03071D7C3BF345C95806EC951F2B7C55AF7FE52174E37219B" + - "59309BAA09EEBA299F78E53F375D549C560E5D3F1848113A2E2C8B6CE65892C1" + - "F55E3822864E6D95E055BA5CBB0098BB922C3188E3EB56B9EAA93030B2E8587E" + - "9EF411F725D57F1A12D6F911413D91C78334963679083C308BCAD4C8DBE43116" + - "BCD5C74AA6B22FE9904329DF6E8EFF458BA7659ED9A043302009D6BBED2EC674" + - "EE661AB7789058CE56918A7F31A4827667338C7B12A958C239300FEE3D151F46" + - "D595707B76707B247BC37457DC1498818F6B56D9CABB72682F65BE500F74D00F" + - "A7B1144FD05FBAEB12DD83014F1A01367543761DA8FFF0B78072E5DCC14A561C" + - "5A08F509FEFF2DA20C5F342BC7E7C498417BF758B5DB26732DE45034FE952AC7" + - "6FDB80A526066505610E8753BC5521BB47333C2EBBD42B959010EA18FB5F8194" + - "A8BCD6FC6E5B8059F8C14DE002E3F78AC453C4E14FCA4DF09EFDBC992ABAEA34" + - "A7E6648B6E7F4B7D7676B2DFDFB780A275456ABC2E0C6409FBD017A10E08D3D7" + - "D8F37CF3C451BC73CD28C77F3BE2E1812D5FA6DBB842B60FBA1A5B718BC43A18" + - "2D9BAFC7C2899D8BCBBED51B2B252AC90EAADEFEA04E801F3AEB7766D92C70A5" + - "D4D05546F698B191B080135CE040A626F91589088E14E4AC494B2C4EB5CE6D1C" + - "0B827E412001F6C366A9FCD29C1663D4003AF2BE77733DA806787E4AC39DBE07" + - "9F13344B958A38EB6A0D7DB28F51241D1820316D33A23233766D57ADC6C7383A" + - "16FDC152C4100CFB459D37915478DE03A6BCF64CF513EB650D8390077C7EE12E" + - "0CC2EC447B1DECC5C675F0A0912FAD96727942E3F76490A319B65689D25212E6" + - "574AFE8AC6E558FA17D324E8ACAE80DA5803CC17C10BEB887F5B8783C37D1EF8" + - "4382D5DEEBCF3D2CBCD4420C1B5B304C56F60EF06A58C632CBB9435932E0C605" + - "C46291952EC08261C5847B2785E5E3FCCE6756BCE869E5E564FFA6D92F494099" + - "BCC08F2BB84ADAE91F000D134E918DE947DBB217EE5D24A42E68C1A6F14B3C37" + - "0F0E0E829FE29F91DF01B205BA209736A979D06DA905E52569D19F460CD3C9FB" + - "AC7D428554D7BE5766D246DAF927F32EEE76D88B29D24EBB8F20C0EE01661579" + - "568B20F08523893DA596F0418EF2565CFC90A2AE97860D77F43806B1493FB1B9" + - "873AF46DDCE8112CA98141AEB2A3CDCE6FB90545CDA7215CB5AFD0E1FA1CFA01" + - "9B8A95E552F7B4C174F4B5635430500DFF11851F5084233AE8D9CBB3416C7AF6" + - "128DAFA764C2032E4E6FD45C2C8ED83436CA673C7F0A1310B50A59E99A4DA328" + - "A179D29128C74B14FEACF2C6D0B0E079B7A700CD4D5D92E50CFEBB5633055C87" + - "5F57D2063CE4A5A4BDE5043F9F069C8051A5F2803D242A67F61FAAE19CB01827" + - "7132484137ED45B02FA919C399AA6F7DC4EC9D116C7A12803F8A6589AD801FD0" + - "154387C09072D48D4D9F4DD70E3319B61AE35F6B8BDF77C796C12FCFCA7D3F5B" + - "AB3C639D79AF8CA579944D2EA9F1D3F8B1E872C48384FA089EE74E044255BEA7" + - "79C79FE7826D7BAADD6CCA0B53912762CCE890A419E94B9CEC3152A46D1D1158" + - "84671CAF82DB53B3E1D23934C58AADC97E84589F2E0745DB62B757C4061416BB" + - "5DA650CAF0D687BAE96DF8521AE185B3122C13E342F7FFC985B4ADB2BE8E412E" + - "A4ABF9D39DDFAB7FACE1BC319F725F9982944F8B34B4F563BC4E138C99B7EA49" + - "05960529F79E4124B52508E7F20547FDBE7DE03692A5B249AD0E0739E34CE987" + - "9423CEEECBAFF243EA4EECAF7E7DA782306FA771AC2D4CF3EDB33255382CA410" + - "F8DA4B467012CF4322BBA86138A75D1252C1C08A5E24C8BFB57DF48C860B93A4" + - "55C6A428DEEA8EDB91272BFA03C73E80987BB94F8C0CB504552C8FB0C7A3003A" + - "62F678A4424C0B8AA1A9AAEC747786C051FCD407F1CE56A28F3ED28B2BCDADE0" + - "5724FE94FBF67EA6BA4030832D4B8DB97DEA9496CFEDC85E71AA35524DB4331E" + - "742BE36CC7EE1DB92AA57467F23AB8AA73FC23B32E5749C8A43915A8E5A1AC3D" + - "3E031CB786B4070195D88C98E0075B713BB71D77D6F9E50FB65F18B0552B1F83" + - "B4158B304014C94B266C52781521388E3469FF78E8128EB26D8FDFFF75FEF45B" + - "C0D2241851E976F3B0A75E6A9BED401095A093AEB7F6F8EFC8338F3CEC40B71B" + - "DB86433E7E0617FBB354D37ABBC4C998E8E4E3E9514C2BE06D65109772DCE808" + - "CDB9AE319F35E681B83D29E01BACFB0E02619DC8C1D390C35C87299D40D8396C" + - "0344242A963987ABAFA357634E8398C0CAFFCB5AB5F5EAF395B38CBBF87F6AB1" + - "0331837BB08FEEC14A16B22B1B8DECF5C79DB8F27D4DCE805FB15E8823E35529" + - "136BDB216A3E48EA042DF59237C65048DA7CA3E06FDE9EA8EF04124A8DBF4ED2" + - "720A4B9F99B93659B220072A4B88EC093E3E69CE964837E413A145EF08E9265A" + - "946653112C46CBA62D2425AF9A56B6F88A30802BF1174E854EEA5585AC4CAC24" + - "2A5BFDF5585659C2FF9EDFAF71594DB4A9D81188350AD5F30CBA9FED3B023ACC" + - "9424D1FBAB4FE8B1562544C90EA2599E8FAB028A244EAF3FC6FF72EE0029B277" + - "6E12786BD3783E51CF2AFC8AE484C205576F6E4EB216F7D4573D0EBCFA67BC3E" + - "999DE7390EE74F5E73B570DF80D8145DE6FE96A2D4D12D758A6B134303E7F361" + - "500377765CE0F5D3ACDB9ED3FEEC304CD10C0C19872A426A077B0D57D4CC8969" + - "81EAD835C2EAABDA8A6537E19B034638F1B57F544D8B5CAF9AB8754E45CBCCFB" + - "C1AC47A4AF3E225D96FCC828979B4A6923294E1E9F75C3F35354C9BE8C9FF95C" + - "805FF2E108FDC596965FC4837D0EC02C40F1C343B21C96110FB4AAB46A840984" + - "8AF62B81FA593FD49DA006B3B44CB16F7DAF32603742990FEE02569EEC8CC042" + - "EAEBAFC01F28DF096A3CD961D8F4975FD19CD8FE4F64D46D14A53BEFC60B3CC4" + - "8CD8DBADEDE41EFBF9DDDC5C583F0E66F577192D3B31D2254B6A6991B1BD684B" + - "6DECD4E8F232767A46BBBD83E9BA5CDE1EAA09BBEF497FAC94BE5DD435E364AA" + - "8A19E9413C47214C2D285A6B20303B301F300706052B0E03021A04147E4F196F" + - "6D0A4EF84A2C08674CDDF6899F7672A50414206C1AAAFFB1E282745C91F6BF5F" + - "0E87036AC61E020207D0").HexToByteArray(); - - public static readonly byte[] ChainPfxBytes = - PlatformSupport.IsRC2Supported - ? ChainPfxBytes_RC2ContentEncryption - : ChainPfxBytes_TripleDESContentEncryption; - - internal static readonly byte[] Pkcs7ChainDerBytes = ( - "30820E1606092A864886F70D010702A0820E0730820E030201013100300B0609" + - "2A864886F70D010701A0820DEB3082050B30820474A003020102020A15EAA83A" + - "000100009291300D06092A864886F70D010105050030818131133011060A0992" + - "268993F22C6401191603636F6D31193017060A0992268993F22C64011916096D" + - "6963726F736F667431143012060A0992268993F22C6401191604636F72703117" + - "3015060A0992268993F22C64011916077265646D6F6E643120301E0603550403" + - "13174D532050617373706F7274205465737420537562204341301E170D313330" + - "3131303231333931325A170D3331313231333232323630375A308185310B3009" + - "060355040613025553310B30090603550408130257413110300E060355040713" + - "075265646D6F6E64310D300B060355040A130454455354310D300B060355040B" + - "130454455354311330110603550403130A746573742E6C6F63616C3124302206" + - "092A864886F70D010901161563726973706F70406D6963726F736F66742E636F" + - "6D30819F300D06092A864886F70D010101050003818D0030818902818100B406" + - "851089E9CF7CDB438DD77BEBD819197BEEFF579C35EF9C4652DF9E6330AA7E2E" + - "24B181C59DA4AF10E97220C1DF99F66CE6E97247E9126A016AC647BD2EFD136C" + - "31470C7BE01A20E381243BEEC8530B7F6466C50A051DCE37274ED7FF2AFFF4E5" + - "8AABA61D5A448F4A8A9B3765D1D769F627ED2F2DE9EE67B1A7ECA3D288C90203" + - "010001A38202823082027E300E0603551D0F0101FF0404030204F0301D060355" + - "1D250416301406082B0601050507030106082B06010505070302301D0603551D" + - "0E04160414FB3485708CBF6188F720EF948489405C8D0413A7301F0603551D23" + - "0418301680146A6678620A4FF49CA8B75FD566348F3371E42B133081D0060355" + - "1D1F0481C83081C53081C2A081BFA081BC865F687474703A2F2F707074657374" + - "73756263612E7265646D6F6E642E636F72702E6D6963726F736F66742E636F6D" + - "2F43657274456E726F6C6C2F4D5325323050617373706F727425323054657374" + - "25323053756225323043412831292E63726C865966696C653A2F2F5C5C707074" + - "65737473756263612E7265646D6F6E642E636F72702E6D6963726F736F66742E" + - "636F6D5C43657274456E726F6C6C5C4D532050617373706F7274205465737420" + - "5375622043412831292E63726C3082013806082B060105050701010482012A30" + - "82012630819306082B06010505073002868186687474703A2F2F707074657374" + - "73756263612E7265646D6F6E642E636F72702E6D6963726F736F66742E636F6D" + - "2F43657274456E726F6C6C2F70707465737473756263612E7265646D6F6E642E" + - "636F72702E6D6963726F736F66742E636F6D5F4D5325323050617373706F7274" + - "2532305465737425323053756225323043412831292E63727430818D06082B06" + - "01050507300286818066696C653A2F2F5C5C70707465737473756263612E7265" + - "646D6F6E642E636F72702E6D6963726F736F66742E636F6D5C43657274456E72" + - "6F6C6C5C70707465737473756263612E7265646D6F6E642E636F72702E6D6963" + - "726F736F66742E636F6D5F4D532050617373706F727420546573742053756220" + - "43412831292E637274300D06092A864886F70D0101050500038181009DEBB8B5" + - "A41ED54859795F68EF767A98A61EF7B07AAC190FCC0275228E4CAD360C9BA98B" + - "0AE153C75522EEF42D400E813B4E49E7ACEB963EEE7B61D3C8DA05C183471544" + - "725B2EBD1889877F62134827FB5993B8FDF618BD421ABA18D70D1C5B41ECDD11" + - "695A48CB42EB501F96DA905471830C612B609126559120F6E18EA44830820358" + - "308202C1A00302010202101B9671A4BC128B8341B0E314EAD9A191300D06092A" + - "864886F70D01010505003081A13124302206092A864886F70D01090116156173" + - "6D656D6F6E406D6963726F736F66742E636F6D310B3009060355040613025553" + - "310B30090603550408130257413110300E060355040713075265646D6F6E6431" + - "123010060355040A13094D6963726F736F667431163014060355040B130D5061" + - "7373706F727420546573743121301F060355040313184D532050617373706F72" + - "74205465737420526F6F74204341301E170D3035303132363031333933325A17" + - "0D3331313231333232323630375A3081A13124302206092A864886F70D010901" + - "161561736D656D6F6E406D6963726F736F66742E636F6D310B30090603550406" + - "13025553310B30090603550408130257413110300E060355040713075265646D" + - "6F6E6431123010060355040A13094D6963726F736F667431163014060355040B" + - "130D50617373706F727420546573743121301F060355040313184D5320506173" + - "73706F7274205465737420526F6F7420434130819F300D06092A864886F70D01" + - "0101050003818D0030818902818100C4673C1226254F6BBD01B01D21BB05264A" + - "9AA5B77AC51748EAC52048706DA6B890DCE043C6426FC44E76D70F9FE3A4AC85" + - "5F533E3D08E140853DB769EE24DBDB7269FABEC0FDFF6ADE0AA85F0085B78864" + - "58E7585E433B0924E81600433CB1177CE6AD5F2477B2A0E2D1A34B41F6C6F5AD" + - "E4A9DD7D565C65F02C2AAA01C8E0C10203010001A3818E30818B301306092B06" + - "0104018237140204061E0400430041300B0603551D0F040403020186300F0603" + - "551D130101FF040530030101FF301D0603551D0E04160414F509C1D6267FC39F" + - "CA1DE648C969C74FB111FE10301206092B060104018237150104050203010002" + - "302306092B0601040182371502041604147F7A5208411D4607C0057C98F0C473" + - "07010CB3DE300D06092A864886F70D0101050500038181004A8EAC73D8EA6D7E" + - "893D5880945E0E3ABFC79C40BFA60A680CF8A8BF63EDC3AD9C11C081F1F44408" + - "9581F5C8DCB23C0AEFA27571D971DBEB2AA9A1B3F7B9B0877E9311D36098A65B" + - "7D03FC69A835F6C3096DEE135A864065F9779C82DEB0C777B9C4DB49F0DD11A0" + - "EAB287B6E352F7ECA467D0D3CA2A8081119388BAFCDD25573082057C308204E5" + - "A003020102020A6187C7F200020000001B300D06092A864886F70D0101050500" + - "3081A13124302206092A864886F70D010901161561736D656D6F6E406D696372" + - "6F736F66742E636F6D310B3009060355040613025553310B3009060355040813" + - "0257413110300E060355040713075265646D6F6E6431123010060355040A1309" + - "4D6963726F736F667431163014060355040B130D50617373706F727420546573" + - "743121301F060355040313184D532050617373706F7274205465737420526F6F" + - "74204341301E170D3039313032373231333133395A170D333131323133323232" + - "3630375A30818131133011060A0992268993F22C6401191603636F6D31193017" + - "060A0992268993F22C64011916096D6963726F736F667431143012060A099226" + - "8993F22C6401191604636F727031173015060A0992268993F22C640119160772" + - "65646D6F6E643120301E060355040313174D532050617373706F727420546573" + - "742053756220434130819F300D06092A864886F70D010101050003818D003081" + - "8902818100A6A4918F93C5D23B3C3A325AD8EC77043D207A0DDC294AD3F5BDE0" + - "4033FADD4097BB1DB042B1D3B2F26A42CC3CB88FA9357710147AB4E1020A0DFB" + - "2597AB8031DB62ABDC48398067EB79E4E2BBE5762F6B4C5EA7629BAC23F70269" + - "06D46EC106CC6FBB4D143F7D5ADADEDE19B021EEF4A6BCB9D01DAEBB9A947703" + - "40B748A3490203010001A38202D7308202D3300F0603551D130101FF04053003" + - "0101FF301D0603551D0E041604146A6678620A4FF49CA8B75FD566348F3371E4" + - "2B13300B0603551D0F040403020186301206092B060104018237150104050203" + - "010001302306092B060104018237150204160414A0A485AE8296EA4944C6F6F3" + - "886A8603FD07472C301906092B0601040182371402040C1E0A00530075006200" + - "430041301F0603551D23041830168014F509C1D6267FC39FCA1DE648C969C74F" + - "B111FE103081D60603551D1F0481CE3081CB3081C8A081C5A081C28663687474" + - "703A2F2F70617373706F72747465737463612E7265646D6F6E642E636F72702E" + - "6D6963726F736F66742E636F6D2F43657274456E726F6C6C2F4D532532305061" + - "7373706F727425323054657374253230526F6F7425323043412831292E63726C" + - "865B66696C653A2F2F50415353504F52545445535443412E7265646D6F6E642E" + - "636F72702E6D6963726F736F66742E636F6D2F43657274456E726F6C6C2F4D53" + - "2050617373706F7274205465737420526F6F742043412831292E63726C308201" + - "4406082B06010505070101048201363082013230819A06082B06010505073002" + - "86818D687474703A2F2F70617373706F72747465737463612E7265646D6F6E64" + - "2E636F72702E6D6963726F736F66742E636F6D2F43657274456E726F6C6C2F50" + - "415353504F52545445535443412E7265646D6F6E642E636F72702E6D6963726F" + - "736F66742E636F6D5F4D5325323050617373706F727425323054657374253230" + - "526F6F7425323043412832292E63727430819206082B06010505073002868185" + - "66696C653A2F2F50415353504F52545445535443412E7265646D6F6E642E636F" + - "72702E6D6963726F736F66742E636F6D2F43657274456E726F6C6C2F50415353" + - "504F52545445535443412E7265646D6F6E642E636F72702E6D6963726F736F66" + - "742E636F6D5F4D532050617373706F7274205465737420526F6F742043412832" + - "292E637274300D06092A864886F70D010105050003818100C44788F8C4F5C2DC" + - "84976F66417CBAE19FBFA82C257DA4C7FED6267BC711D113C78B1C097154A62A" + - "B462ADC84A434AEBAE38DEB9605FAB534A3CAF7B72C199448E58640388911296" + - "115ED6B3478D0E741D990F2D59D66F12E58669D8983489AB0406E37462164B56" + - "6AA1D9B273C406FA694A2556D1D3ACE723382C19871B8C143100").HexToByteArray(); - - internal static readonly byte[] Pkcs7ChainPemBytes = ByteUtils.AsciiBytes( - @"-----BEGIN PKCS7----- -MIIOFgYJKoZIhvcNAQcCoIIOBzCCDgMCAQExADALBgkqhkiG9w0BBwGggg3rMIIF -CzCCBHSgAwIBAgIKFeqoOgABAACSkTANBgkqhkiG9w0BAQUFADCBgTETMBEGCgmS -JomT8ixkARkWA2NvbTEZMBcGCgmSJomT8ixkARkWCW1pY3Jvc29mdDEUMBIGCgmS -JomT8ixkARkWBGNvcnAxFzAVBgoJkiaJk/IsZAEZFgdyZWRtb25kMSAwHgYDVQQD -ExdNUyBQYXNzcG9ydCBUZXN0IFN1YiBDQTAeFw0xMzAxMTAyMTM5MTJaFw0zMTEy -MTMyMjI2MDdaMIGFMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcT -B1JlZG1vbmQxDTALBgNVBAoTBFRFU1QxDTALBgNVBAsTBFRFU1QxEzARBgNVBAMT -CnRlc3QubG9jYWwxJDAiBgkqhkiG9w0BCQEWFWNyaXNwb3BAbWljcm9zb2Z0LmNv -bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtAaFEInpz3zbQ43Xe+vYGRl7 -7v9XnDXvnEZS355jMKp+LiSxgcWdpK8Q6XIgwd+Z9mzm6XJH6RJqAWrGR70u/RNs -MUcMe+AaIOOBJDvuyFMLf2RmxQoFHc43J07X/yr/9OWKq6YdWkSPSoqbN2XR12n2 -J+0vLenuZ7Gn7KPSiMkCAwEAAaOCAoIwggJ+MA4GA1UdDwEB/wQEAwIE8DAdBgNV -HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFPs0hXCMv2GI9yDv -lISJQFyNBBOnMB8GA1UdIwQYMBaAFGpmeGIKT/ScqLdf1WY0jzNx5CsTMIHQBgNV -HR8EgcgwgcUwgcKggb+ggbyGX2h0dHA6Ly9wcHRlc3RzdWJjYS5yZWRtb25kLmNv -cnAubWljcm9zb2Z0LmNvbS9DZXJ0RW5yb2xsL01TJTIwUGFzc3BvcnQlMjBUZXN0 -JTIwU3ViJTIwQ0EoMSkuY3JshllmaWxlOi8vXFxwcHRlc3RzdWJjYS5yZWRtb25k -LmNvcnAubWljcm9zb2Z0LmNvbVxDZXJ0RW5yb2xsXE1TIFBhc3Nwb3J0IFRlc3Qg -U3ViIENBKDEpLmNybDCCATgGCCsGAQUFBwEBBIIBKjCCASYwgZMGCCsGAQUFBzAC -hoGGaHR0cDovL3BwdGVzdHN1YmNhLnJlZG1vbmQuY29ycC5taWNyb3NvZnQuY29t -L0NlcnRFbnJvbGwvcHB0ZXN0c3ViY2EucmVkbW9uZC5jb3JwLm1pY3Jvc29mdC5j -b21fTVMlMjBQYXNzcG9ydCUyMFRlc3QlMjBTdWIlMjBDQSgxKS5jcnQwgY0GCCsG -AQUFBzAChoGAZmlsZTovL1xccHB0ZXN0c3ViY2EucmVkbW9uZC5jb3JwLm1pY3Jv -c29mdC5jb21cQ2VydEVucm9sbFxwcHRlc3RzdWJjYS5yZWRtb25kLmNvcnAubWlj -cm9zb2Z0LmNvbV9NUyBQYXNzcG9ydCBUZXN0IFN1YiBDQSgxKS5jcnQwDQYJKoZI -hvcNAQEFBQADgYEAneu4taQe1UhZeV9o73Z6mKYe97B6rBkPzAJ1Io5MrTYMm6mL -CuFTx1Ui7vQtQA6BO05J56zrlj7ue2HTyNoFwYNHFURyWy69GImHf2ITSCf7WZO4 -/fYYvUIauhjXDRxbQezdEWlaSMtC61AfltqQVHGDDGErYJEmVZEg9uGOpEgwggNY -MIICwaADAgECAhAblnGkvBKLg0Gw4xTq2aGRMA0GCSqGSIb3DQEBBQUAMIGhMSQw -IgYJKoZIhvcNAQkBFhVhc21lbW9uQG1pY3Jvc29mdC5jb20xCzAJBgNVBAYTAlVT -MQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDESMBAGA1UEChMJTWljcm9z -b2Z0MRYwFAYDVQQLEw1QYXNzcG9ydCBUZXN0MSEwHwYDVQQDExhNUyBQYXNzcG9y -dCBUZXN0IFJvb3QgQ0EwHhcNMDUwMTI2MDEzOTMyWhcNMzExMjEzMjIyNjA3WjCB -oTEkMCIGCSqGSIb3DQEJARYVYXNtZW1vbkBtaWNyb3NvZnQuY29tMQswCQYDVQQG -EwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQxEjAQBgNVBAoTCU1p -Y3Jvc29mdDEWMBQGA1UECxMNUGFzc3BvcnQgVGVzdDEhMB8GA1UEAxMYTVMgUGFz -c3BvcnQgVGVzdCBSb290IENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDE -ZzwSJiVPa70BsB0huwUmSpqlt3rFF0jqxSBIcG2muJDc4EPGQm/ETnbXD5/jpKyF -X1M+PQjhQIU9t2nuJNvbcmn6vsD9/2reCqhfAIW3iGRY51heQzsJJOgWAEM8sRd8 -5q1fJHeyoOLRo0tB9sb1reSp3X1WXGXwLCqqAcjgwQIDAQABo4GOMIGLMBMGCSsG -AQQBgjcUAgQGHgQAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G -A1UdDgQWBBT1CcHWJn/Dn8od5kjJacdPsRH+EDASBgkrBgEEAYI3FQEEBQIDAQAC -MCMGCSsGAQQBgjcVAgQWBBR/elIIQR1GB8AFfJjwxHMHAQyz3jANBgkqhkiG9w0B -AQUFAAOBgQBKjqxz2Optfok9WICUXg46v8ecQL+mCmgM+Ki/Y+3DrZwRwIHx9EQI -lYH1yNyyPArvonVx2XHb6yqpobP3ubCHfpMR02CYplt9A/xpqDX2wwlt7hNahkBl -+Xecgt6wx3e5xNtJ8N0RoOqyh7bjUvfspGfQ08oqgIERk4i6/N0lVzCCBXwwggTl -oAMCAQICCmGHx/IAAgAAABswDQYJKoZIhvcNAQEFBQAwgaExJDAiBgkqhkiG9w0B -CQEWFWFzbWVtb25AbWljcm9zb2Z0LmNvbTELMAkGA1UEBhMCVVMxCzAJBgNVBAgT -AldBMRAwDgYDVQQHEwdSZWRtb25kMRIwEAYDVQQKEwlNaWNyb3NvZnQxFjAUBgNV -BAsTDVBhc3Nwb3J0IFRlc3QxITAfBgNVBAMTGE1TIFBhc3Nwb3J0IFRlc3QgUm9v -dCBDQTAeFw0wOTEwMjcyMTMxMzlaFw0zMTEyMTMyMjI2MDdaMIGBMRMwEQYKCZIm -iZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MRQwEgYKCZIm -iZPyLGQBGRYEY29ycDEXMBUGCgmSJomT8ixkARkWB3JlZG1vbmQxIDAeBgNVBAMT -F01TIFBhc3Nwb3J0IFRlc3QgU3ViIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB -iQKBgQCmpJGPk8XSOzw6MlrY7HcEPSB6DdwpStP1veBAM/rdQJe7HbBCsdOy8mpC -zDy4j6k1dxAUerThAgoN+yWXq4Ax22Kr3Eg5gGfreeTiu+V2L2tMXqdim6wj9wJp -BtRuwQbMb7tNFD99Wtre3hmwIe70pry50B2uu5qUdwNAt0ijSQIDAQABo4IC1zCC -AtMwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUamZ4YgpP9Jyot1/VZjSPM3Hk -KxMwCwYDVR0PBAQDAgGGMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUC -BBYEFKCkha6ClupJRMb284hqhgP9B0csMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA -QwBBMB8GA1UdIwQYMBaAFPUJwdYmf8Ofyh3mSMlpx0+xEf4QMIHWBgNVHR8Egc4w -gcswgciggcWggcKGY2h0dHA6Ly9wYXNzcG9ydHRlc3RjYS5yZWRtb25kLmNvcnAu -bWljcm9zb2Z0LmNvbS9DZXJ0RW5yb2xsL01TJTIwUGFzc3BvcnQlMjBUZXN0JTIw -Um9vdCUyMENBKDEpLmNybIZbZmlsZTovL1BBU1NQT1JUVEVTVENBLnJlZG1vbmQu -Y29ycC5taWNyb3NvZnQuY29tL0NlcnRFbnJvbGwvTVMgUGFzc3BvcnQgVGVzdCBS -b290IENBKDEpLmNybDCCAUQGCCsGAQUFBwEBBIIBNjCCATIwgZoGCCsGAQUFBzAC -hoGNaHR0cDovL3Bhc3Nwb3J0dGVzdGNhLnJlZG1vbmQuY29ycC5taWNyb3NvZnQu -Y29tL0NlcnRFbnJvbGwvUEFTU1BPUlRURVNUQ0EucmVkbW9uZC5jb3JwLm1pY3Jv -c29mdC5jb21fTVMlMjBQYXNzcG9ydCUyMFRlc3QlMjBSb290JTIwQ0EoMikuY3J0 -MIGSBggrBgEFBQcwAoaBhWZpbGU6Ly9QQVNTUE9SVFRFU1RDQS5yZWRtb25kLmNv -cnAubWljcm9zb2Z0LmNvbS9DZXJ0RW5yb2xsL1BBU1NQT1JUVEVTVENBLnJlZG1v -bmQuY29ycC5taWNyb3NvZnQuY29tX01TIFBhc3Nwb3J0IFRlc3QgUm9vdCBDQSgy -KS5jcnQwDQYJKoZIhvcNAQEFBQADgYEAxEeI+MT1wtyEl29mQXy64Z+/qCwlfaTH -/tYme8cR0RPHixwJcVSmKrRirchKQ0rrrjjeuWBfq1NKPK97csGZRI5YZAOIkRKW -EV7Ws0eNDnQdmQ8tWdZvEuWGadiYNImrBAbjdGIWS1Zqodmyc8QG+mlKJVbR06zn -IzgsGYcbjBQxAA== ------END PKCS7-----"); - - public static readonly byte[] PfxWithNoPassword = ( - "308205DB0201033082059706092A864886F70D010701A0820588048205843082" + - "0580308202F106092A864886F70D010701A08202E2048202DE308202DA308202" + - "D6060B2A864886F70D010C0A0102A08202AE308202AA3024060A2A864886F70D" + - "010C010330160410286B4EFF0202AFE16583E50C3EB8F38F020207D004820280" + - "C3452B3850AE69FCC0AC426FF3A0421477813259C128D643452219EEF71EBCAB" + - "5E7054B7A195E3F945222864CA37D8F67DDFA9136A93CD7FEAD86F00D4179F1C" + - "557253253C6235295499729C564DE2CE30E131C0D9B3E1BDBB211F8FA9E78B7B" + - "088C63137DF44CF50C293E082E7C57A8D0CB0404D1F5B9D1491F4EF9045181D3" + - "8D528C61F49EB3F1CF11ABB60270CBC10AC4BAF115A5AB52EA22FE4406743695" + - "7DDC1BFEE0C6BDC097BDF092AC6D11CABAE497FC10564E7E7797BC6028CBD75B" + - "1A2339329D439F6557B3CEB77489467FC8990EE832D48E2FF65A7BCB20E2DBBD" + - "F81C762F688E2EE43822CF9DEDE11914DD982FAB2AC141496D912396F6F67E3C" + - "D04D0617F8EC2BE6D35AF9860C384DB8C21FD0B00494FA3188983E6200DE90B3" + - "E6E662C5B07AB202A1B9C3F10F03B88E677EC7BDC2873AA4DFF873DCD714AD39" + - "42C33E63442A855C709F58063D836F8CA77DB9D208F3DB2552D7AC611409E8BA" + - "942BE520D9B4951AB844892D123DF550BA4F22D255069AD77E6C14D730128FAD" + - "3736551CDBEED2022417A948B1BD2567C39DFB561F251C45D2F4B5711B4AC82A" + - "09226403432FEEEEAC5F54590CC2EC7925768651ACF42C32A114ED133718FB1A" + - "9FB0DCC35C10640226F7587BD4EA67C10EF963113D03988C82B3A0B43B1B60ED" + - "BE0A7CBCB6422CD9695BF206A190E8EA6F87E2FABB33549081EFA7A8B04CA044" + - "9DF52E133556781511CDFCBDC55B487DA51C8D476FB635896FD0C71754B2EEE9" + - "A395F355F4A56CCB8CE75D65F73AE8DE80215EA7CA9129FD90EC48ECF26828A6" + - "0E471FA5F34471AD1C21AD200E16C4E4E99FA55B5CF75BC4BF68D1A975E8276F" + - "5EBD47EEB21DB06092F51AEE6BDAA2CDBC9387620A2B3983247E9AA93252D49A" + - "3115301306092A864886F70D01091531060404000000003082028706092A8648" + - "86F70D010706A0820278308202740201003082026D06092A864886F70D010701" + - "3024060A2A864886F70D010C010330160410127070F8C0D10011CBE0CC8489A3" + - "0C71020207D080820238C151B340B678AE8CAD6C6B11E0661E91FD1F4107F0DB" + - "687F039362D88AFF382E3557A75157D8A6D93F0777AD41D7520D32916677699A" + - "EC3DAEE462344BF18410EE07E83811EC26569FF9CD8D13A77F387D6E7C5C21C1" + - "6BC9936DB1B8AB614A8B4B6F9975E4A0A5DFCE29EF14E833FB2526805901A782" + - "724AF6BA2A80E93A4C4BA07B1C1319169E200A4B7AB100AE2CA135512919120A" + - "1D1AB57EF6DED00144F87D051391676D205196ABB4B211698BF137436D6E39D6" + - "719737B66AD2E76D5766D36E87108D79145021C77328A9F2ADDF44EC2A95EEA3" + - "86DCA32FB53D0AA92FB5C5BB7B49CB1F1755ABF195C7274702681C616C21BB05" + - "817B5FD344FC25CC6145A4FFE36F4D5BC131434E6C44BD14769EB08FBDA0D1A0" + - "6DCF2D061FA4A2FCC45A30125680507AACEE7903ABA0C1D36395925A82741797" + - "CF93A11F249D7E7D8228F8F6AFD03FB317D1F2BDB319C0AEF15E19E9DB1067B1" + - "A6CC12CA458C33BAB31C3F275C45A956F71CFF939F393EC7D20E13B397E64263" + - "702B54DD228D0E1275B39A77B3B1A28EFD5C7DF2643CAF7AAF8574988CDF4112" + - "E057F715331F6E75462E6C948BFB92C5BC81B84FBB47FB97AEB3D8C228388B94" + - "CDFA0E2A48F05A32EA9F2CDFAE2B0CEFF815531B148C358ECE1D23F7B793A1FF" + - "ECE491E990BDF231756600B87FA2F7F3AD2C2AA2F6DB42FC6D62766F28F60436" + - "FFA4993C87BE6631D7CE6C06C5B7AE7218450C504ADB401B9FBA5FB2FC6A8289" + - "B42D51B4E1AF159AE7F3A63BA8644C8C5A99F108FC25A27DE54E268AF8A259D9" + - "F6E3303B301F300706052B0E03021A0414AF311074EBABE699402460BFFFE14E" + - "4D7314FCB4041469835268466D1390373566F7034C4736346CD17D020207D0").HexToByteArray(); - - internal static readonly byte[] Pkcs12OpenSslOneCertDefaultNoMac = - ("308209180201033082091106092A864886F70D010701A0820902048208FE3082" + - "08FA3082036E06092A864886F70D010701A082035F0482035B30820357308203" + - "53060B2A864886F70D010C0A0103A082031B30820317060A2A864886F70D0109" + - "1601A082030704820303308202FF308201E7A00302010202146543F3FA4FA3B8" + - "75A1BFDDB3CC23ECBFEA736A00300D06092A864886F70D01010B0500300F310D" + - "300B06035504030C0474657374301E170D3233303432303137323530385A170D" + - "3234303431393137323530385A300F310D300B06035504030C04746573743082" + - "0122300D06092A864886F70D01010105000382010F003082010A0282010100D6" + - "C77692CB15D98788AF8C00AA0C626AE45015C8300364C0303F09A1D48DFF5599" + - "A3B8DEC49365E28274E2E33288ECE042322CF8B2697E760D0FB4E8894C209859" + - "9D7BCB39DE74596638D11C87F8245EB245AA0FFDDABC45F804158776B6A1980F" + - "13F4C87F2BDFADEFDA91C3628544BF5F328E5F1215B5F277F6C7B89413720B22" + - "9FA9DFD132A7CC187F88154D6C091841461D2E7C35F75C1A40BB99EDC3C3873D" + - "51C2F68F804F58C0A4FCBA8001F6B92825581B4A9391C6A770614F6BD82B39AF" + - "13094EE7E49DCAB7FA2C751533B358C874148BAB88B915FC99BB8C8F907A4317" + - "3FA14F3B26EF4FE5B014D107DF73E5A2FC0E9ED651CB376A78CB1177E5D83B02" + - "03010001A3533051301D0603551D0E0416041479B60678BEFC1E3784E8A780B2" + - "30A7E826DFD627301F0603551D2304183016801479B60678BEFC1E3784E8A780" + - "B230A7E826DFD627300F0603551D130101FF040530030101FF300D06092A8648" + - "86F70D01010B050003820101000ED62D8DCEF486F04C6047AB0C2CB25FDF1923" + - "7E46D1AD9C16139B8C0A0236EDB46582AE45EDF2E242947A81AEE2AF4BA9AA9B" + - "3297FDD76B1C7F235288802DA590213B4A5E7986B828E072665D2855860B86F8" + - "F625F2BF6111938C54563DBE8AAD21DA2A883F901CC2D7CD08DB113F3468384C" + - "388A6B07E4C301839D4F0E8BD38920FEEA4DCAC3ED817BE1D0F3BBAC45E772C5" + - "41B0819DA4F0EBAFB952C082A57714758394C07416EAB0B53ACBEFD2F2E2138E" + - "B91CE02F4F8E008C976CDBED10340B79B9F9FF8CC94C65EAC274C4A08E1347B0" + - "21268E581CDF1730AE9DF3B959508262DED10B040A1D8346F0F544A2BBBA7454" + - "C2B2A7218B21B64D82B8C04A0F3125302306092A864886F70D01091531160414" + - "D222A8E9C8CF876416F9A2132202326C1AE63C203082058406092A864886F70D" + - "010701A0820575048205713082056D30820569060B2A864886F70D010C0A0102" + - "A08205313082052D305706092A864886F70D01050D304A302906092A864886F7" + - "0D01050C301C0408E0AF915D08BBD72002020800300C06082A864886F70D0209" + - "0500301D060960864801650304012A04100CA536131D2DDA95F84830C4E47E84" + - "4A048204D0075FAF61AAE642CAE276A7AED857DEB08094880EA949F763F54DA3" + - "B1C0CED0D383B3B305F5D4C033B33BCFA08385F58BE862048F739B59C30AB3C2" + - "1B8C877D2056D27CD1A015AB8EAF86725CB5F2892C667CC02DE0AB215D4859F4" + - "A4DFF4C6B8187B4DB5DD2BE8217482148D0135E1B6B7784E7C8515FF4155D662" + - "5E742D45D3DDDA357BB7FAA90601773C96468F2E023242CD6B087CD0C504C124" + - "8D747BF0465F33EA2904682678CFE57B4FC9DD8162230F7C6980B399D793B796" + - "08668B7FEF17430E128AADFAF37F7610924845327A631F51B4AFDBA3D08C2408" + - "8C6059FE0495003E210F53E616CAAD669954A7E517DFF203746DA1A6693BD23F" + - "3DFF096DF050DB68AF7E4213F2A40D03C274C3E1F224B66A3481001C445E99DF" + - "F5EAE97A9E7C34D035A89DDAAF982794E2BAB630EB61F96399B891F460463052" + - "CD6176E6561DDC60C9FE476D125758AD3EAF9747818A4F963181928E79FC667D" + - "396EFFD95CFF25DEAC00E0B1A4092E0F0AF9132116A78C75B3306469F64B536B" + - "FD6130B756899A7FBC79EB11BB0F124A8BF08AD818923ED9D1F93A79C1C1DEC6" + - "58B8859794A3C02F9299901F4303CFFBECD354BB77FB5BF63146B8F85C4C0F81" + - "85A7A6C8A28C51C33340DBB2204AFC70D4D79AD2C35514E4BEB413D519A12288" + - "29E7195A730A1A65FF9585E32F91F232DA23BDEB2DFDBEA5377AB9E053198899" + - "B140F816979D53F738601782C7CC24E881CF337F5CF50C24EBE2ADAD74FCD523" + - "2E620B8F144252BEE0B0FCC08AB239526B1DBE7F6C3D1FC8184F60B8B30C64F9" + - "B42F5B674C475A4D088C81C8D9102118273C6DB632CF7E127E037D6B317DE6FA" + - "C7B2752F1173851E3C099F40F207B0EF57BF80C73C224B70D0D975C15A8A2193" + - "2E34EF1113C48EB8258968B37D2358216E6302192C6FB6A68B325D7B731CDABC" + - "4D1FE7A4EA25AF60F5B224FB8BC0A3DB8ABED63FC1B91FB43AA4ACCDB32B46F8" + - "1441CBE62B1C38A4FC1E76E2419579890A10906044E18CC1D06A1866EAE98B78" + - "2BC5503BE5E24638C79FD804809F69D8019B77F498A36CEAF1582DAE245CE949" + - "BE493751FDD232A02AEF72DAF9B527AA29BF8C0B0384DB29BECE1A1E3B81E681" + - "00C047A22E73ADD5ED5B19BD922279BF8E0D4834B69408B5A9844B8421D6420B" + - "3BA243BEFBC2AFA74DA94D34B58BD827D59025438EF4E982E0BFA133F5EDB181" + - "FB5E3723CAF8BF0AFDDF20DF75A433850DFBBDAAB3FA7B61962A66A0AEEC1476" + - "B12A016CCC2B12C16AA8547D46B33F3B6008A65C165125BAD61345D9777E40DF" + - "232190FB47934E3B935B963DDE4717F4F4D1E932D1D7020226C0A2472327E16A" + - "F8E16DCEF37C52E58A65FA44842E5A323DD768322DD0F22BA1CDF9E54A3C3ABB" + - "7010A9E4C3D856719CD025C1EC7AE2DBCB8DE8ED68AC542797978C47310BB3B4" + - "D34A4244B3D0D01338A2EF8A43DF1180C01BA03AB9A382C888912BBBE541F8A8" + - "7C0CDE2EC2405F67AC22049410E2AFD9A8E4B8A836055C399A5422DB375A7520" + - "99A947C933DEFFC26532F399D08978443C6D040FD29C5842B6E0A8235D44012C" + - "6783AA66ACA31FCCA155AD6429BEDCABA71398B287AFFCE96AC8ECC36CD222C6" + - "7140E144656A0D8FCBDDBF5E96390DBB7C7C093EEBD8DFF0BDB68568BFE77D7A" + - "A7A3B6C94CD9737B8FF3C4F9684062E175AE9EB8DAB5B8956EAFB3A5EC84C9C7" + - "376F5F4B588E60875F3654A15899FD3734455366083125302306092A864886F7" + - "0D01091531160414D222A8E9C8CF876416F9A2132202326C1AE63C20").HexToByteArray(); - - // This is an RSA-1024 certificate, "CN=outer" which has an extension with - // the unregistered OID 0.0.1 that is a PEM-encoded ECDSA-secp521r1 - // certificate ("CN=inner"). - internal static readonly byte[] NestedCertificates = ( - "3082041930820382A00302010202084062D86F6A371DD7300D06092A864886F7" + - "0D01010B05003010310E300C060355040313056F75746572301E170D32343031" + - "32353031333630315A170D3234303132353031343630315A3010310E300C0603" + - "55040313056F7574657230819F300D06092A864886F70D010101050003818D00" + - "30818902818100AC24F75F85C4C38E6DBF57DE889AED7598DD01202FACC00EA7" + - "4EC449DD420E7CB49992E1BCCC69B3EAB8B2AEECA5B2BCFC1295DC82B83EDB71" + - "C1764191EA0F73D1BAB26D03B95F322EF8299B1DADAECCAADEBA052BD3BC2549" + - "D83AD1C2FB2DC556370306AC3CBCE09F3669448EDEF8FCA12164D793D5B456A7" + - "418393899E00590203010001A382027A3082027630820272060200010482026A" + - "0D0A2D2D2D2D2D424547494E2043455254494649434154452D2D2D2D2D0A4D49" + - "49426D6A43422F61414441674543416767312B7233664B4775457054414B4267" + - "6771686B6A4F50515144416A41514D5134774441594456515144457756700A62" + - "6D356C636A4165467730794E4441784D6A55774D544D324D444661467730794E" + - "4441784D6A55774D5451324D4446614D424178446A414D42674E5642414D540A" + - "42576C75626D56794D4947624D42414742797147534D34394167454742537542" + - "4241416A4134474741415142513143437A4448737A724E6839445456697A6E75" + - "0A4B38516F496B5649324138494E676B4B6F6B3649704158484C533058767333" + - "4E43745152396973486C5A376C707844366C6B72376449793363396756427063" + - "630A7131734242796B73534854745A4766337A2F6E526E5A3961684463755444" + - "2F486E71302B326A33476461423557594B6733336E65337730423258385A7377" + - "65390A46774F2B335A6954734D6D647038312B6B4D6C76463775453236597743" + - "6759494B6F5A497A6A304541774944675973414D494748416B4942454B6C4654" + - "4978560A434274324839636A30595042493632457734752F665A4B4A53657272" + - "7848707244476C67546B36635075585345723569395A397731505A3872654A51" + - "7A7367690A6C736268363653584B4F6C67466945435153587972457736633565" + - "734B587A6269484F7762564E4756305A4430424A6761316533667144507A4170" + - "75527734510A75656A636749684E723577504E7A4E696A464D57697944716D79" + - "6E586D4B6E627933457050396D370A2D2D2D2D2D454E44204345525449464943" + - "4154452D2D2D2D2D0D0A300D06092A864886F70D01010B05000381810032833C" + - "38E5F66D64880C191D247CE8B819899B20F284DD59CBDE1731A4D0FE09A95A52" + - "D5A7D049CA108BEB9673FD207C842B324DFD8086C9E1CDAB931D7403730E0521" + - "69943C58ECC3DA6E11C6ED4F16455152D6FF4D104C88976F9BA88120B0889563" + - "1378357F297A6B3E444296C06C636A589973250F2A096C39C1EDE5C1C9").HexToByteArray(); - - // This is a PFX that uses mixed algorithms and iteration counts. - // PFX { - // SC[0], encrypted, PBES2: AES128CBC, SHA256, 2000 iterations { - // cert, keyid=1 - // }, - // SC[1], plaintext { - // shrouded_key, PBES2: AES192CBC, SHA384, 2001 iterations, keyid=1 - // }, - // mac: SHA384, 39 iterations. - // } - // "Placeholder" - internal static readonly byte[] MixedIterationsPfx = ( - "30820A320201033082099206092A864886F70D010701A08209830482097F3082" + - "097B308203FA06092A864886F70D010706A08203EB308203E7020100308203E0" + - "06092A864886F70D010701305F06092A864886F70D01050D3052303106092A86" + - "4886F70D01050C302404101215FB90500DC30882E7D84625F97923020207D030" + - "0C06082A864886F70D020A0500301D06096086480165030401160410558E3FAE" + - "FE92ADEC4CF1405E7DE7C5118082037095F887CE32E748EC0D647062286EA9D0" + - "C3C04213ED9431FFE0F6028105D94D088684A55C997630D16EF7CE7D49729C30" + - "1F750D8864B988ABBAF551A8819ED7D17CBB810CA3DC39EBD804B3C388C73BA9" + - "320783DF3AABEBAD9F44E4ABFF931AAA95AE874E348B49F35749614D9EE94F5E" + - "EC7A5C597FE8CB6CCDB7A0721EE5836C8F839D83E1577A753F8B2B7E0C2C53BD" + - "D365322E211A62DE061FAC3ECF770CEBCF37AC2604A1CF318C4DB1BA83C85779" + - "3BE57C898EA80D52E0F83B17F877210C1A5E819F5C35DE6DB5EBACE1BFB3101C" + - "C2FE9FF4983BAB7B93CA5D466B855D973FB5D72D8DD109B61FBA238320C886C0" + - "8FC141168C43B946AAC28C114B20A667D189E473E9779432EF64C965459AE9C4" + - "6430B94E19BCEB4733D3038D9F65B85D79B7A1DA954887BAE1DB0A999135A09A" + - "25C38616DBADB9BE50F729925F23C107F899341F76B4B2161CCAF8C499DF7646" + - "F7DB16A21EFD90097D75EEF893B8B8115F6A8816B2D9628278F8ADAFD0E4AC7F" + - "14BED13FFB6DD7FEDE1EA1990CACE772DBFDACC2A4547F910D3F2B238FE86A4F" + - "1BAEE8AC282F918D3F82B8149DD3A99B78D433AB0F5592E7B7B25FBC4A726520" + - "A49CF2FDA60D747F19031B34F911C442E20BD91600A32F9A4FCC1E3DF330171A" + - "4E0721CD01A93CC20C2B770990029C30306945AFC7E3ABD9516D8C6F359D904B" + - "5F32F3BAEC8770A8BE15C89E4B836B1F20D29A7D3D3BB7491EFC9B88A161B598" + - "02819D2EC695000C2F83B1A363217CC2B0A70A4A226993BD32BE302DCD157EEE" + - "3F9DBFBDE0375A03DEA2761941F49D42A8F5029DC3B65E661B61DFBE5F2527D6" + - "C59927B5A0857FE7B9836A63D202895C1FA6FDE40E70107659CE3E4C7DDDBC9E" + - "04B85A46104EFDF6692BF898C565480F5A5163231167DB69F873D1341CDF8DF0" + - "7BD4473F58162A3EB653C6567C2E3120B0034B9E35D22838BBF02968651FC1F8" + - "873C405237B7F29297ED47CADEABF248386CE94D215C8D5731A503623C9CA916" + - "C5896B8F713A044ABBECA764D36D074DF523D989228B89589463DD3615A7E844" + - "E7E9E91BE0BFAD79218CD7B52A313458BBA8FC660B5DF8513464FE749EF3C201" + - "5C15B2EF15F63D5FDA726ED8E04B3B5E7DB8610367AB08EDF120DDEEA7382AC5" + - "1C778B8069E09F2EB68D19154DD10129A1E0DA0717AE1E108B2496C69904D851" + - "18B37E06FA95E1F5E75371D8C7EC3BC80C817F931744428BF03C1F51A0164C0A" + - "3082057906092A864886F70D010701A082056A04820566308205623082055E06" + - "0B2A864886F70D010C0A0102A082053930820535305F06092A864886F70D0105" + - "0D3052303106092A864886F70D01050C30240410B5D9DF064271CEFFDBC3C067" + - "F6DFEB74020207D1300C06082A864886F70D02090500301D0609608648016503" + - "0401020410DEC794EDB555B89CAFB3671FAB4A52E1048204D050B2A2E9E3D3C0" + - "84D5BFCAB1C7C27951C0B72FB45B52089605DBF5E60550521DEAB0A66EF2C182" + - "3AE2F4EC21A1B8BF65D240DF20759C66AB0C6311CABB30F9DE1290B81CF69AAE" + - "7B6F42A4E9BE7BB6F09058C7D0D6FBE6F24EC2E3457680907F000C5B457B1E7A" + - "E394A7A70E6289C3B009F522399D5CC41014F12A336926F3AB8E2A3AF5496BC3" + - "27B073206F20F131AFCB627A1E67B9B457C44DB6A6C10EEBB8856E0BAAF99D8A" + - "9D24D7C90AC5EA9EB14C66315E77F158948988BB729D8E0796741CAE29894DC9" + - "6614C2B06911013C168C7A4E6C46F09D1AB0D7933729FC88BA47A41BFAB0AB53" + - "C60FEAF6A93688E67039184B598BDF1CC95C3967F9ECC649745E265974713102" + - "E271ECFA6A067F10751C2A4A70A94FA39F37E944996C843809D82990A9D44C18" + - "9EE7B9DC8534F9B821A5C56A61DFAAF470444883200B3FFFAA23DCE84375937D" + - "D0149869B2683773F650B70003EB025A04E170DFD97D4F86D2913E91F757BC01" + - "5F1F85F497B1400052E0868ED6844E2390F71B036B524E824B3ED6381BCE2600" + - "71A5F6EB12E9C1C44BBE0664217C4A44EA07E440A33010A0CA46647E7FFA4F58" + - "74F1EFB9DD330DA2CD50AC01C489639520A1A27B603F44831EF235F0540817F2" + - "196AF45D3B9957D272B5549BEC5507F6BA443A37F54FD6D6AEBEBBB7AAE15F7F" + - "8C1C46D68CEEC2E95426D177A390E7C50B0122BDDA9EFC104294840F6D5374AD" + - "90952842C0EDF796AF48C6FDD4AA02C81AFCD606CD0E94F3DF2B06B649A47D9D" + - "7C8AE23E1A776E1D149CA25E4929238778B19E33DE05DE577B763305B4884B8A" + - "7DE31BB9712477C9CDC138B52FB2D59CFE42A1CDAEB0E205C70B38CC4B88E0A6" + - "ABCC41D29D11947FF5B03633A5E164ED22F10536AAD07DB2EB52A2C696EBB135" + - "B5220839D1E5A7DB6C2B8DEB09C883129BD5253F68169F9D5F5A2F3AB17C4921" + - "10636978B8573A5B4E1FFF5A4C3E75E2F03AD71AF874C544474E1B41969416D6" + - "D8FFFD595428EC7928BD17C652F67D9B6B150C6D4A9352C405BED162492A5FD5" + - "7EB6BA5F77AAC2BD7E4EEB6B0BD2E4329E2CA8A425F88F4743B25F259E292C04" + - "483472CA79FF52271A830AA6A27A52C3531E2B2503592C017A7CC00F91F63F73" + - "4E3E56746475B8B338440B7D5FAC87A90831EE78A2DE4FD6F60F1C66B31A520C" + - "44B73B09D5419451C4A32E8E1A5BB17E44B9FAABFB07021747093DBFE248BACB" + - "E2BB6C7F145DE7397A2B2AEAA083EE57F46C8DF85FA2DFF4582C2E3CE3CB2E91" + - "706395A63BA96343B0567E41A33FC964BC8C03CEDE5E3E1D7A8B285F745EEAF5" + - "CD1382C86DA82922DA1772158F8BCCFF70DA87A64602033560566F33A3793A4E" + - "5C404D2C69274EEE9E82B5B97B0760FD66067888711C572E84DB382491792CB2" + - "C7BFA472E6B0D70D529701C2A0B730F5E1E0A980EAD56EA323E0008C70D62F53" + - "5B9E0533F9A4B7CFA22319274E68C8B4737E5DAB5B1956C235E7EA548E24E23F" + - "F9FCA61D11DAFC6B90E0BE8E96A66B6973D5F025C0619D283CC92C3224000FEE" + - "F9BD002E7EFAD4C737C4CFEB42858DBDFBC489B1131AABDD1868C58EDCAD35C2" + - "AE1A42BBCF0A2A90E0557A7A5F79F2D92D19E39D505994163CC94F5EA56009F0" + - "5E9ABCAF24807130F90FCA606D5448C103489BB53090EC603394705B472132E4" + - "FAD50491069F44040A0D66F7D3D5C86593D61C9D37CDE3BBE5651EA2E104B324" + - "3272E665CDBB139E063112301006092A864886F70D0109153103040101308196" + - "304F300B060960864801650304020304405EDC86442FD573401BD2DF0F95356A" + - "1C1454F401231B7F772179626ABCB220C8096AC0ED6C27CACED7D94615768B61" + - "8BDDCF4B8A0996E019BD418423F79F173404406B32D0D889B85234D716C87F3D" + - "EEBD62B5DC14984FDB9EA9FC765B340F54D3E5203C6A9F4F23913B22605A32BD" + - "3D8D120CFFFE4ACB83BA4D488C67271E38CD40020127").HexToByteArray(); - - // This is a PFX that mixes encrypted and unencrypted certificates. - // PFX { - // SC[0], plaintext { - // cert, keyid=2 - // }, - // SC[1], encrypted, PBES2: AES128CBC, SHA384, 2000 iterations { - // cert, keyid=1 - // }, - // SC[2], plaintext { - // shrouded_key, PBES2: AES128CBC, SHA256, 2001 iterations, keyid=1, - // shrouded_key, PBES2: AES128CBC, SHA256, 27 iterations, keyid=2 - // }, - // mac: SHA384, 39 iterations. - // } - // "Placeholder" - internal static readonly byte[] TwoCertsPfx_OneEncrypted = ( - "30820CAB02010330820C0B06092A864886F70D010701A0820BFC04820BF83082" + - "0BF43082029406092A864886F70D010701A0820285048202813082027D308202" + - "79060B2A864886F70D010C0A0103A082025430820250060A2A864886F70D0109" + - "1601A08202400482023C30820238308201A1A003020102020900BD698EB46606" + - "F1E4300D06092A864886F70D01010B0500305E311E301C060355040A13154D69" + - "63726F736F667420436F72706F726174696F6E31173015060355040B130E2E4E" + - "4554204C6962726172696573312330210603550403131A506C61696E74657874" + - "2054657374204365727469666963617465301E170D3234303531303138303033" + - "375A170D3234303531303138313033375A305E311E301C060355040A13154D69" + - "63726F736F667420436F72706F726174696F6E31173015060355040B130E2E4E" + - "4554204C6962726172696573312330210603550403131A506C61696E74657874" + - "205465737420436572746966696361746530819F300D06092A864886F70D0101" + - "01050003818D0030818902818100A6638EFEFA9A1B364574D9A7BA4A85017E73" + - "61831606B717DCF8FC0F5B982583D5460BCD216E99FBC15ABF62B5C30FDC7CF7" + - "D13CDCF9E0A0A25C26AF0AC14D116569FAA6496CB87ECB0A3B87AAF624010630" + - "4E7F0DDB63FF7EC95396F2CF4E57A1F0356414C7D1032433831C327AC33DD264" + - "FC7B1BA567FBE360AF35446B63110203010001300D06092A864886F70D01010B" + - "0500038181001E2C2AC3FC484C42FBFC3A0D027E438BDBF6FDD4609BF1E11BA9" + - "CDCF50D121BAEDFA5361619B469D48F1CBA203F361A2E7D662A88239A500D056" + - "CB38D215197A3F5FFACA5AADDAC875D380C4435C0E8633243174623BF59836F0" + - "8DBA917C5B4A2270579574566FFA29A7A5FF0415D34E8CCFFB9EAD4BA8EC90BF" + - "B3A2F7041F6B3112301006092A864886F70D01091531030401023082031A0609" + - "2A864886F70D010706A082030B308203070201003082030006092A864886F70D" + - "010701305F06092A864886F70D01050D3052303106092A864886F70D01050C30" + - "24041078790CAC0DA9B3E9B0EE236CA8073CF3020207D0300C06082A864886F7" + - "0D020A0500301D06096086480165030401160410256DE9ED9807FD7D30597DB5" + - "51A5815080820290D8156A7DDCD344FE96B8D8BBA4303A373108D725BFC30071" + - "DBF716A7C1FD1B598BAE1FBC9A77CCBB7319646E83B747B59330760B6EBC29B0" + - "91E591FF0030ECB28626BA1594FEF6F0A01B60F2548AE1577FD05E7CAC5BCFA6" + - "2422E71F551FC2A8ACE488E871C03E1E02A9DB4ADA9DB335466EE1A6E7AE6B9C" + - "AE118AF4008879690C446F0D4A03740E31E4879B163E58FFA46394DF57CD98AE" + - "FA3C44E94BDD36D31A53EB2C9A74EA9F45718370106F205A81837A05952E7C17" + - "3460A2400195D658671BE62E76AAD565D848109BE41B6F06CA87A7B7676B9A5A" + - "525B6ADDE80A68A87FE82C8547D611F6DEC5C2AD617D354216CC7E724DA2F7A7" + - "BC67C336A68F7A8ED4F555E53330DDA1318332E170458D10E7A1CA60D87F61C5" + - "89B8BC4CDCAC26BA341B96BD20096B89977750B1E2BACDEE23130825B827F5CA" + - "E73373AD2258201B4DED998F003E1084BE969F5B1EE33B8443BE01C509927876" + - "1D69A1E48662EE91DF5211176DA5495AD54CB50EB2A2A3E077E93946958CAFE9" + - "5F2FB3450B6269F3541BB21B6F129288115E177594D6EC0DD516B8882100C9CA" + - "DB9874064B3A1F051FBB0B43257F0644A05C6D416C0652696E89DB6E43CE5E92" + - "94D05711CDBF7B304E076D73FEDA97A7E2DFF398761DE6425730CB576D26F49A" + - "2CCD93BDD0E410F70D4EF8DB6BCF221BD3E53C472CEA9C40E09A284923992BD2" + - "12B32E36BBB06EC3000261E8EC9D9E389F663DBAAFEBE0CF671E771A246D033E" + - "AFFDF400B94D8926E23FD660E0A8304C01D490EF54F868506EE8AA255F95E00A" + - "7E1AA1D18998B9AE8E0AA0472CD152A674536F5D2882502247D2B056137BFF97" + - "08CD8EC61A3339CCB1DF43DC60A0A75F9260905BB3D339580EA5B5B7BAA92374" + - "4A8850389A2E68B98FE6EF78D78207ACC858A97DEC55C0D03082063A06092A86" + - "4886F70D010701A082062B04820627308206233082030E060B2A864886F70D01" + - "0C0A0102A08202E9308202E5305F06092A864886F70D01050D3052303106092A" + - "864886F70D01050C3024041077CF0CA72DCB1B541E6F481D3A51470A020207D1" + - "300C06082A864886F70D02090500301D06096086480165030401020410503141" + - "7A0A42803706E84B8C7470CD2D048202807E3108B51EA4D80D2E7CF358E08C6E" + - "A30087AE0E9F80CF1B022A0DE0BBDA715733141C6877276864DE98D439D61650" + - "63ED517FBF206450AE6B29835F89914003B8D77F0F15D6FB073EF27F90E9D851" + - "ABE569D3DE6968E8F61EA184B484B4284C2FD3803D9AA4DE083F18A64B2F9320" + - "156004E49C4A179CF8800799451563200CD998CBA779032302CEA4227A78CD10" + - "8FC8BFA6C114F005A2937948F14DDDF05AEB543F4D2FB3C56FA7C1F14B7ACA57" + - "0CA8554A6EA4AC1C0FD356E3D1DD568F97D6EDBFD3B09C87BC2013A88C442993" + - "4C73F442A5D5DA3563FCB85EBEC41085843D14EE88E582151E6855741106CA1D" + - "D775A5793E8A0B4E036427E6D2FC9F0AA394085997E42875E4EF5742099141CD" + - "70B4AA887C40E3EDDB407C39F3C474F95D394D98DAA68BD1005F1223B6E58B52" + - "AA552FD25652AA871F03A53A58922953DB120BAFB44C29DCF9C9C2F96DEE3BEE" + - "7E0F7FF8F04C98ED631F6A845B8B89F30EFB956846BA87E9B3F6FCCB49FE0F21" + - "10139E855389C8EC5120983A09D5F4D655E64058ADD1E8C44F3B6A4936E6788B" + - "E939061221424D71666765D99FE6D883C9E8079992D585F73B0CD57947D38A54" + - "FE7CBE93BE117675A3E52F708B2A6BA6C390BD3BE01100243E7B7B5CB8A9F501" + - "CCCCD082F9867B4DA0A9FF3DC5BB0F0974D4EDEF9FF89F6EB355BCE07B00A32D" + - "AEE443D46EF19A96E6EB3EA4C0B141606A1FDB200D1BADBC11954951383E8935" + - "4603C10E241353FED1502D8111E42DA45198D1586CB0938947F3856CCC855F84" + - "231F131D636DFBB46394A3EBEDAC9A4DAD0DCB32163C57A9B5DD21BC56CA969A" + - "F574E38787DFA6EC2BA063E60375585ED5B9E8D5B6D1BC162D63036C52FF4473" + - "E0A4FD96AA055ED4014C3B0E4F7A098F553112301006092A864886F70D010915" + - "31030401013082030D060B2A864886F70D010C0A0102A08202E8308202E4305E" + - "06092A864886F70D01050D3051303006092A864886F70D01050C30230410DE7A" + - "16E6BCE889C8A5F823E7670FB9E302011B300C06082A864886F70D0209050030" + - "1D06096086480165030401020410683B6EE3E1287242ED73707ECE1F271F0482" + - "028018435588B8BEA17EB47E7947D4FE82D66F7C26850B840C0F06964DC99C56" + - "8391707178494F177EFB65E17D20D3C39E80A00342783B33164F371902D76658" + - "78A79802FE7D9EB31D21137B56692D1B7E2B38F54B00CEE506867968420CB4FA" + - "9EE135BA960C0D40EC22714F5D8576BAF03269D19384C246A60ED4B9F0F4268E" + - "2E135F7BE41536D844718B7339EE14A1CE261B8A27C2AA2AAA90C98F9437372B" + - "0448579A0C392B347C7E95A1A7617A47218F63D0C52C88A3F93AC47BB9493E2C" + - "67DE061F28E90FED463E392C6743C8E6F60524A2455C5E46218E357AFB31553D" + - "0AAD18A2784719D281B12560CB904527D912BA2B40790D9068661C01AA4B03E0" + - "B5316D6FEBBC38087B4DD46CF2CC0C98B6F488AA9940C6FA16BC8BB1853CD0B0" + - "E41D85F382F5BE36E5A821C54EBB8C34DD7CD7B970315FF65B657AED6CE2598A" + - "6DF4E96370815660B3711FDE0A2CA0AB5C2B234C95B699922CFC4E95B781F783" + - "F20373E8E23EB6D518C797A21B401A797DDAF8A823602ED38135F3FF3C555C7E" + - "7FA7B800F4535B781B4F6B21BF2147D8C20388CDD48DB3C17413DC4FBD347368" + - "809762C4851C28CAD52B4F916CAF53E2D5E05ACAC3E85BA1F63525F57F456059" + - "56B7830AFBC50D0CE517C282AE5DB5638635155073C567A82A7ACDEE6860D18A" + - "1CAACE341845A41470F47E7A440F21A3DA96CF986181E1F044B28FB3EEFA0E41" + - "82B93AA2FD50A23B045DABCD774519879CB824B9A58A1859ED553578508B5CF7" + - "85BE3D2A67F0FC508B94A738162455CD11A4BF0C3736914D8C5A99F9D5ACA959" + - "B1C4D5872BA852DD3F8B2B8C231017AEF373B3B5E7B65B6DF7115DB99D5EB11F" + - "E80E8CB3E1D3CE6F50DD7B255F103DC290B7287D6AD7AB12D5242E13092DB9E1" + - "ADE03112301006092A864886F70D0109153103040102308196304F300B060960" + - "8648016503040203044044A762FCCD979F096F1C0DE0C4C584E42C1848DB1138" + - "389DAC33048D6B81DBBCA7E031E8620B37C14F85257DA4A6F57FE58E939493C5" + - "928343622B80009D8C5B0440E74D33A5F7DA48801A4EF4FD65D0F442F26C845F" + - "A418D3E0D78FD0285A92A74B433661F516C6955EC40FE46DAB813B6AE940C2DE" + - "FE8F8F5E32E6B491C999D598020127").HexToByteArray(); - } -} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.LegacyLimits.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.LegacyLimits.cs index 16b784215456b6..0149a61deb3469 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.LegacyLimits.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.LegacyLimits.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.X509Certificates { public partial class X509Certificate { - private static readonly Pkcs12LoaderLimits s_legacyLimits = MakeLegacyLimits(); + private static Pkcs12LoaderLimits? s_legacyLimits; internal static Pkcs12LoaderLimits GetPkcs12Limits(bool fromFile, SafePasswordHandle safePasswordHandle) { @@ -16,21 +16,16 @@ internal static Pkcs12LoaderLimits GetPkcs12Limits(bool fromFile, SafePasswordHa return Pkcs12LoaderLimits.DangerousNoLimits; } - return s_legacyLimits; + return (s_legacyLimits ??= MakeLegacyLimits()); } private static Pkcs12LoaderLimits MakeLegacyLimits() { - Pkcs12LoaderLimits limits = new Pkcs12LoaderLimits + // Start with "no limits", then add back the ones we had from before X509CertificateLoader. + Pkcs12LoaderLimits limits = new Pkcs12LoaderLimits(Pkcs12LoaderLimits.DangerousNoLimits) { MacIterationLimit = 600_000, IndividualKdfIterationLimit = 600_000, - MaxCertificates = null, - MaxKeys = null, - PreserveCertificateAlias = true, - PreserveKeyName = true, - PreserveStorageProvider = true, - PreserveUnknownAttributes = true, }; long totalKdfLimit = LocalAppContextSwitches.Pkcs12UnspecifiedPasswordIterationLimit; diff --git a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj index fabc02833ca4e4..6a4e5c5fc4e45d 100644 --- a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj +++ b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj @@ -207,6 +207,8 @@ Link="CommonTest\System\Security\Cryptography\X509Certificates\CertificateAuthority.cs" /> + - From 2753a110610cd7243c96824f3a11081c2b4071dc Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 6 Jun 2024 16:48:49 -0700 Subject: [PATCH 04/19] Finish moving/unifying the TestData file --- .../tests/Microsoft.Bcl.Cryptography.Tests.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj index 525c3135297907..df2eaa4f655fb2 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj +++ b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj @@ -20,11 +20,12 @@ Link="CommonTest\System\Security\Cryptography\SP800108HmacCounterKdfTests.Helpers.cs" /> + - From e58c076da87c92fa61d6be9d11d4ed5f8cc6310e Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Fri, 7 Jun 2024 16:38:19 -0700 Subject: [PATCH 05/19] Add more DisabledOnBrowser attributes --- .../X509Certificates/X509CertificateLoaderTests.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs index 86a8903d078a36..52b850ed76fc52 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs @@ -8,6 +8,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests { + [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support X.509 certificates")] public class X509CertificateLoaderTests_FromByteArray : X509CertificateLoaderTests { protected override void NullInputAssert(Action action) => @@ -37,6 +38,7 @@ protected override bool TryGetContentType(byte[] bytes, string path, out X509Con } } + [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support X.509 certificates")] public class X509CertificateLoaderTests_FromByteSpan : X509CertificateLoaderTests { protected override void NullInputAssert(Action action) => @@ -75,6 +77,7 @@ protected override bool TryGetContentType(byte[] bytes, string path, out X509Con } } + [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support X.509 certificates")] public class X509CertificateLoaderTests_FromFile : X509CertificateLoaderTests { protected override void NullInputAssert(Action action) => From f1cab831b6f1eac0cd3cc68e87de23c3032a11b2 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Mon, 17 Jun 2024 17:42:58 -0700 Subject: [PATCH 06/19] Apply further feedback --- .../X509CertificateLoaderPkcs12Tests.cs | 11 +--- .../X509CertificateLoaderTests.cs | 15 ++---- .../X509CertificateLoader.OpenSsl.cs | 2 + .../X509CertificateLoader.Unix.cs | 24 +++++++-- .../X509CertificateLoader.Windows.cs | 53 +++++++------------ 5 files changed, 47 insertions(+), 58 deletions(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs index 53b53fba3ef7af..a45036b832d973 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs @@ -151,16 +151,9 @@ protected override X509Certificate2 LoadPfxNoFileCore( X509KeyStorageFlags keyStorageFlags, Pkcs12LoaderLimits loaderLimits) { - string path = Path.GetTempFileName(); - - try - { - File.WriteAllBytes(path, bytes); - return LoadPfx(bytes, path, password, keyStorageFlags, loaderLimits); - } - finally + using (TempFileHolder holder = new TempFileHolder(bytes)) { - File.Delete(path); + return LoadPfx(bytes, holder.FilePath, password, keyStorageFlags, loaderLimits); } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs index 52b850ed76fc52..e0a80e825f7d34 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs @@ -94,16 +94,9 @@ protected override X509Certificate2 LoadCertificateFileOnly(string path) => protected override X509Certificate2 LoadCertificateNoFile(byte[] bytes) { - string path = Path.GetTempFileName(); - - try - { - File.WriteAllBytes(path, bytes); - return LoadCertificate(bytes, path); - } - finally + using (TempFileHolder holder = new TempFileHolder(bytes)) { - File.Delete(path); + return LoadCertificate(bytes, holder.FilePath); } } @@ -284,9 +277,9 @@ public void LoadCertificate_DER_WithTrailingData() internal static void AssertRawDataEquals(byte[] expected, X509Certificate2 cert) { #if NET - AssertExtensions.SequenceEqual(TestData.MsCertificate, cert.RawDataMemory.Span); + AssertExtensions.SequenceEqual(TestData.MsCertificate, cert.RawDataMemory.Span); #else - AssertExtensions.SequenceEqual(TestData.MsCertificate, cert.RawData); + AssertExtensions.SequenceEqual(TestData.MsCertificate, cert.RawData); #endif } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs index e5eec17e57b69c..1873f3ea934ac7 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.OpenSsl.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using Microsoft.Win32.SafeHandles; namespace System.Security.Cryptography.X509Certificates @@ -29,6 +30,7 @@ private static partial ICertificatePal LoadCertificatePalFromFile(string path) Interop.Crypto.CheckValidOpenSslHandle(fileBio); int bioPosition = Interop.Crypto.BioTell(fileBio); + Debug.Assert(bioPosition >= 0); if (!OpenSslX509CertificateReader.TryReadX509Der(fileBio, out pal)) { diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Unix.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Unix.cs index 8cf2d16bdde673..f851d163fc8fcb 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Unix.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Unix.cs @@ -264,9 +264,7 @@ internal void LoadKeys(ref BagState bagState) if (key is not null) { - // No need to check bytesRead since it was verified - // in PrivateKeyInfoAsn.Decode - key.ImportPkcs8PrivateKey(safeBag.BagValue.Span, out int _); + ImportPrivateKey(key, safeBag.BagValue.Span); if (_rentedSpki is null) { @@ -390,7 +388,7 @@ internal CertAndKey[] MatchCertAndKeys(ref BagState bagState, bool allowDoubleBi } _certAndKeys[certBagIdx].Key = key; - key.ImportPkcs8PrivateKey(keyBag.BagValue.Span, out _); + ImportPrivateKey(key, keyBag.BagValue.Span); } else { @@ -563,6 +561,24 @@ internal void Dispose() this = default; } + + private static void ImportPrivateKey(AsymmetricAlgorithm key, ReadOnlySpan pkcs8) + { + try + { + key.ImportPkcs8PrivateKey(pkcs8, out int bytesRead); + + // The key should have already been run through PrivateKeyInfoAsn.Decode, + // verifying no trailing data. + Debug.Assert(bytesRead == pkcs8.Length); + } + catch (PlatformNotSupportedException nse) + { + // Turn a "curve not supported" PNSE (or other PNSE) + // into a standardized CryptographicException. + throw new CryptographicException(SR.Cryptography_NotValidPrivateKey, nse); + } + } } private struct RentedSubjectPublicKeyInfo diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Windows.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Windows.cs index 9672990faff844..346e8062d34561 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Windows.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Windows.cs @@ -236,34 +236,24 @@ private static unsafe SafeCertStoreHandle ImportPfx( X509KeyStorageFlags keyStorageFlags) { const int MaxStackPasswordLength = 64; - void* alloc = default; Span szPassword = stackalloc char[MaxStackPasswordLength + 1]; - ReadOnlySpan effectivePassword = szPassword; Interop.Crypt32.PfxCertStoreFlags flags = MapKeyStorageFlags(keyStorageFlags); - try + if (password.Length >= MaxStackPasswordLength) { - if (password.Contains('\0')) - { - effectivePassword = password; - } - else - { - if (password.Length >= MaxStackPasswordLength) - { - alloc = NativeMemory.Alloc((uint)password.Length + 1, sizeof(char)); - szPassword = new Span(alloc, password.Length + 1); - } + szPassword = new char[password.Length + 1]; + } + + SafeCertStoreHandle storeHandle; + fixed (byte* dataPtr = data) + fixed (char* szPtr = szPassword) + { + try + { password.CopyTo(szPassword); szPassword[password.Length] = '\0'; - } - SafeCertStoreHandle storeHandle; - - fixed (byte* dataPtr = data) - fixed (char* szPtr = szPassword) - { Interop.Crypt32.DATA_BLOB blob = new((IntPtr)dataPtr, (uint)data.Length); storeHandle = Interop.Crypt32.PFXImportCertStore( @@ -271,25 +261,20 @@ private static unsafe SafeCertStoreHandle ImportPfx( szPtr, flags); } - - if (storeHandle.IsInvalid) + finally { - Exception e = Marshal.GetHRForLastWin32Error().ToCryptographicException(); - storeHandle.Dispose(); - throw e; + CryptographicOperations.ZeroMemory(MemoryMarshal.AsBytes(szPassword)); } - - return storeHandle; } - finally - { - CryptographicOperations.ZeroMemory(MemoryMarshal.AsBytes(szPassword)); - if (alloc != (void*)0) - { - NativeMemory.Free(alloc); - } + if (storeHandle.IsInvalid) + { + Exception e = Marshal.GetHRForLastWin32Error().ToCryptographicException(); + storeHandle.Dispose(); + throw e; } + + return storeHandle; } private static Interop.Crypt32.PfxCertStoreFlags MapKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) From b3e18c81ad7f064b1e14100a99f9c8877763e023 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Tue, 18 Jun 2024 16:30:51 -0700 Subject: [PATCH 07/19] Complete the move to TempFileHolder --- .../Security/Cryptography}/X509Certificates/TempFileHolder.cs | 0 .../tests/Microsoft.Bcl.Cryptography.Tests.csproj | 2 ++ .../tests/System.Security.Cryptography.Tests.csproj | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) rename src/libraries/{System.Security.Cryptography/tests => Common/tests/System/Security/Cryptography}/X509Certificates/TempFileHolder.cs (100%) diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/TempFileHolder.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TempFileHolder.cs similarity index 100% rename from src/libraries/System.Security.Cryptography/tests/X509Certificates/TempFileHolder.cs rename to src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TempFileHolder.cs diff --git a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj index df2eaa4f655fb2..912f12fb54a63f 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj +++ b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj @@ -20,6 +20,8 @@ Link="CommonTest\System\Security\Cryptography\SP800108HmacCounterKdfTests.Helpers.cs" /> + + - From e45cf607424694dc24ffdc4c488fb47ff612024c Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Tue, 18 Jun 2024 16:53:38 -0700 Subject: [PATCH 08/19] Fix handling of DSA keys in cert loader on macOS --- .../Cryptography/DSASecurityTransforms.macOS.cs | 2 +- .../X509Certificates/X509CertificateLoader.macOS.cs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.macOS.cs b/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.macOS.cs index 47396768b09d97..05fd3676f76166 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.macOS.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.macOS.cs @@ -134,7 +134,7 @@ public override void ImportEncryptedPkcs8PrivateKey( base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead); } - private static SafeSecKeyRefHandle ImportKey(DSAParameters parameters) + internal static SafeSecKeyRefHandle ImportKey(DSAParameters parameters) { AsnWriter keyWriter; bool hasPrivateKey; diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.macOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.macOS.cs index 4928c4f27025d7..62216ebeeefb23 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.macOS.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.macOS.cs @@ -152,7 +152,6 @@ private static partial Pkcs12Return FromCertAndKey(CertAndKey certAndKey, Import if (key is RSAImplementation.RSASecurityTransforms rsa) { - // Convert data key to legacy CSSM key that can be imported into keychain byte[] rsaPrivateKey = rsa.ExportRSAPrivateKey(); using (PinAndClear.Track(rsaPrivateKey)) { @@ -162,13 +161,16 @@ private static partial Pkcs12Return FromCertAndKey(CertAndKey certAndKey, Import if (key is DSAImplementation.DSASecurityTransforms dsa) { - // DSA always uses legacy CSSM keys do no need to convert - return dsa.GetKeys().PrivateKey; + DSAParameters dsaParameters = dsa.ExportParameters(true); + + using (PinAndClear.Track(dsaParameters.X!)) + { + return DSAImplementation.DSASecurityTransforms.ImportKey(dsaParameters); + } } if (key is ECDsaImplementation.ECDsaSecurityTransforms ecdsa) { - // Convert data key to legacy CSSM key that can be imported into keychain byte[] ecdsaPrivateKey = ecdsa.ExportECPrivateKey(); using (PinAndClear.Track(ecdsaPrivateKey)) { From cf1ae303005c85574819e4d78131970698b061c6 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Wed, 19 Jun 2024 16:52:05 -0700 Subject: [PATCH 09/19] Clean up test scaffolding from feature standup --- .../X509Certificates/X509CertificateLoader.cs | 2 +- .../System/Security/Cryptography/ByteUtils.cs | 11 + .../X509Certificates/TempFileHolder.cs | 9 +- ...9CertificateLoaderPkcs12CollectionTests.cs | 763 ++++++++++++++++++ .../X509CertificateLoaderPkcs12Tests.cs | 27 + .../X509CertificateLoaderTests.cs | 52 +- .../Microsoft.Bcl.Cryptography.Tests.csproj | 2 + .../System.Security.Cryptography.Tests.csproj | 3 +- .../tests/X509Certificates/CertLoaderTests.cs | 125 --- .../X509Certificates/CollectionImportTests.cs | 62 -- .../tests/X509Certificates/CtorTests.cs | 37 - .../X509Certificates/LoadFromFileTests.cs | 19 - .../tests/X509Certificates/PfxFormatTests.cs | 15 +- .../PfxFormatTests_Collection.cs | 58 +- .../PfxFormatTests_SingleCert.cs | 29 +- 15 files changed, 874 insertions(+), 340 deletions(-) create mode 100644 src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12CollectionTests.cs delete mode 100644 src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs index 8468c8fbe7c064..a4ef6480b0eea9 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.cs @@ -533,7 +533,7 @@ public static X509Certificate2Collection LoadPkcs12CollectionFromFile( X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.DefaultKeySet, Pkcs12LoaderLimits? loaderLimits = null) { - ThrowIfNull(path); + ThrowIfNullOrEmpty(path); ValidateKeyStorageFlagsCore(keyStorageFlags); return LoadFromFile( diff --git a/src/libraries/Common/tests/System/Security/Cryptography/ByteUtils.cs b/src/libraries/Common/tests/System/Security/Cryptography/ByteUtils.cs index 8a1a95bb0984be..569ef966f13cb6 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/ByteUtils.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/ByteUtils.cs @@ -3,6 +3,7 @@ using System; using System.Globalization; +using System.Security.Cryptography; using System.Text; namespace Test.Cryptography @@ -72,5 +73,15 @@ internal static byte[] RepeatByte(byte b, int count) return value; } + + internal static string PemEncode(string label, byte[] data) + { +#if NET + return PemEncoding.WriteString(label, data); +#else + return + $"-----BEGIN {label}-----\n{Convert.ToBase64String(data, Base64FormattingOptions.InsertLineBreaks)}\n-----END {label}-----"; +#endif + } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TempFileHolder.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TempFileHolder.cs index a6efa3a1f19173..23b844cd655492 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TempFileHolder.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TempFileHolder.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.IO; namespace System.Security.Cryptography.X509Certificates.Tests @@ -16,7 +15,13 @@ public TempFileHolder(ReadOnlySpan content) using (StreamWriter writer = new StreamWriter(FilePath, append: false)) { - writer.Write(content); + writer.Write( +#if NET + content +#else + content.ToArray() +#endif + ); } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12CollectionTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12CollectionTests.cs new file mode 100644 index 00000000000000..84d57f6e7e74e7 --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12CollectionTests.cs @@ -0,0 +1,763 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.IO; +using System.IO.MemoryMappedFiles; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support X.509 certificates")] + public class X509CertificateLoaderPkcs12CollectionTests_FromByteArray : X509CertificateLoaderPkcs12CollectionTests + { + protected override void NullInputAssert(Action action) => + AssertExtensions.Throws("data", action); + + protected override void EmptyInputAssert(Action action) => + Assert.Throws(action); + + protected override X509Certificate2Collection LoadPfxCore( + byte[] bytes, + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadPkcs12Collection(bytes, password, keyStorageFlags, loaderLimits); + } + + protected override X509Certificate2Collection LoadPfxFileOnlyCore( + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadPkcs12Collection( + File.ReadAllBytes(path), + password, + keyStorageFlags, + loaderLimits); + } + + protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) + { + if (bytes is null) + { + contentType = X509ContentType.Unknown; + return false; + } + + contentType = X509Certificate2.GetCertContentType(bytes); + return true; + } + } + + [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support X.509 certificates")] + public class X509CertificateLoaderPkcs12CollectionTests_FromByteSpan : X509CertificateLoaderPkcs12CollectionTests + { + protected override void NullInputAssert(Action action) => + Assert.ThrowsAny(action); + + protected override void EmptyInputAssert(Action action) => + Assert.ThrowsAny(action); + + protected override X509Certificate2Collection LoadPfxCore( + byte[] bytes, + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadPkcs12Collection( + new ReadOnlySpan(bytes), + password.AsSpan(), + keyStorageFlags, + loaderLimits); + } + + protected override X509Certificate2Collection LoadPfxAtOffsetCore( + byte[] bytes, + int offset, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadPkcs12Collection( + bytes.AsSpan(offset), + password.AsSpan(), + keyStorageFlags, + loaderLimits); + } + + protected override X509Certificate2Collection LoadPfxFileOnlyCore( + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + // Use a strategy other than File.ReadAllBytes. + + using (FileStream stream = File.OpenRead(path)) + using (MemoryManager manager = MemoryMappedFileMemoryManager.CreateFromFileClamped(stream)) + { + return X509CertificateLoader.LoadPkcs12Collection( + manager.Memory.Span, + password.AsSpan(), + keyStorageFlags, + loaderLimits); + } + } + + protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) + { + contentType = X509ContentType.Unknown; + return false; + } + } + + [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support X.509 certificates")] + public class X509CertificateLoaderPkcs12CollectionTests_FromFile : X509CertificateLoaderPkcs12CollectionTests + { + protected override void NullInputAssert(Action action) => + AssertExtensions.Throws("path", action); + + protected override void EmptyInputAssert(Action action) => + AssertExtensions.Throws("path", action); + + protected override X509Certificate2Collection LoadPfxCore( + byte[] bytes, + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadPkcs12CollectionFromFile(path, password, keyStorageFlags, loaderLimits); + } + + protected override X509Certificate2Collection LoadPfxFileOnlyCore( + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return X509CertificateLoader.LoadPkcs12CollectionFromFile(path, password, keyStorageFlags, loaderLimits); + } + + protected override X509Certificate2Collection LoadPfxNoFileCore( + byte[] bytes, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + using (TempFileHolder holder = new TempFileHolder(bytes)) + { + return LoadPfx(bytes, holder.FilePath, password, keyStorageFlags, loaderLimits); + } + } + + protected override bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType) + { + if (path is null) + { + contentType = X509ContentType.Unknown; + return false; + } + + contentType = X509Certificate2.GetCertContentType(path); + return true; + } + } + + public abstract partial class X509CertificateLoaderPkcs12CollectionTests + { + private const int ERROR_INVALID_PASSWORD = -2147024810; + + protected static readonly X509KeyStorageFlags EphemeralIfPossible = +#if NETFRAMEWORK + X509KeyStorageFlags.DefaultKeySet; +#else + PlatformDetection.UsesAppleCrypto ? + X509KeyStorageFlags.DefaultKeySet : + X509KeyStorageFlags.EphemeralKeySet; +#endif + + protected abstract void NullInputAssert(Action action); + protected abstract void EmptyInputAssert(Action action); + + protected X509Certificate2Collection LoadPfx( + byte[] bytes, + string path, + string password = "", + X509KeyStorageFlags? keyStorageFlags = null, + Pkcs12LoaderLimits loaderLimits = null) + { + return LoadPfxCore( + bytes, + path, + password, + keyStorageFlags.GetValueOrDefault(EphemeralIfPossible), + loaderLimits); + } + + protected abstract X509Certificate2Collection LoadPfxCore( + byte[] bytes, + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits); + + protected X509Certificate2Collection LoadPfxFileOnly( + string path, + string password = "", + X509KeyStorageFlags? keyStorageFlags = null, + Pkcs12LoaderLimits loaderLimits = null) + { + return LoadPfxFileOnlyCore( + path, + password, + keyStorageFlags.GetValueOrDefault(EphemeralIfPossible), + loaderLimits); + } + + protected abstract X509Certificate2Collection LoadPfxFileOnlyCore( + string path, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits); + + protected X509Certificate2Collection LoadPfxNoFile( + byte[] bytes, + string password = "", + X509KeyStorageFlags? keyStorageFlags = null, + Pkcs12LoaderLimits loaderLimits = null) + { + return LoadPfxNoFileCore( + bytes, + password, + keyStorageFlags.GetValueOrDefault(EphemeralIfPossible), + loaderLimits); + } + + protected virtual X509Certificate2Collection LoadPfxNoFileCore( + byte[] bytes, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return LoadPfx(bytes, null, password, keyStorageFlags, loaderLimits); + } + + protected X509Certificate2Collection LoadPfxAtOffset( + byte[] bytes, + int offset, + string password = "", + X509KeyStorageFlags? keyStorageFlags = null, + Pkcs12LoaderLimits loaderLimits = null) + { + return LoadPfxAtOffsetCore( + bytes, + offset, + password, + keyStorageFlags.GetValueOrDefault(EphemeralIfPossible), + loaderLimits); + } + + protected virtual X509Certificate2Collection LoadPfxAtOffsetCore( + byte[] bytes, + int offset, + string password, + X509KeyStorageFlags keyStorageFlags, + Pkcs12LoaderLimits loaderLimits) + { + return LoadPfxNoFile( + bytes.AsSpan(offset).ToArray(), + password, + keyStorageFlags, + loaderLimits); + } + + protected abstract bool TryGetContentType(byte[] bytes, string path, out X509ContentType contentType); + + [Fact] + public void LoadNull() + { + NullInputAssert(() => LoadPfx(null, null, null)); + } + + [Fact] + public void LoadEmpty() + { + EmptyInputAssert(() => LoadPfx(Array.Empty(), string.Empty)); + } + + private void LoadKnownFormat_Fails(byte[] data, string path, X509ContentType contentType) + { + if (PlatformDetection.IsWindows || !X509CertificateLoaderTests.IsWindowsOnlyContentType(contentType)) + { + if (TryGetContentType(data, path, out X509ContentType actualType)) + { + Assert.Equal(contentType, actualType); + } + } + + if (path is null) + { + Assert.ThrowsAny(() => LoadPfxNoFile(data)); + } + else if (data is null) + { + Assert.ThrowsAny(() => LoadPfxFileOnly(path)); + } + else + { + Assert.ThrowsAny(() => LoadPfx(data, path)); + } + } + + [Fact] + public void LoadCertificate_DER_Fails() + { + LoadKnownFormat_Fails(TestData.MsCertificate, TestFiles.MsCertificateDerFile, X509ContentType.Cert); + } + + [Fact] + public void LoadCertificate_PEM_Fails() + { + LoadKnownFormat_Fails(TestData.MsCertificatePemBytes, TestFiles.MsCertificatePemFile, X509ContentType.Cert); + } + + [Fact] + public void LoadPkcs7_BER_Fails() + { + LoadKnownFormat_Fails(TestData.Pkcs7ChainDerBytes, TestFiles.Pkcs7ChainDerFile, X509ContentType.Pkcs7); + } + + [Fact] + public void LoadPkcs7_PEM_Fails() + { + LoadKnownFormat_Fails(TestData.Pkcs7ChainPemBytes, TestFiles.Pkcs7ChainPemFile, X509ContentType.Pkcs7); + } + + [Fact] + public void LoadSerializedCert_Fails() + { + LoadKnownFormat_Fails(TestData.StoreSavedAsSerializedCerData, null, X509ContentType.SerializedCert); + } + + [Fact] + public void LoadSerializedStore_Fails() + { + LoadKnownFormat_Fails(TestData.StoreSavedAsSerializedStoreData, null, X509ContentType.SerializedStore); + } + + [Fact] + public void LoadSignedFile_Fails() + { + LoadKnownFormat_Fails(null, TestFiles.SignedMsuFile, X509ContentType.Authenticode); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoadPfx_Single_WithPassword(bool ignorePrivateKeys) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IgnorePrivateKeys = ignorePrivateKeys, + }; + + X509Certificate2Collection coll = LoadPfx( + TestData.PfxData, + TestFiles.PfxFile, + TestData.PfxDataPassword, + EphemeralIfPossible, + loaderLimits); + + using (new CollectionDisposer(coll)) + { + Assert.Equal(1, coll.Count); + + X509Certificate2 cert = coll[0]; + Assert.Equal("CN=MyName", cert.Subject); + Assert.NotEqual(ignorePrivateKeys, cert.HasPrivateKey); + } + } + + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void LoadPfx_Single_NoPassword(bool ignorePrivateKeys, bool useNull) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IgnorePrivateKeys = ignorePrivateKeys, + }; + + string password = useNull ? null : ""; + + X509Certificate2Collection coll = LoadPfxNoFile( + TestData.PfxWithNoPassword, + password, + EphemeralIfPossible, + loaderLimits); + + using (new CollectionDisposer(coll)) + { + Assert.Equal(1, coll.Count); + + X509Certificate2 cert = coll[0]; + Assert.Equal("CN=MyName", cert.Subject); + Assert.NotEqual(ignorePrivateKeys, cert.HasPrivateKey); + } + } + + [ConditionalTheory(typeof(PlatformSupport), nameof(PlatformSupport.IsRC2Supported))] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void LoadPfx_Single_NoPassword_AmbiguousDecrypt(bool ignorePrivateKeys, bool useNull) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IgnorePrivateKeys = ignorePrivateKeys, + }; + + string password = useNull ? null : ""; + + X509Certificate2Collection coll = LoadPfxNoFile( + TestData.MsCertificateExportedToPfx_NullPassword, + password, + EphemeralIfPossible, + loaderLimits); + + using (new CollectionDisposer(coll)) + { + Assert.Equal(1, coll.Count); + + X509Certificate2 cert = coll[0]; + X509CertificateLoaderTests.AssertRawDataEquals(TestData.MsCertificate, cert); + Assert.False(cert.HasPrivateKey, "cert.HasPrivateKey"); + } + } + + [Fact] + public void LoadPfx_Single_WrongPassword() + { + CryptographicException ex = Assert.Throws( + () => LoadPfx(TestData.PfxData, TestFiles.PfxFile, "asdf")); + + Assert.Contains("password", ex.Message); + Assert.Equal(ERROR_INVALID_PASSWORD, ex.HResult); + } + + [Fact] + public void LoadPfx_Single_EmptyPassword_WithWrongPassword() + { + CryptographicException ex = Assert.Throws( + () => LoadPfxNoFile(TestData.PfxWithNoPassword, "asdf")); + + Assert.Contains("password", ex.Message); + Assert.Equal(ERROR_INVALID_PASSWORD, ex.HResult); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoadPfx_Single_EmptyPassword_NoMac(bool useEmpty) + { + string password = useEmpty ? "" : null; + + X509Certificate2Collection coll = LoadPfxNoFile( + TestData.Pkcs12OpenSslOneCertDefaultNoMac, + password, + EphemeralIfPossible); + + using (new CollectionDisposer(coll)) + { + Assert.Equal(1, coll.Count); + + X509Certificate2 cert = coll[0]; + Assert.Equal("CN=test", cert.Subject); + } + } + + [Fact] + public void LoadPfx_WithTrailingData() + { + byte[] data = TestData.PfxWithNoPassword; + Array.Resize(ref data, data.Length + 10); + + X509Certificate2Collection coll = LoadPfxNoFile(data); + + using (new CollectionDisposer(coll)) + { + Assert.Equal(1, coll.Count); + + X509Certificate2 cert = coll[0]; + Assert.Equal("CN=MyName", cert.Subject); + } + } + + [Fact] + public void LoadPfx_Empty() + { + X509Certificate2Collection coll = LoadPfxNoFile(TestData.EmptyPfx); + + using (new CollectionDisposer(coll)) + { + Assert.Equal(0, coll.Count); + } + } + + private void LoadPfx_VerifyLimit( + string propertyTested, + bool fail, + byte[] bytes, + string path, + string password, + Pkcs12LoaderLimits loaderLimits) + { + Func test; + + if (bytes is null) + { + test = () => LoadPfxFileOnly(path, password, EphemeralIfPossible, loaderLimits); + } + else if (path is null) + { + test = () => LoadPfxNoFile(bytes, password, EphemeralIfPossible, loaderLimits); + } + else + { + test = () => LoadPfx(bytes, path, password, EphemeralIfPossible, loaderLimits); + } + + if (fail) + { + Pkcs12LoadLimitExceededException ex = + AssertExtensions.Throws(() => test()); + + Assert.Contains(propertyTested, ex.Message); + } + else + { + // Assert.NoThrow + (new CollectionDisposer(test())).Dispose(); + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoadPfx_VerifyMacIterationLimit(bool failLimit) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + MacIterationLimit = failLimit ? 1999 : 2000, + }; + + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.MacIterationLimit), + failLimit, + TestData.PfxData, + TestFiles.PfxFile, + TestData.PfxDataPassword, + loaderLimits); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoadPfx_VerifyKdfIterationLimit(bool failLimit) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IndividualKdfIterationLimit = failLimit ? 1999 : 2000, + }; + + // Both 1999 and 2000 will fail, because the key uses 2001. + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.IndividualKdfIterationLimit), + fail: true, + TestData.MixedIterationsPfx, + null, + TestData.PlaceholderPw, + loaderLimits); + + loaderLimits.IgnorePrivateKeys = true; + + // Now that we're ignoring the key, 1999 will fail, 2000 will pass. + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.IndividualKdfIterationLimit), + failLimit, + TestData.MixedIterationsPfx, + null, + TestData.PlaceholderPw, + loaderLimits); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoadPfx_VerifyTotalKdfIterationLimit(bool failLimit) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + TotalKdfIterationLimit = failLimit ? 3999 : 4000, + }; + + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.TotalKdfIterationLimit), + failLimit, + TestData.PfxData, + TestFiles.PfxFile, + TestData.PfxDataPassword, + loaderLimits); + } + + [Theory] + [InlineData(null)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + public void LoadPfx_VerifyCertificateLimit(int? certLimit) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + MaxCertificates = certLimit, + }; + + bool expectFailure = certLimit.GetValueOrDefault(int.MaxValue) < 3; + + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.MaxCertificates), + expectFailure, + TestData.ChainPfxBytes, + TestFiles.ChainPfxFile, + TestData.ChainPfxPassword, + loaderLimits); + } + + [Theory] + [InlineData(null)] + [InlineData(0)] + [InlineData(1)] + [InlineData(4)] + public void LoadPfx_VerifyKeysLimit(int? keysLimit) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + MaxKeys = keysLimit, + }; + + bool expectFailure = keysLimit.GetValueOrDefault(int.MaxValue) < 1; + + LoadPfx_VerifyLimit( + nameof(Pkcs12LoaderLimits.MaxKeys), + expectFailure, + TestData.ChainPfxBytes, + TestFiles.ChainPfxFile, + TestData.ChainPfxPassword, + loaderLimits); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void LoadPfx_VerifyIgnoreEncryptedSafes(bool ignoreEncrypted) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IgnoreEncryptedAuthSafes = ignoreEncrypted, + }; + + const string PlaintextSubject = + "CN=Plaintext Test Certificate, OU=.NET Libraries, O=Microsoft Corporation"; + const string EncryptedSubject = + "CN=Encrypted Test Certificate, OU=.NET Libraries, O=Microsoft Corporation"; + + X509Certificate2Collection coll = LoadPfxNoFile( + TestData.TwoCertsPfx_OneEncrypted, + TestData.PlaceholderPw, + default, + loaderLimits); + + using (new CollectionDisposer(coll)) + { + if (ignoreEncrypted) + { + Assert.Equal(1, coll.Count); + + X509Certificate2 cert = coll[0]; + Assert.Equal(PlaintextSubject, cert.Subject); + } + else + { + Assert.Equal(2, coll.Count); + + X509Certificate2 cert = coll[0]; + Assert.Equal(EncryptedSubject, cert.Subject); + + cert = coll[1]; + Assert.Equal(PlaintextSubject, cert.Subject); + } + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void LoadPfx_VerifyIgnoreEncryptedSafes_EmptyIfIgnored(bool ignoreEncrypted) + { + Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits + { + IgnoreEncryptedAuthSafes = ignoreEncrypted, + }; + + X509Certificate2Collection coll = LoadPfx( + TestData.PfxData, + TestFiles.PfxFile, + TestData.PfxDataPassword, + default, + loaderLimits); + + using (new CollectionDisposer(coll)) + { + if (ignoreEncrypted) + { + Assert.Equal(0, coll.Count); + } + else + { + Assert.Equal(1, coll.Count); + + X509Certificate2 cert = coll[0]; + Assert.Equal("CN=MyName", cert.Subject); + } + } + } + + private sealed class CollectionDisposer : IDisposable + { + private readonly X509Certificate2Collection _coll; + + internal CollectionDisposer(X509Certificate2Collection coll) + { + _coll = coll; + } + + public void Dispose() + { + foreach (X509Certificate2 cert in _coll) + { + cert.Dispose(); + } + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs index a45036b832d973..04fe27d9409ea0 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs @@ -708,5 +708,32 @@ public void LoadPfx_VerifyIgnoreEncryptedSafes_EmptyIfIgnored(bool ignoreEncrypt } } } + + [Fact] + public void VerifyIndependentLifetime() + { + X509Certificate2 c1 = LoadPfx(TestData.PfxData, TestFiles.PfxFile, TestData.PfxDataPassword); + + using (X509Certificate2 c2 = LoadPfx(TestData.PfxData, TestFiles.PfxFile, TestData.PfxDataPassword)) + { + RSA rsa = c1.GetRSAPrivateKey(); + byte[] hash = new byte[SHA256.HashSizeInBytes]; + byte[] sig = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + Assert.Equal(TestData.PfxSha256Empty_ExpectedSig, sig); + + c1.Dispose(); + rsa.Dispose(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + // The c1 objects being disposed have no bearing on c2 + using (rsa = c2.GetRSAPrivateKey()) + { + sig = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + Assert.Equal(TestData.PfxSha256Empty_ExpectedSig, sig); + } + } + } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs index e0a80e825f7d34..fcaaf414f865ee 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs @@ -2,8 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Buffers.Binary; +using System.Diagnostics; using System.IO; using System.IO.MemoryMappedFiles; +using Test.Cryptography; using Xunit; namespace System.Security.Cryptography.X509Certificates.Tests @@ -274,12 +277,57 @@ public void LoadCertificate_DER_WithTrailingData() } } + [Fact] + public void LoadWrappingCertificate_PEM() + { + byte[] data = System.Text.Encoding.ASCII.GetBytes( + ByteUtils.PemEncode("CERTIFICATE", TestData.NestedCertificates)); + + using (X509Certificate2 cert = LoadCertificateNoFile(data)) + { + AssertRawDataEquals(TestData.NestedCertificates, cert); + } + } + + [Fact] + public void LoadWrappingCertificate_PEM_WithTrailingData() + { + byte[] source = TestData.NestedCertificates; + Array.Resize(ref source, source.Length + 4); + + BinaryPrimitives.WriteInt32LittleEndian( + source.AsSpan(TestData.NestedCertificates.Length), + Process.GetCurrentProcess().Id); + + byte[] data = System.Text.Encoding.ASCII.GetBytes( + ByteUtils.PemEncode("CERTIFICATE", source)); + + using (X509Certificate2 cert = LoadCertificateNoFile(data)) + { + AssertRawDataEquals(TestData.NestedCertificates, cert); + } + } + + [Fact] + public void LoadWrappingCertificate_PEM_WithSurroundingText() + { + string pem = ByteUtils.PemEncode("CERTIFICATE", TestData.NestedCertificates); + + byte[] data = System.Text.Encoding.ASCII.GetBytes( + "Four score and seven years ago ...\n" + pem + "... perish from this Earth."); + + using (X509Certificate2 cert = LoadCertificateNoFile(data)) + { + AssertRawDataEquals(TestData.NestedCertificates, cert); + } + } + internal static void AssertRawDataEquals(byte[] expected, X509Certificate2 cert) { #if NET - AssertExtensions.SequenceEqual(TestData.MsCertificate, cert.RawDataMemory.Span); + AssertExtensions.SequenceEqual(expected, cert.RawDataMemory.Span); #else - AssertExtensions.SequenceEqual(TestData.MsCertificate, cert.RawData); + AssertExtensions.SequenceEqual(expected, cert.RawData); #endif } diff --git a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj index 912f12fb54a63f..309d9b2e1c16b0 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj +++ b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj @@ -24,6 +24,8 @@ Link="CommonTest\System\Security\Cryptography\X509Certificates\TempFileHolder.cs" /> + + - diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs deleted file mode 100644 index 176f202c3258f9..00000000000000 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertLoaderTests.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.IO; -using Xunit; - -namespace System.Security.Cryptography.X509Certificates.Tests -{ - public class CertLoaderTests - { - public class NewLoaderFromArray : CommonTests - { - protected override X509Certificate2 LoadCertificate(byte[] data) => - X509CertificateLoader.LoadCertificate(data); - } - - public class NewLoaderFromSpan : CommonTests - { - protected override X509Certificate2 LoadCertificate(byte[] data) => - X509CertificateLoader.LoadCertificate(new ReadOnlySpan(data)); - } - - public class NewLoaderFromFile : CommonTests - { - protected override X509Certificate2 LoadCertificate(byte[] data) - { - using (TempFileHolder holder = new TempFileHolder(data)) - { - return X509CertificateLoader.LoadCertificateFromFile(holder.FilePath); - } - } - } - - public class LegacyLoaderFromArray : CommonTests - { - protected override X509Certificate2 LoadCertificate(byte[] data) => - new X509Certificate2(data); - } - - [ActiveIssue("NestedCertificates tests fail", TestPlatforms.Linux)] - public class LegacyLoaderFromFile : CommonTests - { - protected override X509Certificate2 LoadCertificate(byte[] data) - { - using (TempFileHolder holder = new TempFileHolder(data)) - { - return new X509Certificate2(holder.FilePath); - } - } - } - - public abstract class CommonTests - { - protected abstract X509Certificate2 LoadCertificate(byte[] data); - - [Fact] - public void LoadWrappingCertificate_DER() - { - using (X509Certificate2 cert = LoadCertificate(TestData.NestedCertificates)) - { - AssertExtensions.SequenceEqual(TestData.NestedCertificates, cert.RawDataMemory.Span); - } - } - - [Fact] - public void LoadWrappingCertificate_PEM() - { - byte[] data = System.Text.Encoding.ASCII.GetBytes( - PemEncoding.Write("CERTIFICATE", TestData.NestedCertificates)); - - using (X509Certificate2 cert = LoadCertificate(data)) - { - AssertExtensions.SequenceEqual(TestData.NestedCertificates, cert.RawDataMemory.Span); - } - } - - [Fact] - public void LoadWrappingCertificate_DER_Trailing() - { - byte[] source = TestData.NestedCertificates; - byte[] data = new byte[source.Length + 5]; - Array.Copy(source, 0, data, 0, source.Length); - RandomNumberGenerator.Fill(data.AsSpan(source.Length)); - - using (X509Certificate2 cert = LoadCertificate(data)) - { - AssertExtensions.SequenceEqual(TestData.NestedCertificates, cert.RawDataMemory.Span); - } - } - - [Fact] - [ActiveIssue("Fails as NewFile, NewSpan, NewArray, LegacyArray", TestPlatforms.Linux)] - public void LoadWrappingCertificate_PEM_TrailingInner() - { - byte[] source = TestData.NestedCertificates; - byte[] data = new byte[source.Length + 5]; - Array.Copy(source, 0, data, 0, source.Length); - RandomNumberGenerator.Fill(data.AsSpan(source.Length)); - - data = System.Text.Encoding.ASCII.GetBytes( - PemEncoding.Write("CERTIFICATE", data)); - - using (X509Certificate2 cert = LoadCertificate(data)) - { - AssertExtensions.SequenceEqual(TestData.NestedCertificates, cert.RawDataMemory.Span); - } - } - - [Fact] - [ActiveIssue("Fails as NewFile, NewSpan, NewArray, LegacyArray", TestPlatforms.Linux)] - public void LoadWrappingCertificate_PEM_Surround() - { - string pem = PemEncoding.WriteString("CERTIFICATE", TestData.NestedCertificates); - - byte[] data = System.Text.Encoding.ASCII.GetBytes( - "Four score and seven years ago ...\n" + pem + "... perish from this Earth."); - - using (X509Certificate2 cert = LoadCertificate(data)) - { - AssertExtensions.SequenceEqual(TestData.NestedCertificates, cert.RawDataMemory.Span); - } - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionImportTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionImportTests.cs index 38605cdc5e2be9..a789367bac6a53 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionImportTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionImportTests.cs @@ -30,19 +30,6 @@ public static void ImportEmpty_Pkcs12() } } - [Fact] - public static void ImportEmpty_Pkcs12_NL() - { - X509Certificate2Collection collection = X509CertificateLoader.LoadPkcs12Collection( - TestData.EmptyPfx, - (string?)null); - - using (ImportedCollection ic = new ImportedCollection(collection)) - { - Assert.Equal(0, collection.Count); - } - } - [Fact] public static void ImportX509DerBytes() { @@ -267,21 +254,6 @@ public static void ImportPkcs12Bytes_Single_VerifyContents_ArrayString(X509KeySt } } - [Theory] - [MemberData(nameof(StorageFlags))] - public static void ImportPkcs12Bytes_Single_VerifyContents_ArrayString_NL(X509KeyStorageFlags keyStorageFlags) - { - X509Certificate2Collection coll = X509CertificateLoader.LoadPkcs12Collection( - TestData.PfxData, - TestData.PfxDataPassword, - keyStorageFlags); - - using (ImportedCollection ic = new ImportedCollection(coll)) - { - ImportPkcs12Bytes_Single_VerifyContents(ic); - } - } - [Theory] [MemberData(nameof(StorageFlags))] public static void ImportPkcs12Bytes_Single_VerifyContents_SpanSpan(X509KeyStorageFlags keyStorageFlags) @@ -295,24 +267,6 @@ public static void ImportPkcs12Bytes_Single_VerifyContents_SpanSpan(X509KeyStora } } - [Theory] - [MemberData(nameof(StorageFlags))] - public static void ImportPkcs12Bytes_Single_VerifyContents_SpanSpan_NL(X509KeyStorageFlags keyStorageFlags) - { - ReadOnlySpan rawData = TestData.PfxData.AsSpan(); - ReadOnlySpan password = TestData.PfxDataPassword.AsSpan(); - - X509Certificate2Collection coll = X509CertificateLoader.LoadPkcs12Collection( - rawData, - password, - keyStorageFlags); - - using (ImportedCollection ic = new ImportedCollection(coll)) - { - ImportPkcs12Bytes_Single_VerifyContents(ic); - } - } - private static void ImportPkcs12Bytes_Single_VerifyContents(ImportedCollection ic) { using (var pfxCer = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, Cert.EphemeralIfPossible)) @@ -344,22 +298,6 @@ public static void ImportPkcs12File_Single(X509KeyStorageFlags keyStorageFlags) } } - [Theory] - [MemberData(nameof(StorageFlags))] - public static void ImportPkcs12File_Single_NL(X509KeyStorageFlags keyStorageFlags) - { - X509Certificate2Collection cc2 = X509CertificateLoader.LoadPkcs12CollectionFromFile( - TestFiles.PfxFile, - TestData.PfxDataPassword, - keyStorageFlags); - - using (ImportedCollection ic = new ImportedCollection(cc2)) - { - int count = cc2.Count; - Assert.Equal(1, count); - } - } - [Theory] [MemberData(nameof(StorageFlags))] public static void ImportPkcs12File_Single_SpanPassword(X509KeyStorageFlags keyStorageFlags) diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs index 160c2a722f6c56..2847c4221109c0 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CtorTests.cs @@ -88,11 +88,6 @@ public static void TestConstructor_DER() assert(c2); } } - - using (X509Certificate2 c3 = X509CertificateLoader.LoadCertificate(TestData.MsCertificate)) - { - assert(c3); - } } [Fact] @@ -124,11 +119,6 @@ public static void TestConstructor_PEM() assert(c2); } } - - using (X509Certificate2 c3 = X509CertificateLoader.LoadCertificate(TestData.MsCertificatePemBytes)) - { - assert(c3); - } } [Fact] @@ -208,33 +198,6 @@ public static void TestCopyConstructor_Lifetime_Independent() } } - [Fact] - public static void Loader_Lifetime_Independent() - { - X509Certificate2 c1 = X509CertificateLoader.LoadPkcs12(TestData.PfxData, TestData.PfxDataPassword); - using (X509Certificate2 c2 = X509CertificateLoader.LoadPkcs12(TestData.PfxData, TestData.PfxDataPassword)) - { - RSA rsa = c2.GetRSAPrivateKey(); - byte[] hash = new byte[SHA256.HashSizeInBytes]; - byte[] sig = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - Assert.Equal(TestData.PfxSha256Empty_ExpectedSig, sig); - - c1.Dispose(); - rsa.Dispose(); - - GC.Collect(); - GC.WaitForPendingFinalizers(); - - // Verify other cert and previous key do not affect cert - using (rsa = c2.GetRSAPrivateKey()) - { - hash = new byte[SHA256.HashSizeInBytes]; - sig = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - Assert.Equal(TestData.PfxSha256Empty_ExpectedSig, sig); - } - } - } - [Fact] public static void TestCopyConstructor_Lifetime_Cloned() { diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/LoadFromFileTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/LoadFromFileTests.cs index e22832785fd2a8..222b468ff33672 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/LoadFromFileTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/LoadFromFileTests.cs @@ -55,25 +55,6 @@ public static void TestSerial() } } - [Fact] - public static void TestSerialFromNewLoader() - { - string expectedSerialHex = "33000000B011AF0A8BD03B9FDD0001000000B0"; - byte[] expectedSerial = "B00000000100DD9F3BD08B0AAF11B000000033".HexToByteArray(); - - using (X509Certificate2 c = X509CertificateLoader.LoadCertificateFromFile(TestFiles.MsCertificateDerFile)) - { - byte[] serial = c.GetSerialNumber(); - Assert.Equal(expectedSerial, serial); - string serialHex = c.GetSerialNumberString(); - Assert.Equal(expectedSerialHex, serialHex); - serialHex = c.SerialNumber; - Assert.Equal(expectedSerialHex, serialHex); - - Assert.Equal(expectedSerialHex, c.SerialNumberBytes.ByteArrayToHex()); - } - } - [Fact] public static void TestThumbprint() { diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs index fd7bb4f65a5d54..452d3ad02d534f 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs @@ -59,8 +59,7 @@ private void ReadMultiPfx( string correctPassword, X509Certificate2 expectedSingleCert, X509Certificate2[] expectedOrder, - Action perCertOtherWork = null, - bool newOnly = false) + Action perCertOtherWork = null) { ReadMultiPfx( pfxBytes, @@ -68,8 +67,7 @@ private void ReadMultiPfx( expectedSingleCert, expectedOrder, s_importFlags, - perCertOtherWork, - newOnly); + perCertOtherWork); } protected abstract void ReadPfx( @@ -85,8 +83,7 @@ protected abstract void ReadMultiPfx( X509Certificate2 expectedSingleCert, X509Certificate2[] expectedOrder, X509KeyStorageFlags nonExportFlags, - Action perCertOtherWork = null, - bool newOnly = false); + Action perCertOtherWork = null); private void ReadUnreadablePfx( byte[] pfxBytes, @@ -1219,16 +1216,14 @@ public void TwoCerts_TwoKeys_ManySafeContentsValues_UnencryptedAuthSafes_NoMac(b "", first, expectedOrder, - followup, - newOnly: true); + followup); ReadMultiPfx( pfxBytes, null, first, expectedOrder, - followup, - newOnly: true); + followup); } } } diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_Collection.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_Collection.cs index 78debf84db3f91..8de41a83c80a81 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_Collection.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_Collection.cs @@ -20,8 +20,6 @@ protected override void ReadPfx( ReadPfx(pfxBytes, correctPassword, expectedCert, null, otherWork, nonExportFlags); ReadPfx(pfxBytes, correctPassword, expectedCert, null, otherWork, exportFlags); - ReadPfxFromNewLoader(pfxBytes, correctPassword, expectedCert, null, otherWork, nonExportFlags); - ReadPfxFromNewLoader(pfxBytes, correctPassword, expectedCert, null, otherWork, exportFlags); } protected override void ReadMultiPfx( @@ -30,29 +28,9 @@ protected override void ReadMultiPfx( X509Certificate2 expectedSingleCert, X509Certificate2[] expectedOrder, X509KeyStorageFlags nonExportFlags, - Action perCertOtherWork, - bool newOnly) + Action perCertOtherWork) { - if (!newOnly) - { - ReadPfx( - pfxBytes, - correctPassword, - expectedSingleCert, - expectedOrder, - perCertOtherWork, - nonExportFlags); - - ReadPfx( - pfxBytes, - correctPassword, - expectedSingleCert, - expectedOrder, - perCertOtherWork, - nonExportFlags | X509KeyStorageFlags.Exportable); - } - - ReadPfxFromNewLoader( + ReadPfx( pfxBytes, correctPassword, expectedSingleCert, @@ -60,7 +38,7 @@ protected override void ReadMultiPfx( perCertOtherWork, nonExportFlags); - ReadPfxFromNewLoader( + ReadPfx( pfxBytes, correctPassword, expectedSingleCert, @@ -95,36 +73,6 @@ private void ReadPfx( } } - private void ReadPfxFromNewLoader( - byte[] pfxBytes, - string correctPassword, - X509Certificate2 expectedCert, - X509Certificate2[] expectedOrder, - Action otherWork, - X509KeyStorageFlags flags) - { - X509Certificate2Collection coll = X509CertificateLoader.LoadPkcs12Collection( - pfxBytes, - correctPassword, - flags); - - using (ImportedCollection imported = new ImportedCollection(coll)) - { - Assert.Equal(expectedOrder?.Length ?? 1, coll.Count); - - Span testOrder = expectedOrder == null ? - MemoryMarshal.CreateSpan(ref expectedCert, 1) : - expectedOrder.AsSpan(); - - for (int i = 0; i < testOrder.Length; i++) - { - X509Certificate2 actual = coll[i]; - AssertCertEquals(testOrder[i], actual); - otherWork?.Invoke(actual); - } - } - } - protected override void ReadEmptyPfx(byte[] pfxBytes, string correctPassword) { X509Certificate2Collection coll = new X509Certificate2Collection(); diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_SingleCert.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_SingleCert.cs index b2a480d450e609..c2368dfdd38f23 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_SingleCert.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests_SingleCert.cs @@ -19,8 +19,6 @@ protected override void ReadPfx( ReadPfx(pfxBytes, correctPassword, expectedCert, otherWork, nonExportFlags); ReadPfx(pfxBytes, correctPassword, expectedCert, otherWork, exportFlags); - ReadPfxFromLoader(pfxBytes, correctPassword, expectedCert, otherWork, nonExportFlags); - ReadPfxFromLoader(pfxBytes, correctPassword, expectedCert, otherWork, exportFlags); } protected override void ReadMultiPfx( @@ -29,19 +27,12 @@ protected override void ReadMultiPfx( X509Certificate2 expectedSingleCert, X509Certificate2[] expectedOrder, X509KeyStorageFlags nonExportFlags, - Action perCertOtherWork, - bool newOnly) + Action perCertOtherWork) { X509KeyStorageFlags exportFlags = nonExportFlags | X509KeyStorageFlags.Exportable; - if (!newOnly) - { - ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, nonExportFlags); - ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, exportFlags); - } - - ReadPfxFromLoader(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, nonExportFlags); - ReadPfxFromLoader(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, exportFlags); + ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, nonExportFlags); + ReadPfx(pfxBytes, correctPassword, expectedSingleCert, perCertOtherWork, exportFlags); } private void ReadPfx( @@ -58,20 +49,6 @@ private void ReadPfx( } } - private void ReadPfxFromLoader( - byte[] pfxBytes, - string correctPassword, - X509Certificate2 expectedCert, - Action otherWork, - X509KeyStorageFlags flags) - { - using (X509Certificate2 cert = X509CertificateLoader.LoadPkcs12(pfxBytes, correctPassword, flags)) - { - AssertCertEquals(expectedCert, cert); - otherWork?.Invoke(cert); - } - } - protected override void ReadEmptyPfx(byte[] pfxBytes, string correctPassword) { CryptographicException ex = Assert.Throws( From efb17e3b5a525cb2aeb2fc3f681b6c7d5234c766 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 20 Jun 2024 15:07:14 -0700 Subject: [PATCH 10/19] Fix tests on Linux --- .../X509Certificates/X509CertificateLoaderTests.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs index fcaaf414f865ee..0fda3fb5d400d6 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs @@ -302,6 +302,12 @@ public void LoadWrappingCertificate_PEM_WithTrailingData() byte[] data = System.Text.Encoding.ASCII.GetBytes( ByteUtils.PemEncode("CERTIFICATE", source)); + if (OperatingSystem.IsLinux()) + { + Assert.Throws(() => LoadCertificateNoFile(data)); + return; + } + using (X509Certificate2 cert = LoadCertificateNoFile(data)) { AssertRawDataEquals(TestData.NestedCertificates, cert); @@ -314,7 +320,7 @@ public void LoadWrappingCertificate_PEM_WithSurroundingText() string pem = ByteUtils.PemEncode("CERTIFICATE", TestData.NestedCertificates); byte[] data = System.Text.Encoding.ASCII.GetBytes( - "Four score and seven years ago ...\n" + pem + "... perish from this Earth."); + "Four score and seven years ago ...\n" + pem + "\n... perish from this Earth."); using (X509Certificate2 cert = LoadCertificateNoFile(data)) { From df8503478d368cdea5ff648a99d7d8b8bb466aa6 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 20 Jun 2024 15:10:18 -0700 Subject: [PATCH 11/19] Fix test build for netfx --- .../X509Certificates/X509CertificateLoaderPkcs12Tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs index 04fe27d9409ea0..6568be9756eb2f 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs @@ -717,7 +717,7 @@ public void VerifyIndependentLifetime() using (X509Certificate2 c2 = LoadPfx(TestData.PfxData, TestFiles.PfxFile, TestData.PfxDataPassword)) { RSA rsa = c1.GetRSAPrivateKey(); - byte[] hash = new byte[SHA256.HashSizeInBytes]; + byte[] hash = new byte[256 >> 3]; byte[] sig = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); Assert.Equal(TestData.PfxSha256Empty_ExpectedSig, sig); From 11513f0a0eabbbdf0c536f0a947d62d4f98a1eb3 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 20 Jun 2024 16:37:17 -0700 Subject: [PATCH 12/19] Turn ActiveIssue (skip) into expected failures --- .../X509Certificates/X509CertificateLoaderTests.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs index 0fda3fb5d400d6..ac4a325e7dee89 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs @@ -248,7 +248,6 @@ public void LoadNestedCertificates() } [Fact] - [ActiveIssue("macOS seems to not like the PEM post-EB not followed by a newline or EOF", TestPlatforms.OSX)] public void LoadCertificate_WithTrailingData() { // Find the PEM-encoded certificate embedded within NestedCertificates, and @@ -259,6 +258,13 @@ public void LoadCertificate_WithTrailingData() Span needle = stackalloc byte[] { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D }; int offset = data.AsSpan().IndexOf(needle); + // The macOS PEM loader seems to be rejecting the trailing data. + if (OperatingSystem.IsMacOS()) + { + Assert.ThrowsAny(() => LoadCertificateAtOffset(data, offset)); + return; + } + using (X509Certificate2 cert = LoadCertificateAtOffset(data, offset)) { Assert.Equal("CN=inner", cert.Subject); @@ -302,6 +308,7 @@ public void LoadWrappingCertificate_PEM_WithTrailingData() byte[] data = System.Text.Encoding.ASCII.GetBytes( ByteUtils.PemEncode("CERTIFICATE", source)); + // OpenSSL is being more strict here than other platforms. if (OperatingSystem.IsLinux()) { Assert.Throws(() => LoadCertificateNoFile(data)); From 39075b3dcd8e0d23b4a53f80566017653a98562d Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 20 Jun 2024 16:39:17 -0700 Subject: [PATCH 13/19] Use more varied subject names in WindowsAttribute tests --- .../X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs index 873b8ff562225d..444094c15b0ca8 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs @@ -186,7 +186,7 @@ private static byte[] MakeAttributeTest( } CertificateRequest req = new CertificateRequest( - $"CN={testName}", + $"CN={testName}-{keyName}-{friendlyName}", key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); From 96ed4b9e7ebe9077feb51bc87a8bec86943129b7 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Fri, 21 Jun 2024 01:45:40 -0700 Subject: [PATCH 14/19] Fix netfx test build --- .../X509CertificateLoader.Pkcs12.cs | 42 ++++++++++++ .../X509CertificateLoaderTests.cs | 4 ++ .../Microsoft.Bcl.Cryptography.sln | 22 +++++-- .../System.Security.Cryptography.sln | 32 +++++---- .../tests/X509Certificates/PfxFormatTests.cs | 65 +++++++++++++++++++ 5 files changed, 146 insertions(+), 19 deletions(-) diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs index 75b953d20c19c5..a7cb3423430391 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Collections.Generic; using System.Diagnostics; using System.Formats.Asn1; using System.Security.Cryptography.Asn1; @@ -392,6 +393,47 @@ private static void FilterAttributes( } } + { + // Look for duplicates in any attributes that were accepted by the filter. + // For known-values-only (the default), the limit is 3 for keys and 2 for certs. + // + // Allowing for a minimal amount of unknown attributes to be preserved, we can + // no-alloc check for duplicates with the N^2 algorithm, and use HashSet for + // degenerate cases. + const int NoAllocComparisonLimit = 5; + + if (attrIdx <= 0) + { + // -1: No attributes, no duplicates. + // 0: One attribute, no duplicates + } + else if (attrIdx < NoAllocComparisonLimit) + { + for (int i = attrIdx; i >= 0; i--) + { + for (int j = i - 1; j >= 0; j--) + { + if (bag.BagAttributes[i].AttrType == bag.BagAttributes[j].AttrType) + { + ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE); + } + } + } + } + else + { + HashSet dedup = new HashSet(); + + for (int i = attrIdx; i >= 0; i--) + { + if (!dedup.Add(bag.BagAttributes[i].AttrType)) + { + ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE); + } + } + } + } + attrIdx++; if (attrIdx < bag.BagAttributes.Length) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs index ac4a325e7dee89..f29eaf7c0e0235 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs @@ -258,12 +258,14 @@ public void LoadCertificate_WithTrailingData() Span needle = stackalloc byte[] { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D }; int offset = data.AsSpan().IndexOf(needle); +#if NET // The macOS PEM loader seems to be rejecting the trailing data. if (OperatingSystem.IsMacOS()) { Assert.ThrowsAny(() => LoadCertificateAtOffset(data, offset)); return; } +#endif using (X509Certificate2 cert = LoadCertificateAtOffset(data, offset)) { @@ -308,12 +310,14 @@ public void LoadWrappingCertificate_PEM_WithTrailingData() byte[] data = System.Text.Encoding.ASCII.GetBytes( ByteUtils.PemEncode("CERTIFICATE", source)); +#if NET // OpenSSL is being more strict here than other platforms. if (OperatingSystem.IsLinux()) { Assert.Throws(() => LoadCertificateNoFile(data)); return; } +#endif using (X509Certificate2 cert = LoadCertificateNoFile(data)) { diff --git a/src/libraries/Microsoft.Bcl.Cryptography/Microsoft.Bcl.Cryptography.sln b/src/libraries/Microsoft.Bcl.Cryptography/Microsoft.Bcl.Cryptography.sln index 7b4972e18f9fc4..0a13f73c96f079 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/Microsoft.Bcl.Cryptography.sln +++ b/src/libraries/Microsoft.Bcl.Cryptography/Microsoft.Bcl.Cryptography.sln @@ -1,4 +1,8 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34804.81 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{1AEF7C7B-5A86-4A5E-9F8B-3933F7624751}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.Cryptography", "ref\Microsoft.Bcl.Cryptography.csproj", "{63655B2E-6A06-4E48-9F01-D0B910063165}" @@ -29,11 +33,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{98708A22-726 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{34897637-11A1-48A4-AF1F-E11463A61D0B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{568C437D-072B-48BA-BBB4-20F39E2ADA15}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{568C437D-072B-48BA-BBB4-20F39E2ADA15}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{AEDD1BBB-88D7-4CC1-9262-D94341DAD308}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{AEDD1BBB-88D7-4CC1-9262-D94341DAD308}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{3AFC9F72-C997-4066-86FD-EDA22F432C73}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{3AFC9F72-C997-4066-86FD-EDA22F432C73}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{98BFC458-097E-4C6C-BAE8-03199531D531}" EndProject @@ -93,21 +97,25 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {1AEF7C7B-5A86-4A5E-9F8B-3933F7624751} = {8C3BD4AD-1A56-4204-9826-F8B74251D19F} - {E66D17AB-BBAF-4F2B-AC9C-8E89BDCC6191} = {8C3BD4AD-1A56-4204-9826-F8B74251D19F} {63655B2E-6A06-4E48-9F01-D0B910063165} = {7E9F6DE1-771B-4E25-A603-EC43D0291C8B} {B0716D7E-B824-4866-A1ED-DF31BA2970B9} = {98708A22-7268-4EDB-AE37-70AA958A772A} + {E66D17AB-BBAF-4F2B-AC9C-8E89BDCC6191} = {8C3BD4AD-1A56-4204-9826-F8B74251D19F} {E9271403-BEF5-46E9-B68B-16EF69AA7149} = {34897637-11A1-48A4-AF1F-E11463A61D0B} {A4C2BDDC-1AFB-45A8-9E9B-4AD7396A4DF2} = {34897637-11A1-48A4-AF1F-E11463A61D0B} {777AF5DE-3EB3-4E62-A0D8-CA10D77B5343} = {568C437D-072B-48BA-BBB4-20F39E2ADA15} {94384409-052F-4697-B235-8990C851B6A4} = {568C437D-072B-48BA-BBB4-20F39E2ADA15} - {568C437D-072B-48BA-BBB4-20F39E2ADA15} = {98BFC458-097E-4C6C-BAE8-03199531D531} {A243E975-A17E-4DA8-91AC-7FCB52EAB921} = {AEDD1BBB-88D7-4CC1-9262-D94341DAD308} {7C971319-753D-4D49-B242-994260ABD9AF} = {AEDD1BBB-88D7-4CC1-9262-D94341DAD308} - {AEDD1BBB-88D7-4CC1-9262-D94341DAD308} = {98BFC458-097E-4C6C-BAE8-03199531D531} {E4477F0C-A13A-4C1A-9A70-0A287E09E9EA} = {3AFC9F72-C997-4066-86FD-EDA22F432C73} + {568C437D-072B-48BA-BBB4-20F39E2ADA15} = {98BFC458-097E-4C6C-BAE8-03199531D531} + {AEDD1BBB-88D7-4CC1-9262-D94341DAD308} = {98BFC458-097E-4C6C-BAE8-03199531D531} {3AFC9F72-C997-4066-86FD-EDA22F432C73} = {98BFC458-097E-4C6C-BAE8-03199531D531} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EAA35B12-9858-4428-8510-F09B19933FB9} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{7c971319-753d-4d49-b242-994260abd9af}*SharedItemsImports = 5 + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{94384409-052f-4697-b235-8990c851b6a4}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/src/libraries/System.Security.Cryptography/System.Security.Cryptography.sln b/src/libraries/System.Security.Cryptography/System.Security.Cryptography.sln index 57220bcd94caf7..8429ef873d1359 100644 --- a/src/libraries/System.Security.Cryptography/System.Security.Cryptography.sln +++ b/src/libraries/System.Security.Cryptography/System.Security.Cryptography.sln @@ -1,4 +1,8 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34212.112 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StreamConformanceTests", "..\Common\tests\StreamConformanceTests\StreamConformanceTests.csproj", "{66FE8701-0F3C-4524-ACB6-404E13221733}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{0C78AEB2-80BD-4B1B-B5CD-F038522D065D}" @@ -55,11 +59,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A16B11D7-673 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{2B165D96-356D-409E-88BE-0CA0F407303E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{CC457FED-75A7-450C-91C9-F0F4AC6FF4A9}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{CC457FED-75A7-450C-91C9-F0F4AC6FF4A9}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{7D4583A4-938F-4A2F-945D-9FD4FB4F0110}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7D4583A4-938F-4A2F-945D-9FD4FB4F0110}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{1D7DE530-F078-4076-8D77-0A145DD165F6}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{1D7DE530-F078-4076-8D77-0A145DD165F6}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{5F3BF122-7954-4A83-8196-E8470B0B9C80}" EndProject @@ -172,33 +176,37 @@ Global GlobalSection(NestedProjects) = preSolution {66FE8701-0F3C-4524-ACB6-404E13221733} = {B3BB2DA0-53B8-4B49-999F-CD650EB61040} {0C78AEB2-80BD-4B1B-B5CD-F038522D065D} = {B3BB2DA0-53B8-4B49-999F-CD650EB61040} - {A242B3AB-EE45-4C95-9550-DB970BA23CD5} = {B3BB2DA0-53B8-4B49-999F-CD650EB61040} {BEE9B8AD-4BB4-43F9-B718-AC967E1DA092} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {EE43BF44-3757-4401-86E9-CCC789BA89E4} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {B1EA9B57-3B9F-47FD-BE76-01D3D672C88C} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {D5032AC3-A2A7-4B6A-93CB-2448DEB0B492} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} + {3B1486E4-DCDE-4629-AACC-D1A6FF8A1993} = {A16B11D7-6732-45E2-894B-D5AFA879B68A} {DEDBB595-C6B5-4084-8193-2F080A385DBB} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} + {0EA41D6A-87A2-42B7-8171-78E167570993} = {2B165D96-356D-409E-88BE-0CA0F407303E} + {9E7AD0F4-0D4D-4E42-8C6C-513C006E0590} = {2B165D96-356D-409E-88BE-0CA0F407303E} + {1B999BF7-657A-4342-A97E-B5CC5916AFB4} = {2B165D96-356D-409E-88BE-0CA0F407303E} {6A4AB150-B689-41A4-9DAD-B4C8A22709A6} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {DBEEA00C-86D6-4C87-A7EF-1171D7C25F59} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {24FAFCA3-8747-4E8A-BA2B-996A8F411C80} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {258B22AA-ECA6-412E-9F29-6BB6063CD036} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} - {E9BF1F4D-7D5C-4539-B426-2FCCB2585138} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} - {3B1486E4-DCDE-4629-AACC-D1A6FF8A1993} = {A16B11D7-6732-45E2-894B-D5AFA879B68A} {E72C02D0-CC28-4FE2-8A2F-D91ABD990065} = {A16B11D7-6732-45E2-894B-D5AFA879B68A} + {E9BF1F4D-7D5C-4539-B426-2FCCB2585138} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {AF227727-11A6-4E5F-98DE-C362FC829633} = {A16B11D7-6732-45E2-894B-D5AFA879B68A} - {0EA41D6A-87A2-42B7-8171-78E167570993} = {2B165D96-356D-409E-88BE-0CA0F407303E} - {9E7AD0F4-0D4D-4E42-8C6C-513C006E0590} = {2B165D96-356D-409E-88BE-0CA0F407303E} - {1B999BF7-657A-4342-A97E-B5CC5916AFB4} = {2B165D96-356D-409E-88BE-0CA0F407303E} + {A242B3AB-EE45-4C95-9550-DB970BA23CD5} = {B3BB2DA0-53B8-4B49-999F-CD650EB61040} {7D70A668-5A55-439F-9FB8-8FF2A850CF91} = {CC457FED-75A7-450C-91C9-F0F4AC6FF4A9} {390C4E57-0F24-4E07-AFFB-AFBE67D58A52} = {CC457FED-75A7-450C-91C9-F0F4AC6FF4A9} - {CC457FED-75A7-450C-91C9-F0F4AC6FF4A9} = {5F3BF122-7954-4A83-8196-E8470B0B9C80} {93B28BF5-88A1-4763-913C-6F59F291676A} = {7D4583A4-938F-4A2F-945D-9FD4FB4F0110} {CF172501-7056-49E7-93E2-787BD4D95DF1} = {7D4583A4-938F-4A2F-945D-9FD4FB4F0110} - {7D4583A4-938F-4A2F-945D-9FD4FB4F0110} = {5F3BF122-7954-4A83-8196-E8470B0B9C80} {0D113CC7-98F1-4323-B064-25A4EC6DA5A8} = {1D7DE530-F078-4076-8D77-0A145DD165F6} + {CC457FED-75A7-450C-91C9-F0F4AC6FF4A9} = {5F3BF122-7954-4A83-8196-E8470B0B9C80} + {7D4583A4-938F-4A2F-945D-9FD4FB4F0110} = {5F3BF122-7954-4A83-8196-E8470B0B9C80} {1D7DE530-F078-4076-8D77-0A145DD165F6} = {5F3BF122-7954-4A83-8196-E8470B0B9C80} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F5615383-F387-45EA-A05A-CB65D8A6FE88} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{390c4e57-0f24-4e07-affb-afbe67d58a52}*SharedItemsImports = 5 + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{cf172501-7056-49e7-93e2-787bd4d95df1}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs index 452d3ad02d534f..04019b838401b4 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs @@ -195,6 +195,24 @@ public void OneCert_NoKeys_EncryptedEmptyPassword_NoMac() } } + [ConditionalFact(typeof(Environment), nameof(Environment.HasShutdownStarted))] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "The PKCS#12 Exportable flag is not supported on iOS/MacCatalyst/tvOS")] + public void OneCert_NoKeys_EncryptedEmptyPassword_NullMac() + { + 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.SealWithMac(null, HashAlgorithmName.SHA1, 37); + byte[] pfxBytes = builder.Encode(); + + ReadPfx(pfxBytes, null, cert); + ReadPfx(pfxBytes, string.Empty, cert); + } + } + [Theory] [InlineData(false, false)] [InlineData(false, true)] @@ -1228,6 +1246,53 @@ public void TwoCerts_TwoKeys_ManySafeContentsValues_UnencryptedAuthSafes_NoMac(b } } + [ConditionalTheory(typeof(Environment), nameof(Environment.HasShutdownStarted))] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public void TwoCerts_TwoKeys_DuplicateLocalKeyId(int itemIndex) + { + string pw = nameof(TwoCerts_TwoKeys_DuplicateLocalKeyId); + byte[] pfx; + + using (ImportedCollection ic = Cert.Import(TestData.MultiPrivateKeyPfx, null, s_exportableImportFlags)) + { + X509Certificate2Collection certs = ic.Collection; + X509Certificate2 first = certs[0]; + X509Certificate2 second = certs[1]; + + using (AsymmetricAlgorithm firstKey = first.GetRSAPrivateKey()) + using (AsymmetricAlgorithm secondKey = second.GetRSAPrivateKey()) + { + Pkcs12Builder builder = new Pkcs12Builder(); + Pkcs12SafeContents certContents = new Pkcs12SafeContents(); + Pkcs12SafeContents keyContents = new Pkcs12SafeContents(); + + Pkcs12CertBag certBagOne = certContents.AddCertificate(first); + Pkcs12CertBag certBagTwo = certContents.AddCertificate(second); + Pkcs12ShroudedKeyBag keyBagOne = keyContents.AddShroudedKey(firstKey, pw, s_windowsPbe); + Pkcs12ShroudedKeyBag keyBagTwo = keyContents.AddShroudedKey(secondKey, pw, s_windowsPbe); + + Pkcs12SafeBag[] bags = { certBagOne, certBagTwo, keyBagOne, keyBagTwo }; + bags[itemIndex].Attributes.Add(s_keyIdOne); + + certBagOne.Attributes.Add(s_keyIdOne); + keyBagOne.Attributes.Add(s_keyIdOne); + certBagTwo.Attributes.Add(s_keyIdTwo); + keyBagTwo.Attributes.Add(s_keyIdTwo); + + builder.AddSafeContentsUnencrypted(keyContents); + builder.AddSafeContentsEncrypted(certContents, pw, s_windowsPbe); + + builder.SealWithMac(pw, HashAlgorithmName.SHA1, 22); + pfx = builder.Encode(); + } + } + + ReadUnreadablePfx(pfx, pw); + } + private static void CheckKeyConsistency(X509Certificate2 cert) { byte[] data = { 2, 7, 4 }; From 3a24bebbfe950c3473ce3e8da8368722c979be4a Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Fri, 21 Jun 2024 01:48:11 -0700 Subject: [PATCH 15/19] Revert "Fix netfx test build" --- .../X509CertificateLoader.Pkcs12.cs | 42 ------------ .../X509CertificateLoaderTests.cs | 4 -- .../Microsoft.Bcl.Cryptography.sln | 22 ++----- .../System.Security.Cryptography.sln | 32 ++++----- .../tests/X509Certificates/PfxFormatTests.cs | 65 ------------------- 5 files changed, 19 insertions(+), 146 deletions(-) diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs index a7cb3423430391..75b953d20c19c5 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; -using System.Collections.Generic; using System.Diagnostics; using System.Formats.Asn1; using System.Security.Cryptography.Asn1; @@ -393,47 +392,6 @@ private static void FilterAttributes( } } - { - // Look for duplicates in any attributes that were accepted by the filter. - // For known-values-only (the default), the limit is 3 for keys and 2 for certs. - // - // Allowing for a minimal amount of unknown attributes to be preserved, we can - // no-alloc check for duplicates with the N^2 algorithm, and use HashSet for - // degenerate cases. - const int NoAllocComparisonLimit = 5; - - if (attrIdx <= 0) - { - // -1: No attributes, no duplicates. - // 0: One attribute, no duplicates - } - else if (attrIdx < NoAllocComparisonLimit) - { - for (int i = attrIdx; i >= 0; i--) - { - for (int j = i - 1; j >= 0; j--) - { - if (bag.BagAttributes[i].AttrType == bag.BagAttributes[j].AttrType) - { - ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE); - } - } - } - } - else - { - HashSet dedup = new HashSet(); - - for (int i = attrIdx; i >= 0; i--) - { - if (!dedup.Add(bag.BagAttributes[i].AttrType)) - { - ThrowWithHResult(SR.Cryptography_Der_Invalid_Encoding, CRYPT_E_BAD_DECODE); - } - } - } - } - attrIdx++; if (attrIdx < bag.BagAttributes.Length) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs index f29eaf7c0e0235..ac4a325e7dee89 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs @@ -258,14 +258,12 @@ public void LoadCertificate_WithTrailingData() Span needle = stackalloc byte[] { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D }; int offset = data.AsSpan().IndexOf(needle); -#if NET // The macOS PEM loader seems to be rejecting the trailing data. if (OperatingSystem.IsMacOS()) { Assert.ThrowsAny(() => LoadCertificateAtOffset(data, offset)); return; } -#endif using (X509Certificate2 cert = LoadCertificateAtOffset(data, offset)) { @@ -310,14 +308,12 @@ public void LoadWrappingCertificate_PEM_WithTrailingData() byte[] data = System.Text.Encoding.ASCII.GetBytes( ByteUtils.PemEncode("CERTIFICATE", source)); -#if NET // OpenSSL is being more strict here than other platforms. if (OperatingSystem.IsLinux()) { Assert.Throws(() => LoadCertificateNoFile(data)); return; } -#endif using (X509Certificate2 cert = LoadCertificateNoFile(data)) { diff --git a/src/libraries/Microsoft.Bcl.Cryptography/Microsoft.Bcl.Cryptography.sln b/src/libraries/Microsoft.Bcl.Cryptography/Microsoft.Bcl.Cryptography.sln index 0a13f73c96f079..7b4972e18f9fc4 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/Microsoft.Bcl.Cryptography.sln +++ b/src/libraries/Microsoft.Bcl.Cryptography/Microsoft.Bcl.Cryptography.sln @@ -1,8 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.10.34804.81 -MinimumVisualStudioVersion = 10.0.40219.1 +Microsoft Visual Studio Solution File, Format Version 12.00 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{1AEF7C7B-5A86-4A5E-9F8B-3933F7624751}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.Cryptography", "ref\Microsoft.Bcl.Cryptography.csproj", "{63655B2E-6A06-4E48-9F01-D0B910063165}" @@ -33,11 +29,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{98708A22-726 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{34897637-11A1-48A4-AF1F-E11463A61D0B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{568C437D-072B-48BA-BBB4-20F39E2ADA15}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{568C437D-072B-48BA-BBB4-20F39E2ADA15}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{AEDD1BBB-88D7-4CC1-9262-D94341DAD308}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{AEDD1BBB-88D7-4CC1-9262-D94341DAD308}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{3AFC9F72-C997-4066-86FD-EDA22F432C73}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{3AFC9F72-C997-4066-86FD-EDA22F432C73}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{98BFC458-097E-4C6C-BAE8-03199531D531}" EndProject @@ -97,25 +93,21 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {1AEF7C7B-5A86-4A5E-9F8B-3933F7624751} = {8C3BD4AD-1A56-4204-9826-F8B74251D19F} + {E66D17AB-BBAF-4F2B-AC9C-8E89BDCC6191} = {8C3BD4AD-1A56-4204-9826-F8B74251D19F} {63655B2E-6A06-4E48-9F01-D0B910063165} = {7E9F6DE1-771B-4E25-A603-EC43D0291C8B} {B0716D7E-B824-4866-A1ED-DF31BA2970B9} = {98708A22-7268-4EDB-AE37-70AA958A772A} - {E66D17AB-BBAF-4F2B-AC9C-8E89BDCC6191} = {8C3BD4AD-1A56-4204-9826-F8B74251D19F} {E9271403-BEF5-46E9-B68B-16EF69AA7149} = {34897637-11A1-48A4-AF1F-E11463A61D0B} {A4C2BDDC-1AFB-45A8-9E9B-4AD7396A4DF2} = {34897637-11A1-48A4-AF1F-E11463A61D0B} {777AF5DE-3EB3-4E62-A0D8-CA10D77B5343} = {568C437D-072B-48BA-BBB4-20F39E2ADA15} {94384409-052F-4697-B235-8990C851B6A4} = {568C437D-072B-48BA-BBB4-20F39E2ADA15} + {568C437D-072B-48BA-BBB4-20F39E2ADA15} = {98BFC458-097E-4C6C-BAE8-03199531D531} {A243E975-A17E-4DA8-91AC-7FCB52EAB921} = {AEDD1BBB-88D7-4CC1-9262-D94341DAD308} {7C971319-753D-4D49-B242-994260ABD9AF} = {AEDD1BBB-88D7-4CC1-9262-D94341DAD308} - {E4477F0C-A13A-4C1A-9A70-0A287E09E9EA} = {3AFC9F72-C997-4066-86FD-EDA22F432C73} - {568C437D-072B-48BA-BBB4-20F39E2ADA15} = {98BFC458-097E-4C6C-BAE8-03199531D531} {AEDD1BBB-88D7-4CC1-9262-D94341DAD308} = {98BFC458-097E-4C6C-BAE8-03199531D531} + {E4477F0C-A13A-4C1A-9A70-0A287E09E9EA} = {3AFC9F72-C997-4066-86FD-EDA22F432C73} {3AFC9F72-C997-4066-86FD-EDA22F432C73} = {98BFC458-097E-4C6C-BAE8-03199531D531} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EAA35B12-9858-4428-8510-F09B19933FB9} EndGlobalSection - GlobalSection(SharedMSBuildProjectFiles) = preSolution - ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{7c971319-753d-4d49-b242-994260abd9af}*SharedItemsImports = 5 - ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{94384409-052f-4697-b235-8990c851b6a4}*SharedItemsImports = 5 - EndGlobalSection EndGlobal diff --git a/src/libraries/System.Security.Cryptography/System.Security.Cryptography.sln b/src/libraries/System.Security.Cryptography/System.Security.Cryptography.sln index 8429ef873d1359..57220bcd94caf7 100644 --- a/src/libraries/System.Security.Cryptography/System.Security.Cryptography.sln +++ b/src/libraries/System.Security.Cryptography/System.Security.Cryptography.sln @@ -1,8 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.8.34212.112 -MinimumVisualStudioVersion = 10.0.40219.1 +Microsoft Visual Studio Solution File, Format Version 12.00 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StreamConformanceTests", "..\Common\tests\StreamConformanceTests\StreamConformanceTests.csproj", "{66FE8701-0F3C-4524-ACB6-404E13221733}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{0C78AEB2-80BD-4B1B-B5CD-F038522D065D}" @@ -59,11 +55,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A16B11D7-673 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{2B165D96-356D-409E-88BE-0CA0F407303E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{CC457FED-75A7-450C-91C9-F0F4AC6FF4A9}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{CC457FED-75A7-450C-91C9-F0F4AC6FF4A9}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7D4583A4-938F-4A2F-945D-9FD4FB4F0110}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{7D4583A4-938F-4A2F-945D-9FD4FB4F0110}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{1D7DE530-F078-4076-8D77-0A145DD165F6}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{1D7DE530-F078-4076-8D77-0A145DD165F6}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{5F3BF122-7954-4A83-8196-E8470B0B9C80}" EndProject @@ -176,37 +172,33 @@ Global GlobalSection(NestedProjects) = preSolution {66FE8701-0F3C-4524-ACB6-404E13221733} = {B3BB2DA0-53B8-4B49-999F-CD650EB61040} {0C78AEB2-80BD-4B1B-B5CD-F038522D065D} = {B3BB2DA0-53B8-4B49-999F-CD650EB61040} + {A242B3AB-EE45-4C95-9550-DB970BA23CD5} = {B3BB2DA0-53B8-4B49-999F-CD650EB61040} {BEE9B8AD-4BB4-43F9-B718-AC967E1DA092} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {EE43BF44-3757-4401-86E9-CCC789BA89E4} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {B1EA9B57-3B9F-47FD-BE76-01D3D672C88C} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {D5032AC3-A2A7-4B6A-93CB-2448DEB0B492} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} - {3B1486E4-DCDE-4629-AACC-D1A6FF8A1993} = {A16B11D7-6732-45E2-894B-D5AFA879B68A} {DEDBB595-C6B5-4084-8193-2F080A385DBB} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} - {0EA41D6A-87A2-42B7-8171-78E167570993} = {2B165D96-356D-409E-88BE-0CA0F407303E} - {9E7AD0F4-0D4D-4E42-8C6C-513C006E0590} = {2B165D96-356D-409E-88BE-0CA0F407303E} - {1B999BF7-657A-4342-A97E-B5CC5916AFB4} = {2B165D96-356D-409E-88BE-0CA0F407303E} {6A4AB150-B689-41A4-9DAD-B4C8A22709A6} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {DBEEA00C-86D6-4C87-A7EF-1171D7C25F59} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {24FAFCA3-8747-4E8A-BA2B-996A8F411C80} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} {258B22AA-ECA6-412E-9F29-6BB6063CD036} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} - {E72C02D0-CC28-4FE2-8A2F-D91ABD990065} = {A16B11D7-6732-45E2-894B-D5AFA879B68A} {E9BF1F4D-7D5C-4539-B426-2FCCB2585138} = {F591FB6D-6957-46DB-BF9B-C00772BC11E3} + {3B1486E4-DCDE-4629-AACC-D1A6FF8A1993} = {A16B11D7-6732-45E2-894B-D5AFA879B68A} + {E72C02D0-CC28-4FE2-8A2F-D91ABD990065} = {A16B11D7-6732-45E2-894B-D5AFA879B68A} {AF227727-11A6-4E5F-98DE-C362FC829633} = {A16B11D7-6732-45E2-894B-D5AFA879B68A} - {A242B3AB-EE45-4C95-9550-DB970BA23CD5} = {B3BB2DA0-53B8-4B49-999F-CD650EB61040} + {0EA41D6A-87A2-42B7-8171-78E167570993} = {2B165D96-356D-409E-88BE-0CA0F407303E} + {9E7AD0F4-0D4D-4E42-8C6C-513C006E0590} = {2B165D96-356D-409E-88BE-0CA0F407303E} + {1B999BF7-657A-4342-A97E-B5CC5916AFB4} = {2B165D96-356D-409E-88BE-0CA0F407303E} {7D70A668-5A55-439F-9FB8-8FF2A850CF91} = {CC457FED-75A7-450C-91C9-F0F4AC6FF4A9} {390C4E57-0F24-4E07-AFFB-AFBE67D58A52} = {CC457FED-75A7-450C-91C9-F0F4AC6FF4A9} + {CC457FED-75A7-450C-91C9-F0F4AC6FF4A9} = {5F3BF122-7954-4A83-8196-E8470B0B9C80} {93B28BF5-88A1-4763-913C-6F59F291676A} = {7D4583A4-938F-4A2F-945D-9FD4FB4F0110} {CF172501-7056-49E7-93E2-787BD4D95DF1} = {7D4583A4-938F-4A2F-945D-9FD4FB4F0110} - {0D113CC7-98F1-4323-B064-25A4EC6DA5A8} = {1D7DE530-F078-4076-8D77-0A145DD165F6} - {CC457FED-75A7-450C-91C9-F0F4AC6FF4A9} = {5F3BF122-7954-4A83-8196-E8470B0B9C80} {7D4583A4-938F-4A2F-945D-9FD4FB4F0110} = {5F3BF122-7954-4A83-8196-E8470B0B9C80} + {0D113CC7-98F1-4323-B064-25A4EC6DA5A8} = {1D7DE530-F078-4076-8D77-0A145DD165F6} {1D7DE530-F078-4076-8D77-0A145DD165F6} = {5F3BF122-7954-4A83-8196-E8470B0B9C80} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F5615383-F387-45EA-A05A-CB65D8A6FE88} EndGlobalSection - GlobalSection(SharedMSBuildProjectFiles) = preSolution - ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{390c4e57-0f24-4e07-affb-afbe67d58a52}*SharedItemsImports = 5 - ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{cf172501-7056-49e7-93e2-787bd4d95df1}*SharedItemsImports = 5 - EndGlobalSection EndGlobal diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs index 04019b838401b4..452d3ad02d534f 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxFormatTests.cs @@ -195,24 +195,6 @@ public void OneCert_NoKeys_EncryptedEmptyPassword_NoMac() } } - [ConditionalFact(typeof(Environment), nameof(Environment.HasShutdownStarted))] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "The PKCS#12 Exportable flag is not supported on iOS/MacCatalyst/tvOS")] - public void OneCert_NoKeys_EncryptedEmptyPassword_NullMac() - { - 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.SealWithMac(null, HashAlgorithmName.SHA1, 37); - byte[] pfxBytes = builder.Encode(); - - ReadPfx(pfxBytes, null, cert); - ReadPfx(pfxBytes, string.Empty, cert); - } - } - [Theory] [InlineData(false, false)] [InlineData(false, true)] @@ -1246,53 +1228,6 @@ public void TwoCerts_TwoKeys_ManySafeContentsValues_UnencryptedAuthSafes_NoMac(b } } - [ConditionalTheory(typeof(Environment), nameof(Environment.HasShutdownStarted))] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - [InlineData(3)] - public void TwoCerts_TwoKeys_DuplicateLocalKeyId(int itemIndex) - { - string pw = nameof(TwoCerts_TwoKeys_DuplicateLocalKeyId); - byte[] pfx; - - using (ImportedCollection ic = Cert.Import(TestData.MultiPrivateKeyPfx, null, s_exportableImportFlags)) - { - X509Certificate2Collection certs = ic.Collection; - X509Certificate2 first = certs[0]; - X509Certificate2 second = certs[1]; - - using (AsymmetricAlgorithm firstKey = first.GetRSAPrivateKey()) - using (AsymmetricAlgorithm secondKey = second.GetRSAPrivateKey()) - { - Pkcs12Builder builder = new Pkcs12Builder(); - Pkcs12SafeContents certContents = new Pkcs12SafeContents(); - Pkcs12SafeContents keyContents = new Pkcs12SafeContents(); - - Pkcs12CertBag certBagOne = certContents.AddCertificate(first); - Pkcs12CertBag certBagTwo = certContents.AddCertificate(second); - Pkcs12ShroudedKeyBag keyBagOne = keyContents.AddShroudedKey(firstKey, pw, s_windowsPbe); - Pkcs12ShroudedKeyBag keyBagTwo = keyContents.AddShroudedKey(secondKey, pw, s_windowsPbe); - - Pkcs12SafeBag[] bags = { certBagOne, certBagTwo, keyBagOne, keyBagTwo }; - bags[itemIndex].Attributes.Add(s_keyIdOne); - - certBagOne.Attributes.Add(s_keyIdOne); - keyBagOne.Attributes.Add(s_keyIdOne); - certBagTwo.Attributes.Add(s_keyIdTwo); - keyBagTwo.Attributes.Add(s_keyIdTwo); - - builder.AddSafeContentsUnencrypted(keyContents); - builder.AddSafeContentsEncrypted(certContents, pw, s_windowsPbe); - - builder.SealWithMac(pw, HashAlgorithmName.SHA1, 22); - pfx = builder.Encode(); - } - } - - ReadUnreadablePfx(pfx, pw); - } - private static void CheckKeyConsistency(X509Certificate2 cert) { byte[] data = { 2, 7, 4 }; From 2ee13b3a39aeecb0baf6e3a5254a3e5ba28892b3 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Fri, 21 Jun 2024 01:49:46 -0700 Subject: [PATCH 16/19] Fix netfx test build --- .../X509Certificates/X509CertificateLoaderTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs index ac4a325e7dee89..f29eaf7c0e0235 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderTests.cs @@ -258,12 +258,14 @@ public void LoadCertificate_WithTrailingData() Span needle = stackalloc byte[] { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D }; int offset = data.AsSpan().IndexOf(needle); +#if NET // The macOS PEM loader seems to be rejecting the trailing data. if (OperatingSystem.IsMacOS()) { Assert.ThrowsAny(() => LoadCertificateAtOffset(data, offset)); return; } +#endif using (X509Certificate2 cert = LoadCertificateAtOffset(data, offset)) { @@ -308,12 +310,14 @@ public void LoadWrappingCertificate_PEM_WithTrailingData() byte[] data = System.Text.Encoding.ASCII.GetBytes( ByteUtils.PemEncode("CERTIFICATE", source)); +#if NET // OpenSSL is being more strict here than other platforms. if (OperatingSystem.IsLinux()) { Assert.Throws(() => LoadCertificateNoFile(data)); return; } +#endif using (X509Certificate2 cert = LoadCertificateNoFile(data)) { From 4d2a0af9a8093f1b234a2572ee45b44e7c45f8cc Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Sat, 22 Jun 2024 12:34:11 -0700 Subject: [PATCH 17/19] Full swap is important in both Debug and Release For one, the code as written lost the attributes it should have preserved. For two, the swapped in attribute might be important. --- .../X509Certificates/X509CertificateLoader.Pkcs12.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs index 75b953d20c19c5..ccea2de41fc007 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs @@ -377,17 +377,11 @@ private static void FilterAttributes( { AttributeAsn attr = bag.BagAttributes[i]; bag.BagAttributes[i] = bag.BagAttributes[attrIdx]; + bag.BagAttributes[attrIdx] = attr; // After swapping, back up one position to check if the attribute // swapped into this position should also be preserved. i++; - -#if DEBUG - // In debug we'll do a full swap, just so the full set of input - // attributes can be seen under a debugger before the reducing - // Array.Resize - bag.BagAttributes[attrIdx] = attr; -#endif } } } From cbcf611b769202851d30250a2cb64f9fe3237239 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Mon, 24 Jun 2024 11:13:25 -0700 Subject: [PATCH 18/19] Prevent Android PKCS7 content sniffing in X509 load --- .../X509Certificates/AndroidCertificatePal.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs index 4e5e8f976cfa12..bb21853eaced25 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -8,6 +9,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.X509Certificates.Asn1; using System.Text; using Microsoft.Win32.SafeHandles; @@ -131,8 +133,25 @@ public static ICertificatePal FromFile(string fileName, SafePasswordHandle passw } // Handles both DER and PEM - internal static bool TryReadX509(ReadOnlySpan rawData, [NotNullWhen(true)] out ICertificatePal? handle) + internal static unsafe bool TryReadX509(ReadOnlySpan rawData, [NotNullWhen(true)] out ICertificatePal? handle) { + if (rawData.IsEmpty) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + // Prevent Android PKCS7 content sniffing + if (rawData[0] == 0x30) + { + fixed (byte* rawDataPtr = rawData) + { + using (PointerMemoryManager manager = new(rawDataPtr, rawData.Length)) + { + CertificateAsn.Decode(manager.Memory, AsnEncodingRules.DER); + } + } + } + handle = null; SafeX509Handle certHandle = Interop.AndroidCrypto.X509Decode( ref MemoryMarshal.GetReference(rawData), From dce56f46657d2e0dbc1a4cf77c780ef3ae11b980 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Mon, 24 Jun 2024 14:49:54 -0700 Subject: [PATCH 19/19] Use lax reading in Android content filter --- .../Cryptography/X509Certificates/AndroidCertificatePal.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs index bb21853eaced25..c456244223293c 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs @@ -147,7 +147,8 @@ internal static unsafe bool TryReadX509(ReadOnlySpan rawData, [NotNullWhen { using (PointerMemoryManager manager = new(rawDataPtr, rawData.Length)) { - CertificateAsn.Decode(manager.Memory, AsnEncodingRules.DER); + AsnValueReader reader = new AsnValueReader(rawData, AsnEncodingRules.DER); + CertificateAsn.Decode(ref reader, manager.Memory, out _); } } }