From 6228623bf1e958374baa400ee4997216f1fc1ed5 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Fri, 30 Apr 2021 17:52:16 +0200 Subject: [PATCH 01/27] Implement iOS PAL for S.S.C.X509Certificates --- .../Interop.Keychain.iOS.cs | 91 ++++ ....Keychain.cs => Interop.Keychain.macOS.cs} | 61 +++ .../Interop.X509.cs | 414 +-------------- .../Interop.X509.iOS.cs | 242 +++++++++ .../Interop.X509.macOS.cs | 425 +++++++++++++++ .../Interop.X509Store.cs | 76 --- .../System/Security/Cryptography/PemLabels.cs | 1 + .../CMakeLists.txt | 8 + .../extra_libs.cmake | 3 +- .../pal_keychain_ios.c | 186 +++++++ .../pal_keychain_ios.h | 51 ++ .../pal_x509_ios.c | 161 ++++++ .../pal_x509_ios.h | 57 ++ .../Pal.iOS/AppleCertificatePal.Pem.cs | 81 +++ .../Pal.iOS/AppleCertificatePal.Pkcs12.cs | 114 ++++ .../Pal.iOS/AppleCertificatePal.cs | 494 ++++++++++++++++++ .../Cryptography/Pal.iOS/ApplePkcs12Reader.cs | 76 +++ .../Cryptography/Pal.iOS/CertificatePal.cs | 45 ++ .../Pal.iOS/StorePal.AppleKeychainStore.cs | 77 +++ .../Pal.iOS/StorePal.ExportPal.cs | 59 +++ .../Pal.iOS/StorePal.LoaderPal.cs | 115 ++++ .../Pal.iOS/StorePal.TrustedStore.cs | 43 ++ .../Internal/Cryptography/Pal.iOS/StorePal.cs | 160 ++++++ .../Internal/Cryptography/Pal.iOS/X509Pal.cs | 293 +++++++++++ .../src/Resources/Strings.resx | 6 + ...urity.Cryptography.X509Certificates.csproj | 53 +- .../PrivateKeyAssociationTests.cs | 1 + .../tests/ChainTests.cs | 11 +- .../tests/CollectionImportTests.cs | 14 + .../tests/CollectionTests.cs | 2 + .../tests/CtorTests.cs | 2 +- .../tests/DynamicChainTests.cs | 12 +- .../tests/FindTests.cs | 1 + .../tests/PfxTests.cs | 6 +- .../tests/PublicKeyTests.cs | 10 +- .../tests/RSAOther.cs | 6 + .../tests/Resources/Strings.resx | 9 + .../tests/RevocationTests/AiaTests.cs | 1 + .../RevocationTests/DynamicRevocationTests.cs | 42 +- ...Cryptography.X509Certificates.Tests.csproj | 13 +- .../tests/TestFiles.cs | 2 +- .../tests/X509Certificate2PemTests.cs | 2 + .../tests/X509StoreMutableTests.iOS.cs | 39 ++ .../tests/X509StoreTests.cs | 2 + 44 files changed, 3017 insertions(+), 550 deletions(-) create mode 100644 src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.iOS.cs rename src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/{Interop.Keychain.cs => Interop.Keychain.macOS.cs} (85%) create mode 100644 src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs create mode 100644 src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.macOS.cs delete mode 100644 src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Store.cs create mode 100644 src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c create mode 100644 src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.h create mode 100644 src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c create mode 100644 src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12Reader.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/CertificatePal.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.AppleKeychainStore.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.ExportPal.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.LoaderPal.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.TrustedStore.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.iOS.cs diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.iOS.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.iOS.cs new file mode 100644 index 0000000000000..32346eaacb388 --- /dev/null +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.iOS.cs @@ -0,0 +1,91 @@ +// 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.IO; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; + +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class AppleCrypto + { + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_SecKeychainEnumerateCerts( + out SafeCFArrayHandle matches); + + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_SecKeychainEnumerateIdentities( + out SafeCFArrayHandle matches); + + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509StoreAddCertificate( + SafeHandle certOrIdentity); + + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509StoreRemoveCertificate( + SafeHandle certOrIdentity, + bool isReadOnlyMode); + + internal static SafeCFArrayHandle KeychainEnumerateCerts() + { + SafeCFArrayHandle matches; + int osStatus = AppleCryptoNative_SecKeychainEnumerateCerts(out matches); + + if (osStatus == 0) + { + return matches; + } + + matches.Dispose(); + throw CreateExceptionForOSStatus(osStatus); + } + + internal static SafeCFArrayHandle KeychainEnumerateIdentities() + { + SafeCFArrayHandle matches; + int osStatus = AppleCryptoNative_SecKeychainEnumerateIdentities(out matches); + + if (osStatus == 0) + { + return matches; + } + + matches.Dispose(); + throw CreateExceptionForOSStatus(osStatus); + } + + internal static void X509StoreAddCertificate(SafeHandle certOrIdentity) + { + int osStatus = AppleCryptoNative_X509StoreAddCertificate(certOrIdentity); + + if (osStatus != 0) + { + throw CreateExceptionForOSStatus(osStatus); + } + } + + internal static void X509StoreRemoveCertificate(SafeHandle certOrIdentity, bool isReadOnlyMode) + { + const int errSecItemNotFound = -25300; + + int osStatus = AppleCryptoNative_X509StoreRemoveCertificate(certOrIdentity, isReadOnlyMode); + + if (osStatus == 0 && isReadOnlyMode) + { + // The certificate exists in the store otherwise we would get errSecItemNotFound error + throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); + } + + if (osStatus != 0 && osStatus != errSecItemNotFound) + { + throw CreateExceptionForOSStatus(osStatus); + } + } + } +} diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs similarity index 85% rename from src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.cs rename to src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs index 5ba6dda27a2bf..862760aa2c8de 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.macOS.cs @@ -66,6 +66,19 @@ private static extern int AppleCryptoNative_SecKeychainEnumerateIdentities( out SafeCFArrayHandle matches, out int pOSStatus); + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509StoreAddCertificate( + SafeKeychainItemHandle cert, + SafeKeychainHandle keychain, + out int pOSStatus); + + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509StoreRemoveCertificate( + SafeKeychainItemHandle cert, + SafeKeychainHandle keychain, + bool isReadOnlyMode, + out int pOSStatus); + private static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeHandle item) { bool addedRef = false; @@ -290,6 +303,54 @@ internal static void SecKeychainDelete(IntPtr handle, bool throwOnError=true) throw CreateExceptionForOSStatus(osStatus); } } + + internal static void X509StoreAddCertificate(SafeKeychainItemHandle certOrIdentity, SafeKeychainHandle keychain) + { + int osStatus; + int ret = AppleCryptoNative_X509StoreAddCertificate(certOrIdentity, keychain, out osStatus); + + if (ret == 0) + { + throw CreateExceptionForOSStatus(osStatus); + } + + if (ret != 1) + { + Debug.Fail($"Unexpected result from AppleCryptoNative_X509StoreAddCertificate: {ret}"); + throw new CryptographicException(); + } + } + + internal static void X509StoreRemoveCertificate(SafeKeychainItemHandle certHandle, SafeKeychainHandle keychain, bool isReadOnlyMode) + { + int osStatus; + int ret = AppleCryptoNative_X509StoreRemoveCertificate(certHandle, keychain, isReadOnlyMode, out osStatus); + + if (ret == 0) + { + throw CreateExceptionForOSStatus(osStatus); + } + + const int SuccessOrNoMatch = 1; + const int UserTrustExists = 2; + const int AdminTrustExists = 3; + const int ReadOnlyDelete = 4; + + switch (ret) + { + case SuccessOrNoMatch: + break; + case UserTrustExists: + throw new CryptographicException(SR.Cryptography_X509Store_WouldModifyUserTrust); + case AdminTrustExists: + throw new CryptographicException(SR.Cryptography_X509Store_WouldModifyAdminTrust); + case ReadOnlyDelete: + throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); + default: + Debug.Fail($"Unexpected result from AppleCryptoNative_X509StoreRemoveCertificate: {ret}"); + throw new CryptographicException(); + } + } } } diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs index daa1d95f8ccdb..fa1357c8b9232 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs @@ -14,54 +14,6 @@ internal static partial class Interop { internal static partial class AppleCrypto { - private static readonly SafeCreateHandle s_emptyExportString = - CoreFoundation.CFStringCreateWithCString(""); - - private static int AppleCryptoNative_X509ImportCertificate( - ReadOnlySpan keyBlob, - X509ContentType contentType, - SafeCreateHandle cfPfxPassphrase, - SafeKeychainHandle tmpKeychain, - int exportable, - out SafeSecCertificateHandle pCertOut, - out SafeSecIdentityHandle pPrivateKeyOut, - out int pOSStatus) - { - return AppleCryptoNative_X509ImportCertificate( - ref MemoryMarshal.GetReference(keyBlob), - keyBlob.Length, - contentType, - cfPfxPassphrase, - tmpKeychain, - exportable, - out pCertOut, - out pPrivateKeyOut, - out pOSStatus); - } - - [DllImport(Libraries.AppleCryptoNative)] - private static extern int AppleCryptoNative_X509ImportCertificate( - ref byte pbKeyBlob, - int cbKeyBlob, - X509ContentType contentType, - SafeCreateHandle cfPfxPassphrase, - SafeKeychainHandle tmpKeychain, - int exportable, - out SafeSecCertificateHandle pCertOut, - out SafeSecIdentityHandle pPrivateKeyOut, - out int pOSStatus); - - [DllImport(Libraries.AppleCryptoNative)] - private static extern int AppleCryptoNative_X509ImportCollection( - ref byte pbKeyBlob, - int cbKeyBlob, - X509ContentType contentType, - SafeCreateHandle cfPfxPassphrase, - SafeKeychainHandle tmpKeychain, - int exportable, - out SafeCFArrayHandle pCollectionOut, - out int pOSStatus); - [DllImport(Libraries.AppleCryptoNative)] private static extern int AppleCryptoNative_X509GetRawData( SafeSecCertificateHandle cert, @@ -93,30 +45,6 @@ private static extern int AppleCryptoNative_X509DemuxAndRetainHandle( out SafeSecCertificateHandle certHandle, out SafeSecIdentityHandle identityHandle); - [DllImport(Libraries.AppleCryptoNative)] - private static extern int AppleCryptoNative_X509ExportData( - SafeCreateHandle data, - X509ContentType type, - SafeCreateHandle cfExportPassphrase, - out SafeCFDataHandle pExportOut, - out int pOSStatus); - - [DllImport(Libraries.AppleCryptoNative)] - private static extern int AppleCryptoNative_X509CopyWithPrivateKey( - SafeSecCertificateHandle certHandle, - SafeSecKeyRefHandle privateKeyHandle, - SafeKeychainHandle targetKeychain, - out SafeSecIdentityHandle pIdentityHandleOut, - out int pOSStatus); - - [DllImport(Libraries.AppleCryptoNative)] - private static extern int AppleCryptoNative_X509MoveToKeychain( - SafeSecCertificateHandle certHandle, - SafeKeychainHandle targetKeychain, - SafeSecKeyRefHandle privateKeyHandle, - out SafeSecIdentityHandle pIdentityHandleOut, - out int pOSStatus); - internal static byte[] X509GetRawData(SafeSecCertificateHandle cert) { int osStatus; @@ -141,175 +69,12 @@ internal static byte[] X509GetRawData(SafeSecCertificateHandle cert) throw new CryptographicException(); } - internal static SafeSecCertificateHandle X509ImportCertificate( - ReadOnlySpan bytes, - X509ContentType contentType, - SafePasswordHandle importPassword, - SafeKeychainHandle keychain, - bool exportable, - out SafeSecIdentityHandle identityHandle) - { - SafeCreateHandle? cfPassphrase = null; - bool releasePassword = false; - - try - { - if (!importPassword.IsInvalid) - { - importPassword.DangerousAddRef(ref releasePassword); - cfPassphrase = CoreFoundation.CFStringCreateFromSpan(importPassword.DangerousGetSpan()); - } - - return X509ImportCertificate( - bytes, - contentType, - cfPassphrase, - keychain, - exportable, - out identityHandle); - } - finally - { - if (releasePassword) - { - importPassword.DangerousRelease(); - } - - cfPassphrase?.Dispose(); - } - } - - private static SafeSecCertificateHandle X509ImportCertificate( - ReadOnlySpan bytes, - X509ContentType contentType, - SafeCreateHandle? importPassword, - SafeKeychainHandle keychain, - bool exportable, - out SafeSecIdentityHandle identityHandle) - { - SafeSecCertificateHandle certHandle; - int osStatus; - - SafeCreateHandle cfPassphrase = importPassword ?? s_nullExportString; - - int ret = AppleCryptoNative_X509ImportCertificate( - bytes, - contentType, - cfPassphrase, - keychain, - exportable ? 1 : 0, - out certHandle, - out identityHandle, - out osStatus); - - SafeTemporaryKeychainHandle.TrackItem(certHandle); - SafeTemporaryKeychainHandle.TrackItem(identityHandle); - - if (ret == 1) - { - return certHandle; - } - - certHandle.Dispose(); - identityHandle.Dispose(); - - const int SeeOSStatus = 0; - const int ImportReturnedEmpty = -2; - const int ImportReturnedNull = -3; - - switch (ret) - { - case SeeOSStatus: - throw CreateExceptionForOSStatus(osStatus); - case ImportReturnedNull: - case ImportReturnedEmpty: - throw new CryptographicException(); - default: - Debug.Fail($"Unexpected return value {ret}"); - throw new CryptographicException(); - } - } - - internal static SafeCFArrayHandle X509ImportCollection( - ReadOnlySpan bytes, - X509ContentType contentType, - SafePasswordHandle importPassword, - SafeKeychainHandle keychain, - bool exportable) - { - SafeCreateHandle cfPassphrase = s_nullExportString; - bool releasePassword = false; - - int ret; - SafeCFArrayHandle collectionHandle; - int osStatus; - - try - { - if (!importPassword.IsInvalid) - { - importPassword.DangerousAddRef(ref releasePassword); - IntPtr passwordHandle = importPassword.DangerousGetHandle(); - - if (passwordHandle != IntPtr.Zero) - { - cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle); - } - } - - ret = AppleCryptoNative_X509ImportCollection( - ref MemoryMarshal.GetReference(bytes), - bytes.Length, - contentType, - cfPassphrase, - keychain, - exportable ? 1 : 0, - out collectionHandle, - out osStatus); - - if (ret == 1) - { - return collectionHandle; - } - } - finally - { - if (releasePassword) - { - importPassword.DangerousRelease(); - } - - if (cfPassphrase != s_nullExportString) - { - cfPassphrase.Dispose(); - } - } - - collectionHandle.Dispose(); - - const int SeeOSStatus = 0; - const int ImportReturnedEmpty = -2; - const int ImportReturnedNull = -3; - - switch (ret) - { - case SeeOSStatus: - throw CreateExceptionForOSStatus(osStatus); - case ImportReturnedNull: - case ImportReturnedEmpty: - throw new CryptographicException(); - default: - Debug.Fail($"Unexpected return value {ret}"); - throw new CryptographicException(); - } - } - internal static SafeSecCertificateHandle X509GetCertFromIdentity(SafeSecIdentityHandle identity) { SafeSecCertificateHandle cert; int osStatus = AppleCryptoNative_X509CopyCertFromIdentity(identity, out cert); - SafeTemporaryKeychainHandle.TrackItem(cert); + //SafeTemporaryKeychainHandle.TrackItem(cert); if (osStatus != 0) { @@ -379,8 +144,8 @@ internal static bool X509DemuxAndRetainHandle( { int result = AppleCryptoNative_X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle); - SafeTemporaryKeychainHandle.TrackItem(certHandle); - SafeTemporaryKeychainHandle.TrackItem(identityHandle); + //SafeTemporaryKeychainHandle.TrackItem(certHandle); + //SafeTemporaryKeychainHandle.TrackItem(identityHandle); switch (result) { @@ -393,178 +158,5 @@ internal static bool X509DemuxAndRetainHandle( throw new CryptographicException(); } } - - internal static SafeSecIdentityHandle X509CopyWithPrivateKey( - SafeSecCertificateHandle certHandle, - SafeSecKeyRefHandle privateKeyHandle, - SafeKeychainHandle targetKeychain) - { - SafeSecIdentityHandle identityHandle; - int osStatus; - - int result = AppleCryptoNative_X509CopyWithPrivateKey( - certHandle, - privateKeyHandle, - targetKeychain, - out identityHandle, - out osStatus); - - if (result == 1) - { - Debug.Assert(!identityHandle.IsInvalid); - return identityHandle; - } - - identityHandle.Dispose(); - - if (result == 0) - { - throw CreateExceptionForOSStatus(osStatus); - } - - Debug.Fail($"AppleCryptoNative_X509CopyWithPrivateKey returned {result}"); - throw new CryptographicException(); - } - - internal static SafeSecIdentityHandle? X509MoveToKeychain( - SafeSecCertificateHandle cert, - SafeKeychainHandle targetKeychain, - SafeSecKeyRefHandle? privateKey) - { - SafeSecIdentityHandle identityHandle; - int osStatus; - - int result = AppleCryptoNative_X509MoveToKeychain( - cert, - targetKeychain, - privateKey ?? SafeSecKeyRefHandle.InvalidHandle, - out identityHandle, - out osStatus); - - if (result == 0) - { - identityHandle.Dispose(); - throw CreateExceptionForOSStatus(osStatus); - } - - if (result != 1) - { - Debug.Fail($"AppleCryptoNative_X509MoveToKeychain returned {result}"); - throw new CryptographicException(); - } - - if (privateKey?.IsInvalid == false) - { - // If a PFX has a mismatched association between a private key and the - // certificate public key then MoveToKeychain will write the NULL SecIdentityRef - // (after cleaning up the temporary key). - // - // When that happens, just treat the import as public-only. - if (!identityHandle.IsInvalid) - { - return identityHandle; - } - } - - // If the cert in the PFX had no key, but it was imported with PersistKeySet (imports into - // the default keychain) and a matching private key was already there, then an - // identityHandle could be found. But that's not desirable, since neither Windows or Linux would - // do that matching. - // - // So dispose the handle, no matter what. - identityHandle.Dispose(); - return null; - } - - private static byte[] X509Export(X509ContentType contentType, SafeCreateHandle cfPassphrase, IntPtr[] certHandles) - { - Debug.Assert(contentType == X509ContentType.Pkcs7 || contentType == X509ContentType.Pkcs12); - - using (SafeCreateHandle handlesArray = CoreFoundation.CFArrayCreate(certHandles, (UIntPtr)certHandles.Length)) - { - SafeCFDataHandle exportData; - int osStatus; - - int result = AppleCryptoNative_X509ExportData( - handlesArray, - contentType, - cfPassphrase, - out exportData, - out osStatus); - - using (exportData) - { - if (result != 1) - { - if (result == 0) - { - throw CreateExceptionForOSStatus(osStatus); - } - - Debug.Fail($"Unexpected result from AppleCryptoNative_X509ExportData: {result}"); - throw new CryptographicException(); - } - - Debug.Assert(!exportData.IsInvalid, "Successful export yielded no data"); - return CoreFoundation.CFGetData(exportData); - } - } - } - - internal static byte[] X509ExportPkcs7(IntPtr[] certHandles) - { - return X509Export(X509ContentType.Pkcs7, s_nullExportString, certHandles); - } - - internal static byte[] X509ExportPfx(IntPtr[] certHandles, SafePasswordHandle exportPassword) - { - SafeCreateHandle cfPassphrase = s_emptyExportString; - bool releasePassword = false; - - try - { - if (!exportPassword.IsInvalid) - { - exportPassword.DangerousAddRef(ref releasePassword); - IntPtr passwordHandle = exportPassword.DangerousGetHandle(); - - if (passwordHandle != IntPtr.Zero) - { - cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle); - } - } - - return X509Export(X509ContentType.Pkcs12, cfPassphrase, certHandles); - } - finally - { - if (releasePassword) - { - exportPassword.DangerousRelease(); - } - - if (cfPassphrase != s_emptyExportString) - { - cfPassphrase.Dispose(); - } - } - } - } -} - -namespace System.Security.Cryptography.X509Certificates -{ - internal sealed class SafeSecIdentityHandle : SafeKeychainItemHandle - { - public SafeSecIdentityHandle() - { - } - } - - internal sealed class SafeSecCertificateHandle : SafeKeychainItemHandle - { - public SafeSecCertificateHandle() - { - } } } diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs new file mode 100644 index 0000000000000..54a8d35c7f824 --- /dev/null +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs @@ -0,0 +1,242 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; + +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class AppleCrypto + { + private static readonly SafeCreateHandle s_emptyExportString = + CoreFoundation.CFStringCreateWithCString(""); + + private static int AppleCryptoNative_X509ImportCertificate( + ReadOnlySpan keyBlob, + X509ContentType contentType, + SafeCreateHandle cfPfxPassphrase, + out SafeSecCertificateHandle pCertOut, + out SafeSecIdentityHandle pPrivateKeyOut, + out int pOSStatus) + { + return AppleCryptoNative_X509ImportCertificate( + ref MemoryMarshal.GetReference(keyBlob), + keyBlob.Length, + contentType, + cfPfxPassphrase, + out pCertOut, + out pPrivateKeyOut, + out pOSStatus); + } + + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509ImportCertificate( + ref byte pbKeyBlob, + int cbKeyBlob, + X509ContentType contentType, + SafeCreateHandle cfPfxPassphrase, + out SafeSecCertificateHandle pCertOut, + out SafeSecIdentityHandle pPrivateKeyOut, + out int pOSStatus); + + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509ImportCollection( + ref byte pbKeyBlob, + int cbKeyBlob, + X509ContentType contentType, + SafeCreateHandle cfPfxPassphrase, + out SafeCFArrayHandle pCollectionOut, + out int pOSStatus); + + internal static SafeSecCertificateHandle X509ImportCertificate( + ReadOnlySpan bytes, + X509ContentType contentType, + SafePasswordHandle importPassword, + out SafeSecIdentityHandle identityHandle) + { + SafeCreateHandle? cfPassphrase = null; + bool releasePassword = false; + + try + { + if (!importPassword.IsInvalid) + { + importPassword.DangerousAddRef(ref releasePassword); + cfPassphrase = CoreFoundation.CFStringCreateFromSpan(importPassword.DangerousGetSpan()); + } + + return X509ImportCertificate( + bytes, + contentType, + cfPassphrase, + out identityHandle); + } + finally + { + if (releasePassword) + { + importPassword.DangerousRelease(); + } + + cfPassphrase?.Dispose(); + } + } + + private static SafeSecCertificateHandle X509ImportCertificate( + ReadOnlySpan bytes, + X509ContentType contentType, + SafeCreateHandle? importPassword, + out SafeSecIdentityHandle identityHandle) + { + SafeSecCertificateHandle certHandle; + int osStatus; + + SafeCreateHandle cfPassphrase = importPassword ?? s_emptyExportString; + + int ret = AppleCryptoNative_X509ImportCertificate( + bytes, + contentType, + cfPassphrase, + out certHandle, + out identityHandle, + out osStatus); + + if (ret == 1) + { + return certHandle; + } + + certHandle.Dispose(); + identityHandle.Dispose(); + + const int SeeOSStatus = 0; + const int ImportReturnedEmpty = -2; + const int ImportReturnedNull = -3; + + switch (ret) + { + case SeeOSStatus: + throw CreateExceptionForOSStatus(osStatus); + case ImportReturnedNull: + case ImportReturnedEmpty: + throw new CryptographicException(); + default: + Debug.Fail($"Unexpected return value {ret}"); + throw new CryptographicException(); + } + } + + internal static SafeCFArrayHandle X509ImportCollection( + ReadOnlySpan bytes, + X509ContentType contentType, + SafePasswordHandle importPassword) + { + SafeCreateHandle cfPassphrase = s_emptyExportString; + bool releasePassword = false; + + int ret; + SafeCFArrayHandle collectionHandle; + int osStatus; + + try + { + if (!importPassword.IsInvalid) + { + importPassword.DangerousAddRef(ref releasePassword); + IntPtr passwordHandle = importPassword.DangerousGetHandle(); + + if (passwordHandle != IntPtr.Zero) + { + cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle); + } + } + + ret = AppleCryptoNative_X509ImportCollection( + ref MemoryMarshal.GetReference(bytes), + bytes.Length, + contentType, + cfPassphrase, + out collectionHandle, + out osStatus); + + if (ret == 1) + { + return collectionHandle; + } + } + finally + { + if (releasePassword) + { + importPassword.DangerousRelease(); + } + + if (cfPassphrase != s_emptyExportString) + { + cfPassphrase.Dispose(); + } + } + + collectionHandle.Dispose(); + + const int SeeOSStatus = 0; + const int ImportReturnedEmpty = -2; + const int ImportReturnedNull = -3; + + switch (ret) + { + case SeeOSStatus: + throw CreateExceptionForOSStatus(osStatus); + case ImportReturnedNull: + case ImportReturnedEmpty: + throw new CryptographicException(); + default: + Debug.Fail($"Unexpected return value {ret}"); + throw new CryptographicException(); + } + } + } +} + +namespace System.Security.Cryptography.X509Certificates +{ + internal sealed class SafeSecIdentityHandle : SafeHandle + { + public SafeSecIdentityHandle() + : base(IntPtr.Zero, ownsHandle: true) + { + } + + protected override bool ReleaseHandle() + { + Interop.CoreFoundation.CFRelease(handle); + SetHandle(IntPtr.Zero); + return true; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + } + + internal sealed class SafeSecCertificateHandle : SafeHandle + { + public SafeSecCertificateHandle() + : base(IntPtr.Zero, ownsHandle: true) + { + } + + protected override bool ReleaseHandle() + { + Interop.CoreFoundation.CFRelease(handle); + SetHandle(IntPtr.Zero); + return true; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + } +} diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.macOS.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.macOS.cs new file mode 100644 index 0000000000000..848d67a30c72f --- /dev/null +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.macOS.cs @@ -0,0 +1,425 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; + +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class AppleCrypto + { + private static readonly SafeCreateHandle s_emptyExportString = + CoreFoundation.CFStringCreateWithCString(""); + + private static int AppleCryptoNative_X509ImportCertificate( + ReadOnlySpan keyBlob, + X509ContentType contentType, + SafeCreateHandle cfPfxPassphrase, + SafeKeychainHandle tmpKeychain, + int exportable, + out SafeSecCertificateHandle pCertOut, + out SafeSecIdentityHandle pPrivateKeyOut, + out int pOSStatus) + { + return AppleCryptoNative_X509ImportCertificate( + ref MemoryMarshal.GetReference(keyBlob), + keyBlob.Length, + contentType, + cfPfxPassphrase, + tmpKeychain, + exportable, + out pCertOut, + out pPrivateKeyOut, + out pOSStatus); + } + + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509ImportCertificate( + ref byte pbKeyBlob, + int cbKeyBlob, + X509ContentType contentType, + SafeCreateHandle cfPfxPassphrase, + SafeKeychainHandle tmpKeychain, + int exportable, + out SafeSecCertificateHandle pCertOut, + out SafeSecIdentityHandle pPrivateKeyOut, + out int pOSStatus); + + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509ImportCollection( + ref byte pbKeyBlob, + int cbKeyBlob, + X509ContentType contentType, + SafeCreateHandle cfPfxPassphrase, + SafeKeychainHandle tmpKeychain, + int exportable, + out SafeCFArrayHandle pCollectionOut, + out int pOSStatus); + + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509ExportData( + SafeCreateHandle data, + X509ContentType type, + SafeCreateHandle cfExportPassphrase, + out SafeCFDataHandle pExportOut, + out int pOSStatus); + + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509CopyWithPrivateKey( + SafeSecCertificateHandle certHandle, + SafeSecKeyRefHandle privateKeyHandle, + SafeKeychainHandle targetKeychain, + out SafeSecIdentityHandle pIdentityHandleOut, + out int pOSStatus); + + [DllImport(Libraries.AppleCryptoNative)] + private static extern int AppleCryptoNative_X509MoveToKeychain( + SafeSecCertificateHandle certHandle, + SafeKeychainHandle targetKeychain, + SafeSecKeyRefHandle privateKeyHandle, + out SafeSecIdentityHandle pIdentityHandleOut, + out int pOSStatus); + + internal static SafeSecCertificateHandle X509ImportCertificate( + ReadOnlySpan bytes, + X509ContentType contentType, + SafePasswordHandle importPassword, + SafeKeychainHandle keychain, + bool exportable, + out SafeSecIdentityHandle identityHandle) + { + SafeCreateHandle? cfPassphrase = null; + bool releasePassword = false; + + try + { + if (!importPassword.IsInvalid) + { + importPassword.DangerousAddRef(ref releasePassword); + cfPassphrase = CoreFoundation.CFStringCreateFromSpan(importPassword.DangerousGetSpan()); + } + + return X509ImportCertificate( + bytes, + contentType, + cfPassphrase, + keychain, + exportable, + out identityHandle); + } + finally + { + if (releasePassword) + { + importPassword.DangerousRelease(); + } + + cfPassphrase?.Dispose(); + } + } + + private static SafeSecCertificateHandle X509ImportCertificate( + ReadOnlySpan bytes, + X509ContentType contentType, + SafeCreateHandle? importPassword, + SafeKeychainHandle keychain, + bool exportable, + out SafeSecIdentityHandle identityHandle) + { + SafeSecCertificateHandle certHandle; + int osStatus; + + SafeCreateHandle cfPassphrase = importPassword ?? s_nullExportString; + + int ret = AppleCryptoNative_X509ImportCertificate( + bytes, + contentType, + cfPassphrase, + keychain, + exportable ? 1 : 0, + out certHandle, + out identityHandle, + out osStatus); + + SafeTemporaryKeychainHandle.TrackItem(certHandle); + SafeTemporaryKeychainHandle.TrackItem(identityHandle); + + if (ret == 1) + { + return certHandle; + } + + certHandle.Dispose(); + identityHandle.Dispose(); + + const int SeeOSStatus = 0; + const int ImportReturnedEmpty = -2; + const int ImportReturnedNull = -3; + + switch (ret) + { + case SeeOSStatus: + throw CreateExceptionForOSStatus(osStatus); + case ImportReturnedNull: + case ImportReturnedEmpty: + throw new CryptographicException(); + default: + Debug.Fail($"Unexpected return value {ret}"); + throw new CryptographicException(); + } + } + + internal static SafeCFArrayHandle X509ImportCollection( + ReadOnlySpan bytes, + X509ContentType contentType, + SafePasswordHandle importPassword, + SafeKeychainHandle keychain, + bool exportable) + { + SafeCreateHandle cfPassphrase = s_nullExportString; + bool releasePassword = false; + + int ret; + SafeCFArrayHandle collectionHandle; + int osStatus; + + try + { + if (!importPassword.IsInvalid) + { + importPassword.DangerousAddRef(ref releasePassword); + IntPtr passwordHandle = importPassword.DangerousGetHandle(); + + if (passwordHandle != IntPtr.Zero) + { + cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle); + } + } + + ret = AppleCryptoNative_X509ImportCollection( + ref MemoryMarshal.GetReference(bytes), + bytes.Length, + contentType, + cfPassphrase, + keychain, + exportable ? 1 : 0, + out collectionHandle, + out osStatus); + + if (ret == 1) + { + return collectionHandle; + } + } + finally + { + if (releasePassword) + { + importPassword.DangerousRelease(); + } + + if (cfPassphrase != s_nullExportString) + { + cfPassphrase.Dispose(); + } + } + + collectionHandle.Dispose(); + + const int SeeOSStatus = 0; + const int ImportReturnedEmpty = -2; + const int ImportReturnedNull = -3; + + switch (ret) + { + case SeeOSStatus: + throw CreateExceptionForOSStatus(osStatus); + case ImportReturnedNull: + case ImportReturnedEmpty: + throw new CryptographicException(); + default: + Debug.Fail($"Unexpected return value {ret}"); + throw new CryptographicException(); + } + } + + internal static SafeSecIdentityHandle X509CopyWithPrivateKey( + SafeSecCertificateHandle certHandle, + SafeSecKeyRefHandle privateKeyHandle, + SafeKeychainHandle targetKeychain) + { + SafeSecIdentityHandle identityHandle; + int osStatus; + + int result = AppleCryptoNative_X509CopyWithPrivateKey( + certHandle, + privateKeyHandle, + targetKeychain, + out identityHandle, + out osStatus); + + if (result == 1) + { + Debug.Assert(!identityHandle.IsInvalid); + return identityHandle; + } + + identityHandle.Dispose(); + + if (result == 0) + { + throw CreateExceptionForOSStatus(osStatus); + } + + Debug.Fail($"AppleCryptoNative_X509CopyWithPrivateKey returned {result}"); + throw new CryptographicException(); + } + + internal static SafeSecIdentityHandle? X509MoveToKeychain( + SafeSecCertificateHandle cert, + SafeKeychainHandle targetKeychain, + SafeSecKeyRefHandle? privateKey) + { + SafeSecIdentityHandle identityHandle; + int osStatus; + + int result = AppleCryptoNative_X509MoveToKeychain( + cert, + targetKeychain, + privateKey ?? SafeSecKeyRefHandle.InvalidHandle, + out identityHandle, + out osStatus); + + if (result == 0) + { + identityHandle.Dispose(); + throw CreateExceptionForOSStatus(osStatus); + } + + if (result != 1) + { + Debug.Fail($"AppleCryptoNative_X509MoveToKeychain returned {result}"); + throw new CryptographicException(); + } + + if (privateKey?.IsInvalid == false) + { + // If a PFX has a mismatched association between a private key and the + // certificate public key then MoveToKeychain will write the NULL SecIdentityRef + // (after cleaning up the temporary key). + // + // When that happens, just treat the import as public-only. + if (!identityHandle.IsInvalid) + { + return identityHandle; + } + } + + // If the cert in the PFX had no key, but it was imported with PersistKeySet (imports into + // the default keychain) and a matching private key was already there, then an + // identityHandle could be found. But that's not desirable, since neither Windows or Linux would + // do that matching. + // + // So dispose the handle, no matter what. + identityHandle.Dispose(); + return null; + } + + private static byte[] X509Export(X509ContentType contentType, SafeCreateHandle cfPassphrase, IntPtr[] certHandles) + { + Debug.Assert(contentType == X509ContentType.Pkcs7 || contentType == X509ContentType.Pkcs12); + + using (SafeCreateHandle handlesArray = CoreFoundation.CFArrayCreate(certHandles, (UIntPtr)certHandles.Length)) + { + SafeCFDataHandle exportData; + int osStatus; + + int result = AppleCryptoNative_X509ExportData( + handlesArray, + contentType, + cfPassphrase, + out exportData, + out osStatus); + + using (exportData) + { + if (result != 1) + { + if (result == 0) + { + throw CreateExceptionForOSStatus(osStatus); + } + + Debug.Fail($"Unexpected result from AppleCryptoNative_X509ExportData: {result}"); + throw new CryptographicException(); + } + + Debug.Assert(!exportData.IsInvalid, "Successful export yielded no data"); + return CoreFoundation.CFGetData(exportData); + } + } + } + + internal static byte[] X509ExportPkcs7(IntPtr[] certHandles) + { + return X509Export(X509ContentType.Pkcs7, s_nullExportString, certHandles); + } + + internal static byte[] X509ExportPfx(IntPtr[] certHandles, SafePasswordHandle exportPassword) + { + SafeCreateHandle cfPassphrase = s_emptyExportString; + bool releasePassword = false; + + try + { + if (!exportPassword.IsInvalid) + { + exportPassword.DangerousAddRef(ref releasePassword); + IntPtr passwordHandle = exportPassword.DangerousGetHandle(); + + if (passwordHandle != IntPtr.Zero) + { + cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle); + } + } + + return X509Export(X509ContentType.Pkcs12, cfPassphrase, certHandles); + } + finally + { + if (releasePassword) + { + exportPassword.DangerousRelease(); + } + + if (cfPassphrase != s_emptyExportString) + { + cfPassphrase.Dispose(); + } + } + } + } +} + +namespace System.Security.Cryptography.X509Certificates +{ + internal sealed class SafeSecIdentityHandle : SafeKeychainItemHandle + { + public SafeSecIdentityHandle() + { + } + } + + internal sealed class SafeSecCertificateHandle : SafeKeychainItemHandle + { + public SafeSecCertificateHandle() + { + } + } +} diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Store.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Store.cs deleted file mode 100644 index 80b8dcd7c3440..0000000000000 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Store.cs +++ /dev/null @@ -1,76 +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; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Apple; -using System.Security.Cryptography.X509Certificates; - -internal static partial class Interop -{ - internal static partial class AppleCrypto - { - [DllImport(Libraries.AppleCryptoNative)] - private static extern int AppleCryptoNative_X509StoreAddCertificate( - SafeKeychainItemHandle cert, - SafeKeychainHandle keychain, - out int pOSStatus); - - [DllImport(Libraries.AppleCryptoNative)] - private static extern int AppleCryptoNative_X509StoreRemoveCertificate( - SafeKeychainItemHandle cert, - SafeKeychainHandle keychain, - bool isReadOnlyMode, - out int pOSStatus); - - internal static void X509StoreAddCertificate(SafeKeychainItemHandle certOrIdentity, SafeKeychainHandle keychain) - { - int osStatus; - int ret = AppleCryptoNative_X509StoreAddCertificate(certOrIdentity, keychain, out osStatus); - - if (ret == 0) - { - throw CreateExceptionForOSStatus(osStatus); - } - - if (ret != 1) - { - Debug.Fail($"Unexpected result from AppleCryptoNative_X509StoreAddCertificate: {ret}"); - throw new CryptographicException(); - } - } - - internal static void X509StoreRemoveCertificate(SafeKeychainItemHandle certHandle, SafeKeychainHandle keychain, bool isReadOnlyMode) - { - int osStatus; - int ret = AppleCryptoNative_X509StoreRemoveCertificate(certHandle, keychain, isReadOnlyMode, out osStatus); - - if (ret == 0) - { - throw CreateExceptionForOSStatus(osStatus); - } - - const int SuccessOrNoMatch = 1; - const int UserTrustExists = 2; - const int AdminTrustExists = 3; - const int ReadOnlyDelete = 4; - - switch (ret) - { - case SuccessOrNoMatch: - break; - case UserTrustExists: - throw new CryptographicException(SR.Cryptography_X509Store_WouldModifyUserTrust); - case AdminTrustExists: - throw new CryptographicException(SR.Cryptography_X509Store_WouldModifyAdminTrust); - case ReadOnlyDelete: - throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); - default: - Debug.Fail($"Unexpected result from AppleCryptoNative_X509StoreRemoveCertificate: {ret}"); - throw new CryptographicException(); - } - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/PemLabels.cs b/src/libraries/Common/src/System/Security/Cryptography/PemLabels.cs index ef4faf884f160..9373bbb48349f 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/PemLabels.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/PemLabels.cs @@ -12,5 +12,6 @@ internal static class PemLabels internal const string RsaPrivateKey = "RSA PRIVATE KEY"; internal const string EcPrivateKey = "EC PRIVATE KEY"; internal const string X509Certificate = "CERTIFICATE"; + internal const string Pkcs7Certificate = "PKCS7"; } } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt index 22eea10958afb..a0d7bfb8fb7b9 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt @@ -32,6 +32,14 @@ if (NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CM ) endif() +if (CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) + set(NATIVECRYPTO_SOURCES + ${NATIVECRYPTO_SOURCES} + pal_keychain_ios.c + pal_x509_ios.c + ) +endif() + if (CLR_CMAKE_TARGET_MACCATALYST) add_definitions(-DTARGET_MACCATALYST) endif() diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/extra_libs.cmake b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/extra_libs.cmake index 9ed82cd31bc29..0ce5577c8cbff 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/extra_libs.cmake +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/extra_libs.cmake @@ -2,6 +2,7 @@ macro(append_extra_cryptography_apple_libs NativeLibsExtra) find_library(COREFOUNDATION_LIBRARY CoreFoundation) find_library(SECURITY_LIBRARY Security) + find_library(FOUNDATION_LIBRARY Foundation) - list(APPEND ${NativeLibsExtra} ${COREFOUNDATION_LIBRARY} ${SECURITY_LIBRARY}) + list(APPEND ${NativeLibsExtra} ${COREFOUNDATION_LIBRARY} ${SECURITY_LIBRARY} ${FOUNDATION_LIBRARY}) endmacro() diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c new file mode 100644 index 0000000000000..e95718f423d55 --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c @@ -0,0 +1,186 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pal_keychain_ios.h" +#include "pal_utilities.h" +#include "pal_x509.h" + +static int32_t +EnumerateKeychain(CFStringRef matchType, CFArrayRef* pCertsOut) +{ + assert(pCertsOut != NULL); + assert(matchType != NULL); + + *pCertsOut = NULL; + + const void *keys[] = { kSecReturnRef, kSecMatchLimit, kSecClass }; + const void *values[] = { kCFBooleanTrue, kSecMatchLimitAll, matchType }; + CFDictionaryRef query = CFDictionaryCreate( + kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + if (query == NULL) + { + return errSecAllocate; + } + + CFTypeRef result = NULL; + OSStatus status; + + status = SecItemCopyMatching(query, &result); + + CFRelease(query); + + if (status == noErr) + { + assert(result != NULL); + assert(CFGetTypeID(result) == CFArrayGetTypeID()); + CFRetain(result); + *pCertsOut = (CFArrayRef)result; + } + else if (status == errSecItemNotFound) + { + assert(result == NULL); + status = noErr; + } + else + { + if (result != NULL) + { + CFRelease(result); + } + } + + return status; +} + +int32_t AppleCryptoNative_SecKeychainEnumerateCerts(CFArrayRef* pCertsOut) +{ + return EnumerateKeychain(kSecClassCertificate, pCertsOut); +} + +int32_t AppleCryptoNative_SecKeychainEnumerateIdentities(CFArrayRef* pIdentitiesOut) +{ + return EnumerateKeychain(kSecClassIdentity, pIdentitiesOut); +} + +int32_t AppleCryptoNative_X509StoreAddCertificate(CFTypeRef certOrIdentity) +{ + OSStatus status; + + assert(certOrIdentity != NULL); + + const void *keys[] = { kSecValueRef }; + const void *values[] = { certOrIdentity }; + CFDictionaryRef query = CFDictionaryCreate( + kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + if (query == NULL) + { + return errSecAllocate; + } + + status = SecItemAdd(query, NULL); + + return status == errSecDuplicateItem ? noErr : status; +} + +int32_t +AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t isReadOnlyMode) +{ + OSStatus status; + CFTypeRef result = NULL; + + assert(certOrIdentity != NULL); + + const void *keys[] = { kSecValueRef }; + const void *values[] = { certOrIdentity }; + CFDictionaryRef query = CFDictionaryCreate( + kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + if (!isReadOnlyMode) + { + CFTypeID inputType = CFGetTypeID(certOrIdentity); + + if (inputType == SecCertificateGetTypeID()) + { + // If we got a certificate as input we have to try to delete private key + // as well. There's one-to-many relationship between keys and certificates + // so we save the public key fingerprint first. Then we delete the + // certificate and look whether there are any remaining ceritificates + // referencing the same key. If none are found then we try to delete + // the key. + + SecCertificateRef cert = (SecCertificateRef)CONST_CAST(void*, certOrIdentity); + SecKeyRef publicKey = NULL; + CFTypeRef publicKeyLabel = NULL; + + if (AppleCryptoNative_X509GetPublicKey(cert, &publicKey, &status)) + { + CFDictionaryRef attrs = SecKeyCopyAttributes(publicKey); + publicKeyLabel = CFRetain(CFDictionaryGetValue(attrs, kSecAttrApplicationLabel)); + CFRelease(attrs); + CFRelease(publicKey); + } + + status = SecItemDelete(query); + + CFRelease(query); + + if (status == noErr && publicKeyLabel != NULL) + { + OSStatus keyStatus; + + const void *keys[] = { kSecClass, kSecAttrPublicKeyHash }; + const void *values[] = { kSecClassCertificate, publicKeyLabel }; + query = CFDictionaryCreate( + kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + result = NULL; + keyStatus = SecItemCopyMatching(query, &result); + + CFRelease(query); + + if (result != NULL) + { + CFRelease(result); + } + + if (keyStatus == errSecItemNotFound) + { + const void *keys[] = { kSecClass, kSecAttrApplicationLabel }; + const void *values[] = { kSecClassKey, publicKeyLabel }; + query = CFDictionaryCreate( + kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + SecItemDelete(query); + + CFRelease(query); + } + } + } + else + { + status = SecItemDelete(query); + + CFRelease(query); + } + } + else + { + status = SecItemCopyMatching(query, &result); + + CFRelease(query); + + if (result != NULL) + { + CFRelease(result); + } + } + + return status; +} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.h new file mode 100644 index 0000000000000..910fee2553a2d --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.h @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "pal_types.h" +#include "pal_compiler.h" + +#include + +/* +Enumerate the certificate objects within the given keychain. + +Returns the last OSStatus value (noErr on success). + +Output: +pCertsOut: When the return value is not noErr, NULL. Otherwise NULL on "no certs found", or a CFArrayRef for the matches +(including a single match). +*/ +PALEXPORT int32_t +AppleCryptoNative_SecKeychainEnumerateCerts(CFArrayRef* pCertsOut); + +/* +Enumerate the certificate objects within the given keychain. + +Returns the last OSStatus value (noErr on success). + +Note that any identity will also necessarily be returned as a certificate with no private key by +SecKeychainEnumerateCerts. De-duplication of values is the responsibility of the caller. + +Output: +pCertsOut: When the return value is not noErr, NULL. Otherwise NULL on "no certs found", or a CFArrayRef for the matches +(including a single match). +*/ +PALEXPORT int32_t AppleCryptoNative_SecKeychainEnumerateIdentities(CFArrayRef* pIdentitiesOut); + +/* +Add a certificate from the specified keychain. + +Returns the last OSStatus value (noErr on success). +*/ +PALEXPORT int32_t +AppleCryptoNative_X509StoreAddCertificate(CFTypeRef certOrIdentity); + +/* +Remove a certificate from the specified keychain. + +Returns the last OSStatus value (noErr on success). +*/ +PALEXPORT int32_t +AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t isReadOnlyMode); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c new file mode 100644 index 0000000000000..194aab74b34aa --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c @@ -0,0 +1,161 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pal_x509.h" +#include "pal_x509_ios.h" +#include "pal_utilities.h" +#include +#include + +int32_t AppleCryptoNative_X509ImportCertificate(uint8_t* pbData, + int32_t cbData, + PAL_X509ContentType contentType, + CFStringRef cfPfxPassphrase, + SecCertificateRef* pCertOut, + SecIdentityRef* pIdentityOut, + int32_t* pOSStatus) +{ + assert(pCertOut != NULL); + assert(pIdentityOut != NULL); + assert(pOSStatus != NULL); + assert(pbData != NULL); + assert(cbData >= 0); + + *pCertOut = NULL; + *pIdentityOut = NULL; + *pOSStatus = noErr; + + CFDataRef cfData = CFDataCreateWithBytesNoCopy(NULL, pbData, cbData, kCFAllocatorNull); + + if (cfData == NULL) + { + *pOSStatus = errSecAllocate; + return 0; + } + + if (contentType == PAL_Certificate) + { + *pCertOut = SecCertificateCreateWithData(NULL, cfData); + CFRelease(cfData); + *pOSStatus = *pCertOut == NULL ? errSecUnknownFormat : 0; + return *pCertOut != NULL; + } + else if (contentType == PAL_Pkcs12) + { + CFArrayRef p12Items = NULL; + CFMutableDictionaryRef attrs = CFDictionaryCreateMutable( + kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attrs, kSecImportExportPassphrase, cfPfxPassphrase); + *pOSStatus = SecPKCS12Import(cfData, attrs, &p12Items); + CFRelease(cfData); + CFRelease(attrs); + if (*pOSStatus == noErr) + { + if (CFArrayGetCount(p12Items) > 0) + { + CFDictionaryRef item_dict = CFArrayGetValueAtIndex(p12Items, 0); + *pIdentityOut = (SecIdentityRef)CFRetain(CFDictionaryGetValue(item_dict, kSecImportItemIdentity)); + } + CFRelease(p12Items); + } + return *pIdentityOut != NULL; + } + + CFRelease(cfData); + *pOSStatus = errSecUnknownFormat; + return 0; +} + + +int32_t AppleCryptoNative_X509ImportCollection(uint8_t* pbData, + int32_t cbData, + PAL_X509ContentType contentType, + CFStringRef cfPfxPassphrase, + CFArrayRef* pCollectionOut, + int32_t* pOSStatus) +{ + CFMutableArrayRef outItems; + + assert(pCollectionOut != NULL); + assert(pOSStatus != NULL); + assert(pbData != NULL); + assert(cbData >= 0); + + *pCollectionOut = NULL; + *pOSStatus = noErr; + + CFDataRef cfData = CFDataCreateWithBytesNoCopy(NULL, pbData, cbData, kCFAllocatorNull); + + if (cfData == NULL) + { + *pOSStatus = errSecAllocate; + return 0; + } + + if (contentType == PAL_Certificate) + { + SecCertificateRef certificate = SecCertificateCreateWithData(NULL, cfData); + CFRelease(cfData); + + if (certificate != NULL) + { + outItems = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); + + if (outItems == NULL) + { + CFRelease(certificate); + *pOSStatus = errSecAllocate; + return 0; + } + + CFArrayAppendValue(outItems, certificate); + *pCollectionOut = outItems; + return 1; + } + else + { + *pOSStatus = errSecUnknownFormat; + return 0; + } + } + else if (contentType == PAL_Pkcs12) + { + CFArrayRef p12Items = NULL; + CFMutableDictionaryRef attrs = CFDictionaryCreateMutable( + kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attrs, kSecImportExportPassphrase, cfPfxPassphrase); + *pOSStatus = SecPKCS12Import(cfData, attrs, &p12Items); + CFRelease(cfData); + CFRelease(attrs); + + if (*pOSStatus == noErr) + { + outItems = CFArrayCreateMutable(NULL, CFArrayGetCount(p12Items), &kCFTypeArrayCallBacks); + + if (outItems == NULL) + { + CFRelease(p12Items); + *pOSStatus = errSecAllocate; + return 0; + } + + for (int i = 0; i < CFArrayGetCount(p12Items); i++) + { + CFDictionaryRef item_dict = CFArrayGetValueAtIndex(p12Items, i); + SecIdentityRef identity = (SecIdentityRef)CFRetain(CFDictionaryGetValue(item_dict, kSecImportItemIdentity)); + CFArrayAppendValue(outItems, identity); + } + + CFRelease(p12Items); + *pCollectionOut = outItems; + + return 1; + } + + return 0; + } + + CFRelease(cfData); + *pOSStatus = errSecUnknownFormat; + return 0; +} \ No newline at end of file diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h new file mode 100644 index 0000000000000..8b1fc59db34ec --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "pal_digest.h" +#include "pal_seckey.h" +#include "pal_compiler.h" +#include + +#include + +/* +Read cbData bytes of data from pbData and interpret it to a single certificate (or identity). + +For a single X.509 certificate, that certificate is emitted. +For a PKCS#7 blob the signing certificate is returned. +For a PKCS#12 blob (PFX) the first public+private pair found is returned, or the first certificate. + +If cfPfxPassphrase represents the NULL (but not empty) passphrase and a PFX import gets a password +error then the empty passphrase is automatically attempted. + +Returns 1 on success, 0 on failure, -2 on a successful read of an empty collection, other values reprepresent invalid +state. + +Output: +pCertOut: If the best matched value was a certificate, receives the SecCertificateRef, otherwise receives NULL +pIdentityOut: If the best matched value was an identity, receives the SecIdentityRef, otherwise receives NULL +pOSStatus: Receives the return of the last call to SecItemImport +*/ +PALEXPORT int32_t AppleCryptoNative_X509ImportCertificate(uint8_t* pbData, + int32_t cbData, + PAL_X509ContentType contentType, + CFStringRef cfPfxPassphrase, + SecCertificateRef* pCertOut, + SecIdentityRef* pIdentityOut, + int32_t* pOSStatus); + +/* +Read cbData bytes of data from pbData and interpret it to a collection of certificates (or identities). + +If cfPfxPassphrase represents the NULL (but not empty) passphrase and a PFX import gets a password +error then the empty passphrase is automatically attempted. + +Returns 1 on success (including empty PKCS7 collections), 0 on failure, other values indicate invalid state. + +Output: +pCollectionOut: Receives an array which contains SecCertificateRef, SecIdentityRef, and possibly other values which were +read out of the provided blob +pOSStatus: Receives the output of SecItemImport for the last attempted read +*/ +PALEXPORT int32_t AppleCryptoNative_X509ImportCollection(uint8_t* pbData, + int32_t cbData, + PAL_X509ContentType contentType, + CFStringRef cfPfxPassphrase, + CFArrayRef* pCollectionOut, + int32_t* pOSStatus); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs new file mode 100644 index 0000000000000..dea33f0d90410 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs @@ -0,0 +1,81 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Text; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class AppleCertificatePal : ICertificatePal + { + private static byte[] pemBegin = new byte[] { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20 }; + + internal delegate bool DerCallback(ReadOnlySpan derData); + + internal static bool TryDecodePem(ReadOnlySpan rawData, DerCallback derCallback) + { + // If the character is a control character that isn't whitespace, then we're probably using a DER encoding + // and not using a PEM encoding in ASCII. + if (char.IsControl((char)rawData[0]) && !char.IsWhiteSpace((char)rawData[0])) + { + return false; + } + + if (rawData.IndexOf(pemBegin) < 0) + { + return false; + } + + char[] certPem = ArrayPool.Shared.Rent(rawData.Length); + byte[]? certBytes = null; + + try + { + Encoding.ASCII.GetChars(rawData, certPem); + + foreach ((ReadOnlySpan contents, PemFields fields) in new PemEnumerator(certPem.AsSpan(0, rawData.Length))) + { + ReadOnlySpan label = contents[fields.Label]; + + if (label.SequenceEqual(PemLabels.X509Certificate) || label.SequenceEqual(PemLabels.Pkcs7Certificate)) + { + certBytes = CryptoPool.Rent(fields.DecodedDataLength); + + if (!Convert.TryFromBase64Chars(contents[fields.Base64Data], certBytes, out int bytesWritten) + || bytesWritten != fields.DecodedDataLength) + { + Debug.Fail("The contents should have already been validated by the PEM reader."); + throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); + } + + bool cont = derCallback(certBytes.AsSpan(0, bytesWritten)); + + CryptoPool.Return(certBytes, clearSize: 0); + certBytes = null; + + if (!cont) + { + return true; + } + } + } + } + finally + { + ArrayPool.Shared.Return(certPem); + + if (certBytes != null) + { + CryptoPool.Return(certBytes, clearSize: 0); + } + } + + return true; + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs new file mode 100644 index 0000000000000..a3c66516cf119 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs @@ -0,0 +1,114 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Formats.Asn1; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Asn1.Pkcs12; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class AppleCertificatePal : ICertificatePal + { + private static SafePasswordHandle s_passwordExportHandle = new SafePasswordHandle("DotnetExportPassphrase"); + + private static AppleCertificatePal ImportPkcs12( + ReadOnlySpan rawData, + SafePasswordHandle password) + { + using (ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData)) + { + reader.Decrypt(password); + return ImportPkcs12(reader.GetSingleCert()); + } + } + + internal static AppleCertificatePal ImportPkcs12(UnixPkcs12Reader.CertAndKey certAndKey) + { + AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert!; + + if (certAndKey.Key != null) + { + Pkcs12SmallExport exporter = new Pkcs12SmallExport(new TempExportPal(pal), certAndKey.Key); + byte[] smallPfx = exporter.Export(X509ContentType.Pkcs12, s_passwordExportHandle)!; + + SafeSecIdentityHandle identityHandle; + SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( + smallPfx, + X509ContentType.Pkcs12, + s_passwordExportHandle, + out identityHandle); + + if (identityHandle.IsInvalid) + { + identityHandle.Dispose(); + return new AppleCertificatePal(certHandle); + } + + certHandle.Dispose(); + return new AppleCertificatePal(identityHandle); + } + + return pal; + } + + private sealed class Pkcs12SmallExport : UnixExportProvider + { + private readonly AsymmetricAlgorithm _privateKey; + + internal Pkcs12SmallExport(ICertificatePalCore cert, AsymmetricAlgorithm privateKey) + : base(cert) + { + _privateKey = privateKey; + } + + protected override byte[] ExportPkcs7() => throw new NotImplementedException(); + + protected override byte[] ExportPkcs8(ICertificatePalCore certificatePal, ReadOnlySpan password) + { + return _privateKey.ExportEncryptedPkcs8PrivateKey(password, s_windowsPbe); + } + } + + private sealed class TempExportPal : ICertificatePalCore + { + private readonly ICertificatePal _realPal; + + internal TempExportPal(AppleCertificatePal realPal) + { + _realPal = realPal; + } + + public bool HasPrivateKey => true; + + public void Dispose() + { + // No-op. + } + + // Forwarders to make the interface compliant. + public IntPtr Handle => _realPal.Handle; + public string Issuer => _realPal.Issuer; + public string Subject => _realPal.Subject; + public string LegacyIssuer => _realPal.LegacyIssuer; + public string LegacySubject => _realPal.LegacySubject; + public byte[] Thumbprint => _realPal.Thumbprint; + public string KeyAlgorithm => _realPal.KeyAlgorithm; + public byte[] KeyAlgorithmParameters => _realPal.KeyAlgorithmParameters; + public byte[] PublicKeyValue => _realPal.PublicKeyValue; + public byte[] SerialNumber => _realPal.SerialNumber; + public string SignatureAlgorithm => _realPal.SignatureAlgorithm; + public DateTime NotAfter => _realPal.NotAfter; + public DateTime NotBefore => _realPal.NotBefore; + public byte[] RawData => _realPal.RawData; + public byte[] Export(X509ContentType contentType, SafePasswordHandle password) => + _realPal.Export(contentType, password); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs new file mode 100644 index 0000000000000..ba2882bf105c9 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs @@ -0,0 +1,494 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Formats.Asn1; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class AppleCertificatePal : ICertificatePal + { + private SafeSecIdentityHandle? _identityHandle; + private SafeSecCertificateHandle _certHandle; + private CertificateData _certData; + private bool _readCertData; + + public static ICertificatePal? FromHandle(IntPtr handle) + { + return FromHandle(handle, true); + } + + internal static ICertificatePal? FromHandle(IntPtr handle, bool throwOnFail) + { + if (handle == IntPtr.Zero) + throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); + + SafeSecCertificateHandle certHandle; + SafeSecIdentityHandle identityHandle; + + if (Interop.AppleCrypto.X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle)) + { + Debug.Assert( + certHandle.IsInvalid != identityHandle.IsInvalid, + $"certHandle.IsInvalid ({certHandle.IsInvalid}) should differ from identityHandle.IsInvalid ({identityHandle.IsInvalid})"); + + if (certHandle.IsInvalid) + { + certHandle.Dispose(); + return new AppleCertificatePal(identityHandle); + } + + identityHandle.Dispose(); + return new AppleCertificatePal(certHandle); + } + + certHandle.Dispose(); + identityHandle.Dispose(); + + if (throwOnFail) + { + throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); + } + + return null; + } + + public static ICertificatePal? FromOtherCert(X509Certificate cert) + { + Debug.Assert(cert.Pal != null); + + ICertificatePal? pal = FromHandle(cert.Handle); + GC.KeepAlive(cert); // ensure cert's safe handle isn't finalized while raw handle is in use + return pal; + } + + private static ICertificatePal FromDerBlob( + ReadOnlySpan rawData, + SafePasswordHandle password, + X509KeyStorageFlags keyStorageFlags) + { + Debug.Assert(password != null); + + X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); + + if (contentType == X509ContentType.Pkcs7) + { + throw new CryptographicException( + SR.Cryptography_X509_PKCS7_Unsupported, + new PlatformNotSupportedException(SR.Cryptography_X509_PKCS7_Unsupported)); + } + + if (contentType == X509ContentType.Pkcs12) + { + // TODO: keyStorageFlags + return ImportPkcs12(rawData, password); + } + + SafeSecIdentityHandle identityHandle; + SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( + rawData, + X509ContentType.Cert, + password, + 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(); + } + + public static ICertificatePal FromBlob( + ReadOnlySpan rawData, + SafePasswordHandle password, + X509KeyStorageFlags keyStorageFlags) + { + Debug.Assert(password != null); + + ICertificatePal? result = null; + TryDecodePem( + rawData, + derData => + { + result = FromDerBlob(derData, password, keyStorageFlags); + return false; + }); + + return result ?? FromDerBlob(rawData, password, keyStorageFlags); + } + + public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) + { + Debug.Assert(password != null); + + byte[] fileBytes = System.IO.File.ReadAllBytes(fileName); + return FromBlob(fileBytes, password, keyStorageFlags); + } + + internal AppleCertificatePal(SafeSecCertificateHandle certHandle) + { + Debug.Assert(!certHandle.IsInvalid); + + _certHandle = certHandle; + } + + internal AppleCertificatePal(SafeSecIdentityHandle identityHandle) + { + Debug.Assert(!identityHandle.IsInvalid); + + _identityHandle = identityHandle; + _certHandle = Interop.AppleCrypto.X509GetCertFromIdentity(identityHandle); + } + + public void Dispose() + { + _certHandle?.Dispose(); + _identityHandle?.Dispose(); + + _certHandle = null!; + _identityHandle = null; + } + + internal SafeSecCertificateHandle CertificateHandle => _certHandle; + internal SafeSecIdentityHandle? IdentityHandle => _identityHandle; + + public bool HasPrivateKey => !(_identityHandle?.IsInvalid ?? true); + + public IntPtr Handle + { + get + { + if (HasPrivateKey) + { + return _identityHandle!.DangerousGetHandle(); + } + + return _certHandle?.DangerousGetHandle() ?? IntPtr.Zero; + } + } + + public string Issuer + { + get + { + EnsureCertData(); + return _certData.IssuerName; + } + } + + public string Subject + { + get + { + EnsureCertData(); + return _certData.SubjectName; + } + } + + public string LegacyIssuer => IssuerName.Decode(X500DistinguishedNameFlags.None); + + public string LegacySubject => SubjectName.Decode(X500DistinguishedNameFlags.None); + + public string KeyAlgorithm + { + get + { + EnsureCertData(); + return _certData.PublicKeyAlgorithm.AlgorithmId!; + } + } + + public byte[] KeyAlgorithmParameters + { + get + { + EnsureCertData(); + return _certData.PublicKeyAlgorithm.Parameters; + } + } + + public byte[] PublicKeyValue + { + get + { + EnsureCertData(); + return _certData.PublicKey; + } + } + + public byte[] SerialNumber + { + get + { + EnsureCertData(); + return _certData.SerialNumber; + } + } + + public string SignatureAlgorithm + { + get + { + EnsureCertData(); + return _certData.SignatureAlgorithm.AlgorithmId!; + } + } + + public string FriendlyName + { + get { return ""; } + set + { + throw new PlatformNotSupportedException( + SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(FriendlyName))); + } + } + + public int Version + { + get + { + EnsureCertData(); + return _certData.Version + 1; + } + } + + public X500DistinguishedName SubjectName + { + get + { + EnsureCertData(); + return _certData.Subject; + } + } + + public X500DistinguishedName IssuerName + { + get + { + EnsureCertData(); + return _certData.Issuer; + } + } + + public PolicyData GetPolicyData() + { + PolicyData policyData = default; + EnsureCertData(); + + foreach (X509Extension extension in _certData.Extensions) + { + switch (extension.Oid!.Value) + { + case Oids.ApplicationCertPolicies: + policyData.ApplicationCertPolicies = extension.RawData; + break; + case Oids.CertPolicies: + policyData.CertPolicies = extension.RawData; + break; + case Oids.CertPolicyMappings: + policyData.CertPolicyMappings = extension.RawData; + break; + case Oids.CertPolicyConstraints: + policyData.CertPolicyConstraints = extension.RawData; + break; + case Oids.EnhancedKeyUsage: + policyData.EnhancedKeyUsage = extension.RawData; + break; + case Oids.InhibitAnyPolicyExtension: + policyData.InhibitAnyPolicyExtension = extension.RawData; + break; + } + } + + return policyData; + } + + public IEnumerable Extensions { + get + { + EnsureCertData(); + return _certData.Extensions; + } + } + + public byte[] RawData + { + get + { + EnsureCertData(); + return _certData.RawData.CloneByteArray(); + } + } + + public DateTime NotAfter + { + get + { + EnsureCertData(); + return _certData.NotAfter.ToLocalTime(); + } + } + + public DateTime NotBefore + { + get + { + EnsureCertData(); + return _certData.NotBefore.ToLocalTime(); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is required for Compat")] + public byte[] Thumbprint + { + get + { + EnsureCertData(); + return SHA1.HashData(_certData.RawData); + } + } + + public bool Archived + { + get { return false; } + set + { + throw new PlatformNotSupportedException( + SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(Archived))); + } + } + + public byte[] SubjectPublicKeyInfo + { + get + { + EnsureCertData(); + + return _certData.SubjectPublicKeyInfo; + } + } + + public RSA? GetRSAPrivateKey() + { + if (_identityHandle == null) + return null; + + Debug.Assert(!_identityHandle.IsInvalid); + SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); + SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); + Debug.Assert(!publicKey.IsInvalid); + + return new RSAImplementation.RSASecurityTransforms(publicKey, privateKey); + } + + public DSA? GetDSAPrivateKey() + { + if (_identityHandle == null) + return null; + + throw new PlatformNotSupportedException(); + } + + public ECDsa? GetECDsaPrivateKey() + { + if (_identityHandle == null) + return null; + + Debug.Assert(!_identityHandle.IsInvalid); + SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); + SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); + Debug.Assert(!publicKey.IsInvalid); + + return new ECDsaImplementation.ECDsaSecurityTransforms(publicKey, privateKey); + } + + public ECDiffieHellman? GetECDiffieHellmanPrivateKey() + { + if (_identityHandle == null) + return null; + + Debug.Assert(!_identityHandle.IsInvalid); + SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); + SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); + Debug.Assert(!publicKey.IsInvalid); + + return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(publicKey, privateKey); + } + + public ICertificatePal CopyWithPrivateKey(DSA privateKey) + { + //return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); + throw new PlatformNotSupportedException("TODO CopyWithPrivateKey"); + } + + public ICertificatePal CopyWithPrivateKey(ECDsa privateKey) + { + return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); + } + + public ICertificatePal CopyWithPrivateKey(ECDiffieHellman privateKey) + { + return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); + } + + public ICertificatePal CopyWithPrivateKey(RSA privateKey) + { + return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); + } + + public string GetNameInfo(X509NameType nameType, bool forIssuer) + { + EnsureCertData(); + return _certData.GetNameInfo(nameType, forIssuer); + } + + public void AppendPrivateKeyInfo(StringBuilder sb) + { + if (!HasPrivateKey) + { + return; + } + + // There's nothing really to say about the key, just acknowledge there is one. + sb.AppendLine(); + sb.AppendLine(); + sb.AppendLine("[Private Key]"); + } + + public byte[] Export(X509ContentType contentType, SafePasswordHandle password) + { + using (IExportPal storePal = StorePal.FromCertificate(this)) + { + byte[]? exported = storePal.Export(contentType, password); + Debug.Assert(exported != null); + return exported; + } + } + + private void EnsureCertData() + { + if (_readCertData) + return; + + Debug.Assert(!_certHandle.IsInvalid); + _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle)); + _readCertData = true; + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12Reader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12Reader.cs new file mode 100644 index 0000000000000..cb83e638f39fa --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12Reader.cs @@ -0,0 +1,76 @@ +// 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; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed class ApplePkcs12Reader : UnixPkcs12Reader + { + internal ApplePkcs12Reader(ReadOnlySpan data) + { + ParsePkcs12(data); + } + + 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.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; + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/CertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/CertificatePal.cs new file mode 100644 index 0000000000000..0689088c77f52 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/CertificatePal.cs @@ -0,0 +1,45 @@ +// 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.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class CertificatePal + { + public static ICertificatePal? FromHandle(IntPtr handle) + { + return FromHandle(handle, true); + } + + internal static ICertificatePal? FromHandle(IntPtr handle, bool throwOnFail) + { + return AppleCertificatePal.FromHandle(handle, throwOnFail); + } + + public static ICertificatePal? FromOtherCert(X509Certificate cert) + { + return AppleCertificatePal.FromOtherCert(cert); + } + + public static ICertificatePal FromBlob( + ReadOnlySpan rawData, + SafePasswordHandle password, + X509KeyStorageFlags keyStorageFlags) + { + return AppleCertificatePal.FromBlob(rawData, password, keyStorageFlags); + } + + public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) + { + return AppleCertificatePal.FromFile(fileName, password, keyStorageFlags); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.AppleKeychainStore.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.AppleKeychainStore.cs new file mode 100644 index 0000000000000..88be19a1e03d5 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.AppleKeychainStore.cs @@ -0,0 +1,77 @@ +// 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.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class StorePal + { + private sealed class AppleKeychainStore : IStorePal + { + private readonly bool _readonly; + + internal AppleKeychainStore(OpenFlags openFlags) + { + _readonly = (openFlags & (OpenFlags.ReadWrite | OpenFlags.MaxAllowed)) == 0; + } + + public void Dispose() + { + } + + public void CloneTo(X509Certificate2Collection collection) + { + HashSet dedupedCerts = new HashSet(); + + using (SafeCFArrayHandle identities = Interop.AppleCrypto.KeychainEnumerateIdentities()) + { + ReadCollection(identities, dedupedCerts); + } + + using (SafeCFArrayHandle certs = Interop.AppleCrypto.KeychainEnumerateCerts()) + { + ReadCollection(certs, dedupedCerts); + } + + foreach (X509Certificate2 cert in dedupedCerts) + { + collection.Add(cert); + } + } + + public void Add(ICertificatePal cert) + { + if (_readonly) + throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); + + AppleCertificatePal applePal = (AppleCertificatePal)cert; + + var handle = (SafeHandle?)applePal.IdentityHandle ?? applePal.CertificateHandle; + Interop.AppleCrypto.X509StoreAddCertificate(handle); + } + + public void Remove(ICertificatePal cert) + { + AppleCertificatePal applePal = (AppleCertificatePal)cert; + + var handle = (SafeHandle?)applePal.IdentityHandle ?? applePal.CertificateHandle; + Interop.AppleCrypto.X509StoreRemoveCertificate(handle, _readonly); + } + + public SafeHandle? SafeHandle { get; } + + public static AppleKeychainStore OpenDefaultKeychain(OpenFlags openFlags) + { + return new AppleKeychainStore(openFlags); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.ExportPal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.ExportPal.cs new file mode 100644 index 0000000000000..7dac40ebfc901 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.ExportPal.cs @@ -0,0 +1,59 @@ +// 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.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class StorePal + { + private sealed class AppleCertificateExporter : UnixExportProvider + { + public AppleCertificateExporter(ICertificatePalCore cert) + : base(cert) + { + } + + public AppleCertificateExporter(X509Certificate2Collection certs) + : base(certs) + { + } + + protected override byte[] ExportPkcs7() + { + throw new CryptographicException( + SR.Cryptography_X509_PKCS7_Unsupported, + new PlatformNotSupportedException(SR.Cryptography_X509_PKCS7_Unsupported)); + } + + protected override byte[] ExportPkcs8(ICertificatePalCore certificatePal, ReadOnlySpan password) + { + Debug.Assert(certificatePal.HasPrivateKey); + AppleCertificatePal pal = (AppleCertificatePal)certificatePal; + AsymmetricAlgorithm algorithm; + + switch (pal.KeyAlgorithm) + { + case Oids.Rsa: + algorithm = pal.GetRSAPrivateKey()!; + break; + case Oids.EcPublicKey: + algorithm = pal.GetECDsaPrivateKey()!; + break; + case Oids.Dsa: + default: + throw new CryptographicException(SR.Format(SR.Cryptography_UnknownKeyAlgorithm, pal.KeyAlgorithm)); + }; + + using (algorithm) + { + return algorithm.ExportEncryptedPkcs8PrivateKey(password, s_windowsPbe); + } + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.LoaderPal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.LoaderPal.cs new file mode 100644 index 0000000000000..ec830c82087aa --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.LoaderPal.cs @@ -0,0 +1,115 @@ +// 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.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class StorePal + { + private sealed class AppleCertLoader : ILoaderPal + { + private readonly SafeCFArrayHandle _collectionHandle; + + public AppleCertLoader(SafeCFArrayHandle collectionHandle) + { + _collectionHandle = collectionHandle; + } + + public void Dispose() + { + _collectionHandle.Dispose(); + } + + public void MoveTo(X509Certificate2Collection collection) + { + long longCount = Interop.CoreFoundation.CFArrayGetCount(_collectionHandle); + + if (longCount > int.MaxValue) + throw new CryptographicException(); + + int count = (int)longCount; + + // Apple returns things in the opposite order from Windows, so read backwards. + for (int i = count - 1; i >= 0; i--) + { + IntPtr handle = Interop.CoreFoundation.CFArrayGetValueAtIndex(_collectionHandle, i); + + if (handle != IntPtr.Zero) + { + ICertificatePal? certPal = CertificatePal.FromHandle(handle, throwOnFail: false); + + if (certPal != null) + { + X509Certificate2 cert = new X509Certificate2(certPal); + collection.Add(cert); + } + } + } + } + } + + private sealed class ApplePemCertLoader : ILoaderPal + { + private readonly List _collection; + + public ApplePemCertLoader(List collection) + { + _collection = collection; + } + + public void Dispose() + { + } + + public void MoveTo(X509Certificate2Collection collection) + { + foreach (ICertificatePal pal in _collection) + { + X509Certificate2 cert = new X509Certificate2(pal); + collection.Add(cert); + } + } + } + + private 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()) + { + AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert!; + collection.Add(new X509Certificate2(AppleCertificatePal.ImportPkcs12(certAndKey))); + } + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.TrustedStore.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.TrustedStore.cs new file mode 100644 index 0000000000000..a4debf88bb62a --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.TrustedStore.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; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class StorePal + { + private sealed class TrustedStore : IStorePal + { + internal TrustedStore() + { + } + + public SafeHandle? SafeHandle => null; + + public void Dispose() + { + } + + public void Add(ICertificatePal cert) + { + throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); + } + + public void Remove(ICertificatePal cert) + { + throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); + } + + public void CloneTo(X509Certificate2Collection collection) + { + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs new file mode 100644 index 0000000000000..e16bcdb949f4e --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs @@ -0,0 +1,160 @@ +// 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.IO; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class StorePal + { + public static IStorePal FromHandle(IntPtr storeHandle) + { + throw new PlatformNotSupportedException($"{nameof(StorePal)}.{nameof(FromHandle)}"); + } + + public static ILoaderPal FromBlob(ReadOnlySpan rawData, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) + { + List? pemCerts = null; + + AppleCertificatePal.TryDecodePem( + rawData, + derData => + { + pemCerts = pemCerts ?? new List(); + pemCerts.Add(AppleCertificatePal.FromBlob(derData, password, keyStorageFlags)); + return true; + }); + + if (pemCerts != null) + { + return new ApplePemCertLoader(pemCerts); + } + + X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); + + if (contentType == X509ContentType.Pkcs7) + { + throw new CryptographicException( + SR.Cryptography_X509_PKCS7_Unsupported, + new PlatformNotSupportedException(SR.Cryptography_X509_PKCS7_Unsupported)); + } + + if (contentType == X509ContentType.Pkcs12) + { + ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData); + + try + { + reader.Decrypt(password); + return new ApplePkcs12CertLoader(reader, password); + } + catch + { + reader.Dispose(); + throw; + } + } + + SafeCFArrayHandle certs = Interop.AppleCrypto.X509ImportCollection( + rawData, + contentType, + password); + + return new AppleCertLoader(certs); + } + + public static ILoaderPal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) + { + Debug.Assert(password != null); + + byte[] fileBytes = File.ReadAllBytes(fileName); + return FromBlob(fileBytes, password, keyStorageFlags); + } + + public static IExportPal FromCertificate(ICertificatePalCore cert) + { + return new AppleCertificateExporter(cert); + } + + public static IExportPal LinkFromCertificateCollection(X509Certificate2Collection certificates) + { + return new AppleCertificateExporter(certificates); + } + + public static IStorePal FromSystemStore(string storeName, StoreLocation storeLocation, OpenFlags openFlags) + { + StringComparer ordinalIgnoreCase = StringComparer.OrdinalIgnoreCase; + + if (ordinalIgnoreCase.Equals("Root", storeName)) + { + throw new CryptographicException( + SR.Cryptography_X509_StoreNotFound, + new PlatformNotSupportedException(SR.Cryptography_X509_Store_RootUnsupported)); + } + + if (storeLocation == StoreLocation.CurrentUser) + { + if (ordinalIgnoreCase.Equals("My", storeName)) + return AppleKeychainStore.OpenDefaultKeychain(openFlags); + if (ordinalIgnoreCase.Equals("Disallowed", storeName)) + return new UnsupportedDisallowedStore(openFlags); + } + + if ((openFlags & OpenFlags.OpenExistingOnly) == OpenFlags.OpenExistingOnly) + throw new CryptographicException(SR.Cryptography_X509_StoreNotFound); + + string message = SR.Format( + SR.Cryptography_X509_StoreCannotCreate, + storeName, + storeLocation); + + throw new CryptographicException(message, new PlatformNotSupportedException(message)); + } + + private static void ReadCollection(SafeCFArrayHandle matches, HashSet collection) + { + if (matches.IsInvalid) + { + return; + } + + long count = Interop.CoreFoundation.CFArrayGetCount(matches); + + for (int i = 0; i < count; i++) + { + IntPtr handle = Interop.CoreFoundation.CFArrayGetValueAtIndex(matches, i); + + SafeSecCertificateHandle certHandle; + SafeSecIdentityHandle identityHandle; + + if (Interop.AppleCrypto.X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle)) + { + X509Certificate2 cert; + + if (certHandle.IsInvalid) + { + certHandle.Dispose(); + cert = new X509Certificate2(new AppleCertificatePal(identityHandle)); + } + else + { + identityHandle.Dispose(); + cert = new X509Certificate2(new AppleCertificatePal(certHandle)); + } + + if (!collection.Add(cert)) + { + cert.Dispose(); + } + } + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs new file mode 100644 index 0000000000000..c516aa0d16b1e --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs @@ -0,0 +1,293 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Formats.Asn1; +using System.Text; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Asn1.Pkcs12; +using System.Security.Cryptography.Asn1.Pkcs7; +using System.Security.Cryptography.X509Certificates; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class X509Pal + { + public static IX509Pal Instance = new AppleX509Pal(); + + private X509Pal() + { + } + + private sealed partial class AppleX509Pal : ManagedX509ExtensionProcessor, IX509Pal + { + public ECDsa DecodeECDsaPublicKey(ICertificatePal? certificatePal) + { + return new ECDsaImplementation.ECDsaSecurityTransforms(DecodeECPublicKey(certificatePal)); + } + + public ECDiffieHellman DecodeECDiffieHellmanPublicKey(ICertificatePal? certificatePal) + { + return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(DecodeECPublicKey(certificatePal)); + } + + public AsymmetricAlgorithm DecodePublicKey(Oid oid, byte[] encodedKeyValue, byte[] encodedParameters, + ICertificatePal? certificatePal) + { + AppleCertificatePal? applePal = certificatePal as AppleCertificatePal; + + if (applePal != null) + { + SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPublicKey(applePal.CertificateHandle); + + switch (oid.Value) + { + case Oids.Rsa: + Debug.Assert(!key.IsInvalid); + return new RSAImplementation.RSASecurityTransforms(key); + case Oids.Dsa: + if (key.IsInvalid) + { + // SecCertificateCopyKey returns null for DSA, so fall back to manually building it. + return DecodeDsaPublicKey(encodedKeyValue, encodedParameters); + } + return new DSAImplementation.DSASecurityTransforms(key); + } + + key.Dispose(); + } + else + { + switch (oid.Value) + { + case Oids.Rsa: + return DecodeRsaPublicKey(encodedKeyValue); + case Oids.Dsa: + return DecodeDsaPublicKey(encodedKeyValue, encodedParameters); + } + } + + throw new NotSupportedException(SR.NotSupported_KeyAlgorithm); + } + + private static SafeSecKeyRefHandle DecodeECPublicKey(ICertificatePal? certificatePal) + { + const int errSecInvalidKeyRef = -67712; + const int errSecUnsupportedKeySize = -67735; + + if (certificatePal is null) + throw new NotSupportedException(SR.NotSupported_KeyAlgorithm); + + AppleCertificatePal applePal = (AppleCertificatePal)certificatePal; + SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPublicKey(applePal.CertificateHandle); + + // If X509GetPublicKey uses the new SecCertificateCopyKey API it can return an invalid + // key reference for unsupported algorithms. This currently happens for the BrainpoolP160r1 + // algorithm in the test suite (as of macOS Mojave Developer Preview 4). + if (key.IsInvalid) + { + throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecInvalidKeyRef); + } + // EccGetKeySizeInBits can fail for two reasons. First, the Apple implementation has changed + // and we receive values from API that were not previously handled. In that case the + // implementation will need to be adjusted to handle these values. Second, we deliberately + // return 0 from the native code to prevent hitting buggy API implementations in Apple code + // later. + if (Interop.AppleCrypto.EccGetKeySizeInBits(key) == 0) + { + key.Dispose(); + throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnsupportedKeySize); + } + + return key; + } + + private static AsymmetricAlgorithm DecodeRsaPublicKey(byte[] encodedKeyValue) + { + RSA rsa = RSA.Create(); + try + { + rsa.ImportRSAPublicKey(new ReadOnlySpan(encodedKeyValue), out _); + return rsa; + } + catch (Exception) + { + rsa.Dispose(); + throw; + } + } + + private static AsymmetricAlgorithm DecodeDsaPublicKey(byte[] encodedKeyValue, byte[] encodedParameters) + { + SubjectPublicKeyInfoAsn spki = new SubjectPublicKeyInfoAsn + { + Algorithm = new AlgorithmIdentifierAsn { Algorithm = Oids.Dsa, Parameters = encodedParameters }, + SubjectPublicKey = encodedKeyValue, + }; + + AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); + spki.Encode(writer); + + byte[] rented = CryptoPool.Rent(writer.GetEncodedLength()); + + if (!writer.TryEncode(rented, out int written)) + { + Debug.Fail("TryEncode failed with a pre-allocated buffer"); + throw new InvalidOperationException(); + } + + DSA dsa = DSA.Create(); + IDisposable? toDispose = dsa; + + try + { + dsa.ImportSubjectPublicKeyInfo(rented.AsSpan(0, written), out _); + toDispose = null; + return dsa; + } + finally + { + toDispose?.Dispose(); + CryptoPool.Return(rented, written); + } + } + + public string X500DistinguishedNameDecode(byte[] encodedDistinguishedName, X500DistinguishedNameFlags flag) + { + return X500NameEncoder.X500DistinguishedNameDecode(encodedDistinguishedName, true, flag); + } + + public byte[] X500DistinguishedNameEncode(string distinguishedName, X500DistinguishedNameFlags flag) + { + return X500NameEncoder.X500DistinguishedNameEncode(distinguishedName, flag); + } + + public string X500DistinguishedNameFormat(byte[] encodedDistinguishedName, bool multiLine) + { + return X500NameEncoder.X500DistinguishedNameDecode( + encodedDistinguishedName, + true, + multiLine ? X500DistinguishedNameFlags.UseNewLines : X500DistinguishedNameFlags.None, + multiLine); + } + + private static bool IsPkcs12(ReadOnlySpan rawData) + { + try + { + unsafe + { + fixed (byte* pin = rawData) + { + using (var manager = new PointerMemoryManager(pin, rawData.Length)) + { + PfxAsn.Decode(manager.Memory, AsnEncodingRules.BER); + } + + return true; + } + } + } + catch (CryptographicException) + { + } + + return false; + } + + private static bool IsPkcs7Signed(ReadOnlySpan rawData) + { + try + { + unsafe + { + fixed (byte* pin = rawData) + { + using (var manager = new PointerMemoryManager(pin, rawData.Length)) + { + AsnValueReader reader = new AsnValueReader(rawData, AsnEncodingRules.BER); + + ContentInfoAsn.Decode(ref reader, manager.Memory, out ContentInfoAsn contentInfo); + + switch (contentInfo.ContentType) + { + case Oids.Pkcs7Signed: + case Oids.Pkcs7SignedEnveloped: + return true; + } + } + } + } + } + catch (CryptographicException) + { + } + + return false; + } + + private X509ContentType GetDerCertContentType(ReadOnlySpan rawData) + { + const int errSecUnknownFormat = -25257; + + X509ContentType contentType = Interop.AppleCrypto.X509GetContentType(rawData); + + // Apple doesn't seem to recognize PFX files with no MAC, so try a quick maybe-it's-a-PFX test + if (contentType == X509ContentType.Unknown) + { + if (IsPkcs12(rawData)) + { + return X509ContentType.Pkcs12; + } + + if (IsPkcs7Signed(rawData)) + { + return X509ContentType.Pkcs7; + } + + // Throw to match Windows and Unix behavior. + throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnknownFormat); + } + + return contentType; + } + + public X509ContentType GetCertContentType(ReadOnlySpan rawData) + { + const int errSecUnknownFormat = -25257; + + if (rawData == null || rawData.Length == 0) + { + // Throw to match Windows and Unix behavior. + throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnknownFormat); + } + + X509ContentType result = X509ContentType.Unknown; + + AppleCertificatePal.TryDecodePem( + rawData, + derData => + { + result = GetDerCertContentType(derData); + return false; + }); + + if (result != X509ContentType.Unknown) + { + return result; + } + + return GetDerCertContentType(rawData); + } + + public X509ContentType GetCertContentType(string fileName) + { + return GetCertContentType(System.IO.File.ReadAllBytes(fileName)); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx index 97eefea5eff5b..a5c33a43b40b8 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx @@ -292,6 +292,9 @@ Cannot find the original signer. + + PKCS#7 certificate format is not supported on this platform. + The X509 certificate could not be added to the store. @@ -319,6 +322,9 @@ Failed to enumerate certificates from the store. + + Root certificate store is not supported on this platform. + The certificate contents do not contain a PEM with a CERTIFICATE label, or the content is malformed. diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 44505d275b80e..407d5e1860c90 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -476,8 +476,6 @@ Link="Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Ecc.cs" /> - - Common\System\Security\Cryptography\Asn1\SpecifiedECDomain.xml.cs Common\System\Security\Cryptography\Asn1\SpecifiedECDomain.xml - - - - - - - - - - - - + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + new X509BasicConstraintsExtension( certificateAuthority: true, hasPathLengthConstraint: false, @@ -113,7 +115,7 @@ DateTime RewindIfNeeded(DateTime input, X509Certificate2 cert, X509ChainStatusFl intermediateCert = TamperIfNeeded(intermediateCert, intermediateErrors); rootCert = TamperIfNeeded(rootCert, rootErrors); - if (OperatingSystem.IsMacOS()) + if (IsApple) { // For the lower levels, turn NotSignatureValid into PartialChain, // and clear all errors at higher levels. @@ -138,7 +140,7 @@ DateTime RewindIfNeeded(DateTime input, X509Certificate2 cert, X509ChainStatusFl rootErrors &= ~X509ChainStatusFlags.NotSignatureValid; // On 10.13+ it becomes PartialChain, and UntrustedRoot goes away. - if (PlatformDetection.IsOSX) + if (IsApple) { rootErrors &= ~X509ChainStatusFlags.UntrustedRoot; rootErrors |= X509ChainStatusFlags.PartialChain; @@ -559,7 +561,7 @@ public static void NameConstraintViolation_ExcludedTree_Dns() } [Fact] - [SkipOnPlatform(TestPlatforms.OSX, "macOS appears to just completely ignore min/max.")] + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "macOS appears to just completely ignore min/max.")] public static void NameConstraintViolation_PermittedTree_HasMin() { SubjectAlternativeNameBuilder builder = new SubjectAlternativeNameBuilder(); @@ -844,7 +846,7 @@ private static X509ChainStatusFlags PlatformBasicConstraints(X509ChainStatusFlag private static X509ChainStatusFlags PlatformNameConstraints(X509ChainStatusFlags flags) { - if (OperatingSystem.IsMacOS()) + if (IsApple) { const X509ChainStatusFlags AnyNameConstraintFlags = X509ChainStatusFlags.HasExcludedNameConstraint | @@ -871,7 +873,7 @@ private static X509ChainStatusFlags PlatformNameConstraints(X509ChainStatusFlags private static X509ChainStatusFlags PlatformPolicyConstraints(X509ChainStatusFlags flags) { - if (OperatingSystem.IsMacOS()) + if (IsApple) { const X509ChainStatusFlags AnyPolicyConstraintFlags = X509ChainStatusFlags.NoIssuanceChainPolicy; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs index 23c14801215ae..924cb8ea70bdc 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs @@ -231,6 +231,7 @@ public static void FindByValidThumbprint_ValidOnly(bool validOnly) } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] public static void FindByValidThumbprint_RootCert() { using (X509Store machineRoot = new X509Store(StoreName.Root, StoreLocation.LocalMachine)) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs index 5d753d6a0610f..cd83fdc4ae5b1 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs @@ -230,8 +230,8 @@ public static void ECDHPrivateKeyProperty_WindowsPfx() } } -#if !NO_DSA_AVAILABLE [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void DsaPrivateKeyProperty() { using (var cert = new X509Certificate2(TestData.Dsa1024Pfx, TestData.Dsa1024PfxPassword, Cert.EphemeralIfPossible)) @@ -252,7 +252,6 @@ public static void DsaPrivateKeyProperty() Assert.False(dsa.VerifyData(data, sig, HashAlgorithmName.SHA1), "Key verifies tampered data signature"); } } -#endif private static void Verify_ECDsaPrivateKey_WindowsPfx(ECDsa ecdsa) { @@ -327,8 +326,8 @@ public static void ReadECDsaPrivateKey_OpenSslPfx(X509KeyStorageFlags keyStorage } } -#if !NO_DSA_AVAILABLE [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void ReadDSAPrivateKey() { byte[] data = { 1, 2, 3, 4, 5 }; @@ -349,7 +348,6 @@ public static void ReadDSAPrivateKey() Assert.ThrowsAny(() => pubKey.SignData(data, HashAlgorithmName.SHA1)); } } -#endif #if !NO_EPHEMERALKEYSET_AVAILABLE [Fact] diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs index 346cf59002698..2db85fb176ccb 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs @@ -105,6 +105,7 @@ public static void TestPublicKey_Key_RSA() } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void TestPublicKey_Key_DSA() { PublicKey pk = GetTestDsaKey(); @@ -563,8 +564,8 @@ public static void TestECDsa224PublicKey() } } -#if !NO_DSA_AVAILABLE [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void TestDSAPublicKey() { using (var cert = new X509Certificate2(TestData.DssCer)) @@ -576,6 +577,7 @@ public static void TestDSAPublicKey() } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void TestDSAPublicKey_VerifiesSignature() { byte[] data = { 1, 2, 3, 4, 5 }; @@ -595,6 +597,7 @@ public static void TestDSAPublicKey_VerifiesSignature() } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void TestDSAPublicKey_RSACert() { using (var cert = new X509Certificate2(TestData.Rsa384CertificatePemBytes)) @@ -605,6 +608,7 @@ public static void TestDSAPublicKey_RSACert() } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void TestDSAPublicKey_ECDSACert() { using (var cert = new X509Certificate2(TestData.ECDsa256Certificate)) @@ -613,7 +617,6 @@ public static void TestDSAPublicKey_ECDSACert() Assert.Null(pubKey); } } -#endif [Fact] [PlatformSpecific(TestPlatforms.Windows)] // Uses P/Invokes @@ -666,6 +669,7 @@ public static void ExportSubjectPublicKeyInfo_RSA() } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void ExportSubjectPublicKeyInfo_DSA() { using DSA dsa = DSA.Create(); @@ -738,6 +742,7 @@ public static void CreateFromSubjectPublicKeyInfo_Roundtrip_RSA() } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void CreateFromSubjectPublicKeyInfo_Roundtrip_DSA() { using DSA dsa = DSA.Create(); @@ -783,6 +788,7 @@ public static void CreateFromSubjectPublicKeyInfo_Roundtrip_ECDH() } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void CreateFromSubjectPublicKeyInfo_Roundtrip_DSA_InvalidKey() { // The DSA key is invalid here, but we should be able to round-trip the diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RSAOther.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RSAOther.cs index 75505f0ad9258..860c5507bb172 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RSAOther.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RSAOther.cs @@ -14,6 +14,12 @@ internal RSAOther() _impl = RSA.Create(); } + public override int KeySize + { + get => _impl.KeySize; + set => _impl.KeySize = value; + } + public override KeySizes[] LegalKeySizes => _impl.LegalKeySizes; public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) => _impl.Decrypt(data, padding); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.X509Certificates/tests/Resources/Strings.resx index 7c6a685484082..00e896df64b5b 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/Resources/Strings.resx @@ -153,4 +153,13 @@ The input to WriteEncodedValue must represent a single encoded value with no trailing data. + + Removing the requested certificate would modify user trust settings, and has been denied. + + + Removing the requested certificate would modify admin trust settings, and has been denied. + + + The X509 certificate store is read-only. + diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs index 4dc2a2ae254a0..2bcdb38e9e0db 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs @@ -48,6 +48,7 @@ public static void EmptyAiaResponseIsIgnored() } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "CA store is not available on iOS/tvOS/MacCatalyst")] public static void DisableAiaOptionWorks() { CertificateAuthority.BuildPrivatePki( diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs index 95a26ec5ded86..7bf6fce647409 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs @@ -21,7 +21,7 @@ public static partial class DynamicRevocationTests private static bool SupportsEntireChainCheck => !PlatformDetection.IsAndroid; private static readonly X509ChainStatusFlags ThisOsRevocationStatusUnknown = - PlatformDetection.IsOSX || PlatformDetection.IsAndroid ? + PlatformDetection.IsOSX || PlatformDetection.IsiOS || PlatformDetection.IstvOS || PlatformDetection.IsMacCatalyst || PlatformDetection.IsAndroid ? X509ChainStatusFlags.RevocationStatusUnknown : X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation; @@ -76,7 +76,7 @@ public static IEnumerable AllViableRevocation // https://github.com/dotnet/runtime/issues/31249 // not all scenarios are working on macOS. - if (PlatformDetection.IsOSX) + if (PlatformDetection.IsOSX || PlatformDetection.IsiOS || PlatformDetection.IstvOS || PlatformDetection.IsMacCatalyst) { if (!endEntityRevocation.HasFlag(PkiOptions.EndEntityRevocationViaOcsp)) { @@ -122,7 +122,7 @@ public static void NothingRevoked(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeIntermediate(PkiOptions pkiOptions) { SimpleTest( @@ -191,7 +191,7 @@ public static void RevokeLeafWithAiaFetchingDisabled(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeIntermediateAndEndEntity(PkiOptions pkiOptions) { SimpleTest( @@ -220,7 +220,7 @@ public static void RevokeIntermediateAndEndEntity(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeRoot(PkiOptions pkiOptions) { SimpleTest( @@ -257,7 +257,7 @@ public static void RevokeRoot(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeRootAndEndEntity(PkiOptions pkiOptions) { SimpleTest( @@ -292,7 +292,7 @@ public static void RevokeRootAndEndEntity(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeRootAndIntermediate(PkiOptions pkiOptions) { SimpleTest( @@ -328,7 +328,7 @@ public static void RevokeRootAndIntermediate(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeEverything(PkiOptions pkiOptions) { SimpleTest( @@ -451,7 +451,7 @@ public static void RevokeEndEntity_IssuerUnrelatedOcsp(PkiOptions pkiOptions) [Theory] [InlineData(PkiOptions.OcspEverywhere)] [InlineData(PkiOptions.IssuerRevocationViaOcsp | PkiOptions.AllEndEntityRevocation)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeEndEntity_RootUnrelatedOcsp(PkiOptions pkiOptions) { SimpleTest( @@ -554,7 +554,7 @@ public static IEnumerable PolicyErrorsNotTimeValidData } [Theory] [MemberData(nameof(PolicyErrorsNotTimeValidData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeIntermediate_PolicyErrors_NotTimeValid(bool policyErrors, bool notTimeValid) { SimpleTest( @@ -639,7 +639,7 @@ public static void RevokeIntermediate_PolicyErrors_NotTimeValid(bool policyError [Theory] [MemberData(nameof(PolicyErrorsNotTimeValidData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeEndEntity_PolicyErrors_NotTimeValid(bool policyErrors, bool notTimeValid) { SimpleTest( @@ -721,7 +721,7 @@ public static void RevokeEndEntity_PolicyErrors_NotTimeValid(bool policyErrors, [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeEndEntity_RootRevocationOffline(PkiOptions pkiOptions) { BuildPrivatePki( @@ -798,7 +798,7 @@ public static void RevokeEndEntity_RootRevocationOffline(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void NothingRevoked_RootRevocationOffline(PkiOptions pkiOptions) { BuildPrivatePki( @@ -888,7 +888,7 @@ public static void RevokeEndEntityWithInvalidRevocationSignature(PkiOptions pkiO [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeIntermediateWithInvalidRevocationSignature(PkiOptions pkiOptions) { SimpleTest( @@ -917,7 +917,7 @@ public static void RevokeEndEntityWithInvalidRevocationName(PkiOptions pkiOption [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeIntermediateWithInvalidRevocationName(PkiOptions pkiOptions) { SimpleTest( @@ -968,7 +968,7 @@ public static void RevokeEndEntityWithExpiredRevocation(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void RevokeIntermediateWithExpiredRevocation(PkiOptions pkiOptions) { SimpleTest( @@ -1035,7 +1035,7 @@ public static void CheckEndEntityWithExpiredRevocation(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void CheckIntermediateWithExpiredRevocation(PkiOptions pkiOptions) { bool usingCrl = pkiOptions.HasFlag(PkiOptions.IssuerRevocationViaCrl) || pkiOptions.HasFlag(PkiOptions.EndEntityRevocationViaCrl); @@ -1061,7 +1061,7 @@ public static void CheckIntermediateWithExpiredRevocation(PkiOptions pkiOptions) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void TestRevocationWithNoNextUpdate_NotRevoked() { SimpleTest( @@ -1098,7 +1098,7 @@ public static void TestRevocationWithNoNextUpdate_NotRevoked() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void TestRevocationWithNoNextUpdate_Revoked() { SimpleTest( @@ -1134,7 +1134,7 @@ public static void TestRevocationWithNoNextUpdate_Revoked() } [Fact] - [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.OSX, "Android and macOS do not support offline revocation chain building.")] + [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Android and macOS do not support offline revocation chain building.")] public static void TestRevocation_Offline_NotRevoked() { SimpleTest( @@ -1178,7 +1178,7 @@ public static void TestRevocation_Offline_NotRevoked() } [Fact] - [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.OSX, "Android and macOS do not support offline revocation chain building.")] + [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Android and macOS do not support offline revocation chain building.")] public static void TestRevocation_Offline_Revoked() { SimpleTest( diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj index 4aebe6daec3aa..a3ecd6dbeaaf8 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj @@ -95,7 +95,7 @@ Link="Common\System\IO\PersistedFiles.Names.Unix.cs" /> - + @@ -115,8 +115,8 @@ - - + + - + + + + diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestFiles.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestFiles.cs index 7015c10fd621f..5d93c1c475661 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestFiles.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestFiles.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests { internal static class TestFiles { - internal const string TestDataFolder = "TestData"; + internal const string TestDataFolder = "";//"TestData"; // Certs internal static readonly string MsCertificateDerFile = Path.Combine(TestDataFolder, "MS.cer"); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs index 521fae31a528f..24da8bc73fbcf 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs @@ -266,6 +266,7 @@ public static void CreateFromPem_ECDH_Pkcs8_Success() } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void CreateFromPem_Dsa_Pkcs8_Success() { using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.DsaCertificate, TestData.DsaPkcs8Key)) @@ -276,6 +277,7 @@ public static void CreateFromPem_Dsa_Pkcs8_Success() } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] public static void CreateFromPem_Dsa_EncryptedPkcs8_Success() { X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPem( diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.iOS.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.iOS.cs new file mode 100644 index 0000000000000..29ae5f8927c1f --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.iOS.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + public static partial class X509StoreMutableTests + { + public static bool PermissionsAllowStoreWrite { get; } = true; + + [Theory] + [InlineData(nameof(TestData.RsaCertificate), TestData.RsaCertificate, TestData.RsaPkcs8Key)] + [InlineData(nameof(TestData.EcDhCertificate), TestData.EcDhCertificate, TestData.EcDhPkcs8Key)] + [InlineData(nameof(TestData.ECDsaCertificate), TestData.ECDsaCertificate, TestData.ECDsaPkcs8Key)] + public static void AddRemove_CertWithPrivateKey(string testCase, string certPem, string keyPem) + { + _ = testCase; + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + using (var cert = X509Certificate2.CreateFromPem(certPem, keyPem)) + { + store.Open(OpenFlags.ReadWrite); + + // Make sure cert is not already in the store + store.Remove(cert); + Assert.False(IsCertInStore(cert, store), "Certificate should not be found on pre-condition"); + + // Add + store.Add(cert); + Assert.True(IsCertInStore(cert, store), "Certificate should be found after add"); + Assert.True(StoreHasPrivateKey(store, cert), "Certificate in store should have a private key"); + + // Remove + store.Remove(cert); + Assert.False(IsCertInStore(cert, store), "Certificate should not be found after remove"); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs index ea0906d73e1d2..4169edd3e4674 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs @@ -405,6 +405,7 @@ public static void OpenMachineMyStore_NotSupported() [Theory] [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.OSX)] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] [InlineData(OpenFlags.ReadOnly, false)] [InlineData(OpenFlags.MaxAllowed, false)] [InlineData(OpenFlags.ReadWrite, true)] @@ -427,6 +428,7 @@ public static void OpenMachineRootStore_Permissions(OpenFlags permissions, bool } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] public static void MachineRootStore_NonEmpty() { // This test will fail on systems where the administrator has gone out of their From 92c5ac673f39d7eedadc2f87236e2abdf00a335a Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Mon, 3 May 2021 17:18:24 +0200 Subject: [PATCH 02/27] Remove debugging code --- .../System.Security.Cryptography.Native.Apple/extra_libs.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/extra_libs.cmake b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/extra_libs.cmake index 0ce5577c8cbff..9ed82cd31bc29 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/extra_libs.cmake +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/extra_libs.cmake @@ -2,7 +2,6 @@ macro(append_extra_cryptography_apple_libs NativeLibsExtra) find_library(COREFOUNDATION_LIBRARY CoreFoundation) find_library(SECURITY_LIBRARY Security) - find_library(FOUNDATION_LIBRARY Foundation) - list(APPEND ${NativeLibsExtra} ${COREFOUNDATION_LIBRARY} ${SECURITY_LIBRARY} ${FOUNDATION_LIBRARY}) + list(APPEND ${NativeLibsExtra} ${COREFOUNDATION_LIBRARY} ${SECURITY_LIBRARY}) endmacro() From 770337feec2c269a115f9731e4d150c3d1d14901 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Mon, 3 May 2021 18:50:37 +0200 Subject: [PATCH 03/27] Fix AppleCryptoNative_X509GetPublicKey usage and implementation --- .../pal_keychain_ios.c | 3 ++- .../System.Security.Cryptography.Native.Apple/pal_x509.c | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c index e95718f423d55..399ca3d3e9f3e 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c @@ -116,8 +116,9 @@ AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t i SecCertificateRef cert = (SecCertificateRef)CONST_CAST(void*, certOrIdentity); SecKeyRef publicKey = NULL; CFTypeRef publicKeyLabel = NULL; + int32_t dummyStatus; - if (AppleCryptoNative_X509GetPublicKey(cert, &publicKey, &status)) + if (AppleCryptoNative_X509GetPublicKey(cert, &publicKey, &dummyStatus)) { CFDictionaryRef attrs = SecKeyCopyAttributes(publicKey); publicKeyLabel = CFRetain(CFDictionaryGetValue(attrs, kSecAttrApplicationLabel)); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c index b044a1a7eca9e..4029b18680fa9 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c @@ -42,8 +42,17 @@ AppleCryptoNative_X509DemuxAndRetainHandle(CFTypeRef handle, SecCertificateRef* static void InitCertificateCopy() { +#if defined(TARGET_IOS) || defined(TARGET_TVOS) + // SecCertificateCopyPublicKey on iOS/tvOS has same function prototype as SecCertificateCopyKey + secCertificateCopyKey = (SecKeyRef (*)(SecCertificateRef))dlsym(RTLD_DEFAULT, "SecCertificateCopyKey"); + if (secCertificateCopyKey == NULL) + { + secCertificateCopyKey = (SecKeyRef (*)(SecCertificateRef))dlsym(RTLD_DEFAULT, "SecCertificateCopyPublicKey"); + } +#else secCertificateCopyKey = (SecKeyRef (*)(SecCertificateRef))dlsym(RTLD_DEFAULT, "SecCertificateCopyKey"); secCertificateCopyPublicKey = (OSStatus (*)(SecCertificateRef, SecKeyRef*))dlsym(RTLD_DEFAULT, "SecCertificateCopyPublicKey"); +#endif } int32_t From 6d4c67c7fda08489fc1f9a9695793df7519370c7 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Mon, 3 May 2021 18:51:50 +0200 Subject: [PATCH 04/27] Revert workaround for AppleAppBuilder bug --- .../tests/TestFiles.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestFiles.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestFiles.cs index 5d93c1c475661..7015c10fd621f 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestFiles.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestFiles.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests { internal static class TestFiles { - internal const string TestDataFolder = "";//"TestData"; + internal const string TestDataFolder = "TestData"; // Certs internal static readonly string MsCertificateDerFile = Path.Combine(TestDataFolder, "MS.cer"); From 10d3b1e057bd244bf83193c607ed25a1411913c2 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Mon, 3 May 2021 19:04:40 +0200 Subject: [PATCH 05/27] Avoid double lookup for PEM markers, remove dead DSA code --- .../Pal.iOS/AppleCertificatePal.cs | 82 ++++++++++- .../Internal/Cryptography/Pal.iOS/StorePal.cs | 2 +- .../Internal/Cryptography/Pal.iOS/X509Pal.cs | 131 ++---------------- 3 files changed, 92 insertions(+), 123 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs index ba2882bf105c9..af09f37981c3f 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs @@ -9,6 +9,9 @@ using System.Security.Cryptography; using System.Security.Cryptography.Apple; using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Asn1.Pkcs12; +using System.Security.Cryptography.Asn1.Pkcs7; using System.Text; using System.Threading; using Microsoft.Win32.SafeHandles; @@ -71,6 +74,81 @@ internal sealed partial class AppleCertificatePal : ICertificatePal return pal; } + private static bool IsPkcs12(ReadOnlySpan rawData) + { + try + { + unsafe + { + fixed (byte* pin = rawData) + { + using (var manager = new PointerMemoryManager(pin, rawData.Length)) + { + PfxAsn.Decode(manager.Memory, AsnEncodingRules.BER); + } + + return true; + } + } + } + catch (CryptographicException) + { + } + + return false; + } + + private static bool IsPkcs7Signed(ReadOnlySpan rawData) + { + try + { + unsafe + { + fixed (byte* pin = rawData) + { + using (var manager = new PointerMemoryManager(pin, rawData.Length)) + { + AsnValueReader reader = new AsnValueReader(rawData, AsnEncodingRules.BER); + + ContentInfoAsn.Decode(ref reader, manager.Memory, out ContentInfoAsn contentInfo); + + switch (contentInfo.ContentType) + { + case Oids.Pkcs7Signed: + case Oids.Pkcs7SignedEnveloped: + return true; + } + } + } + } + } + catch (CryptographicException) + { + } + + return false; + } + + internal static X509ContentType GetDerCertContentType(ReadOnlySpan rawData) + { + X509ContentType contentType = Interop.AppleCrypto.X509GetContentType(rawData); + + if (contentType == X509ContentType.Unknown) + { + if (IsPkcs12(rawData)) + { + return X509ContentType.Pkcs12; + } + + if (IsPkcs7Signed(rawData)) + { + return X509ContentType.Pkcs7; + } + } + + return contentType; + } + private static ICertificatePal FromDerBlob( ReadOnlySpan rawData, SafePasswordHandle password, @@ -78,7 +156,7 @@ private static ICertificatePal FromDerBlob( { Debug.Assert(password != null); - X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); + X509ContentType contentType = GetDerCertContentType(rawData); if (contentType == X509ContentType.Pkcs7) { @@ -96,7 +174,7 @@ private static ICertificatePal FromDerBlob( SafeSecIdentityHandle identityHandle; SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( rawData, - X509ContentType.Cert, + contentType, password, out identityHandle); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs index e16bcdb949f4e..59eba05d67508 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs @@ -37,7 +37,7 @@ public static ILoaderPal FromBlob(ReadOnlySpan rawData, SafePasswordHandle return new ApplePemCertLoader(pemCerts); } - X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); + X509ContentType contentType = AppleCertificatePal.GetDerCertContentType(rawData); if (contentType == X509ContentType.Pkcs7) { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs index c516aa0d16b1e..f9d5ce9dadf34 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs @@ -4,13 +4,9 @@ using System; using System.Buffers; using System.Diagnostics; -using System.Formats.Asn1; using System.Text; using System.Security.Cryptography; using System.Security.Cryptography.Apple; -using System.Security.Cryptography.Asn1; -using System.Security.Cryptography.Asn1.Pkcs12; -using System.Security.Cryptography.Asn1.Pkcs7; using System.Security.Cryptography.X509Certificates; namespace Internal.Cryptography.Pal @@ -123,37 +119,7 @@ private static AsymmetricAlgorithm DecodeRsaPublicKey(byte[] encodedKeyValue) private static AsymmetricAlgorithm DecodeDsaPublicKey(byte[] encodedKeyValue, byte[] encodedParameters) { - SubjectPublicKeyInfoAsn spki = new SubjectPublicKeyInfoAsn - { - Algorithm = new AlgorithmIdentifierAsn { Algorithm = Oids.Dsa, Parameters = encodedParameters }, - SubjectPublicKey = encodedKeyValue, - }; - - AsnWriter writer = new AsnWriter(AsnEncodingRules.DER); - spki.Encode(writer); - - byte[] rented = CryptoPool.Rent(writer.GetEncodedLength()); - - if (!writer.TryEncode(rented, out int written)) - { - Debug.Fail("TryEncode failed with a pre-allocated buffer"); - throw new InvalidOperationException(); - } - - DSA dsa = DSA.Create(); - IDisposable? toDispose = dsa; - - try - { - dsa.ImportSubjectPublicKeyInfo(rented.AsSpan(0, written), out _); - toDispose = null; - return dsa; - } - finally - { - toDispose?.Dispose(); - CryptoPool.Return(rented, written); - } + throw new PlatformNotSupportedException(); } public string X500DistinguishedNameDecode(byte[] encodedDistinguishedName, X500DistinguishedNameFlags flag) @@ -175,87 +141,6 @@ public string X500DistinguishedNameFormat(byte[] encodedDistinguishedName, bool multiLine); } - private static bool IsPkcs12(ReadOnlySpan rawData) - { - try - { - unsafe - { - fixed (byte* pin = rawData) - { - using (var manager = new PointerMemoryManager(pin, rawData.Length)) - { - PfxAsn.Decode(manager.Memory, AsnEncodingRules.BER); - } - - return true; - } - } - } - catch (CryptographicException) - { - } - - return false; - } - - private static bool IsPkcs7Signed(ReadOnlySpan rawData) - { - try - { - unsafe - { - fixed (byte* pin = rawData) - { - using (var manager = new PointerMemoryManager(pin, rawData.Length)) - { - AsnValueReader reader = new AsnValueReader(rawData, AsnEncodingRules.BER); - - ContentInfoAsn.Decode(ref reader, manager.Memory, out ContentInfoAsn contentInfo); - - switch (contentInfo.ContentType) - { - case Oids.Pkcs7Signed: - case Oids.Pkcs7SignedEnveloped: - return true; - } - } - } - } - } - catch (CryptographicException) - { - } - - return false; - } - - private X509ContentType GetDerCertContentType(ReadOnlySpan rawData) - { - const int errSecUnknownFormat = -25257; - - X509ContentType contentType = Interop.AppleCrypto.X509GetContentType(rawData); - - // Apple doesn't seem to recognize PFX files with no MAC, so try a quick maybe-it's-a-PFX test - if (contentType == X509ContentType.Unknown) - { - if (IsPkcs12(rawData)) - { - return X509ContentType.Pkcs12; - } - - if (IsPkcs7Signed(rawData)) - { - return X509ContentType.Pkcs7; - } - - // Throw to match Windows and Unix behavior. - throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnknownFormat); - } - - return contentType; - } - public X509ContentType GetCertContentType(ReadOnlySpan rawData) { const int errSecUnknownFormat = -25257; @@ -272,16 +157,22 @@ public X509ContentType GetCertContentType(ReadOnlySpan rawData) rawData, derData => { - result = GetDerCertContentType(derData); + result = AppleCertificatePal.GetDerCertContentType(derData); return false; }); - if (result != X509ContentType.Unknown) + if (result == X509ContentType.Unknown) + { + result = AppleCertificatePal.GetDerCertContentType(rawData); + } + + if (result == X509ContentType.Unknown) { - return result; + // Throw to match Windows and Unix behavior. + throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnknownFormat); } - return GetDerCertContentType(rawData); + return result; } public X509ContentType GetCertContentType(string fileName) From ba2010ae99881d31df18dd422318fdfe24853c68 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Mon, 3 May 2021 20:02:16 +0200 Subject: [PATCH 06/27] Fix accidental removal of temporary keychain tracking on macOS --- .../Interop.X509.cs | 48 ------------------- .../Interop.X509.iOS.cs | 39 +++++++++++++++ .../Interop.X509.macOS.cs | 44 +++++++++++++++++ 3 files changed, 83 insertions(+), 48 deletions(-) diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs index fa1357c8b9232..d50ca014c4e3a 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs @@ -69,35 +69,11 @@ internal static byte[] X509GetRawData(SafeSecCertificateHandle cert) throw new CryptographicException(); } - internal static SafeSecCertificateHandle X509GetCertFromIdentity(SafeSecIdentityHandle identity) - { - SafeSecCertificateHandle cert; - int osStatus = AppleCryptoNative_X509CopyCertFromIdentity(identity, out cert); - - //SafeTemporaryKeychainHandle.TrackItem(cert); - - if (osStatus != 0) - { - cert.Dispose(); - throw CreateExceptionForOSStatus(osStatus); - } - - if (cert.IsInvalid) - { - cert.Dispose(); - throw new CryptographicException(SR.Cryptography_OpenInvalidHandle); - } - - return cert; - } - internal static SafeSecKeyRefHandle X509GetPrivateKeyFromIdentity(SafeSecIdentityHandle identity) { SafeSecKeyRefHandle key; int osStatus = AppleCryptoNative_X509CopyPrivateKeyFromIdentity(identity, out key); - //SafeTemporaryKeychainHandle.TrackItem(key); - if (osStatus != 0) { key.Dispose(); @@ -119,8 +95,6 @@ internal static SafeSecKeyRefHandle X509GetPublicKey(SafeSecCertificateHandle ce int osStatus; int ret = AppleCryptoNative_X509GetPublicKey(cert, out publicKey, out osStatus); - //SafeTemporaryKeychainHandle.TrackItem(publicKey); - if (ret == 1) { return publicKey; @@ -136,27 +110,5 @@ internal static SafeSecKeyRefHandle X509GetPublicKey(SafeSecCertificateHandle ce Debug.Fail($"Unexpected return value {ret}"); throw new CryptographicException(); } - - internal static bool X509DemuxAndRetainHandle( - IntPtr handle, - out SafeSecCertificateHandle certHandle, - out SafeSecIdentityHandle identityHandle) - { - int result = AppleCryptoNative_X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle); - - //SafeTemporaryKeychainHandle.TrackItem(certHandle); - //SafeTemporaryKeychainHandle.TrackItem(identityHandle); - - switch (result) - { - case 1: - return true; - case 0: - return false; - default: - Debug.Fail($"AppleCryptoNative_X509DemuxAndRetainHandle returned {result}"); - throw new CryptographicException(); - } - } } } diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs index 54a8d35c7f824..e17be32baeb40 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs @@ -201,6 +201,45 @@ ref MemoryMarshal.GetReference(bytes), throw new CryptographicException(); } } + + internal static SafeSecCertificateHandle X509GetCertFromIdentity(SafeSecIdentityHandle identity) + { + SafeSecCertificateHandle cert; + int osStatus = AppleCryptoNative_X509CopyCertFromIdentity(identity, out cert); + + if (osStatus != 0) + { + cert.Dispose(); + throw CreateExceptionForOSStatus(osStatus); + } + + if (cert.IsInvalid) + { + cert.Dispose(); + throw new CryptographicException(SR.Cryptography_OpenInvalidHandle); + } + + return cert; + } + + internal static bool X509DemuxAndRetainHandle( + IntPtr handle, + out SafeSecCertificateHandle certHandle, + out SafeSecIdentityHandle identityHandle) + { + int result = AppleCryptoNative_X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle); + + switch (result) + { + case 1: + return true; + case 0: + return false; + default: + Debug.Fail($"AppleCryptoNative_X509DemuxAndRetainHandle returned {result}"); + throw new CryptographicException(); + } + } } } diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.macOS.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.macOS.cs index 848d67a30c72f..3caca43d8ece6 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.macOS.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.macOS.cs @@ -404,6 +404,50 @@ internal static byte[] X509ExportPfx(IntPtr[] certHandles, SafePasswordHandle ex } } } + + internal static SafeSecCertificateHandle X509GetCertFromIdentity(SafeSecIdentityHandle identity) + { + SafeSecCertificateHandle cert; + int osStatus = AppleCryptoNative_X509CopyCertFromIdentity(identity, out cert); + + SafeTemporaryKeychainHandle.TrackItem(cert); + + if (osStatus != 0) + { + cert.Dispose(); + throw CreateExceptionForOSStatus(osStatus); + } + + if (cert.IsInvalid) + { + cert.Dispose(); + throw new CryptographicException(SR.Cryptography_OpenInvalidHandle); + } + + return cert; + } + + internal static bool X509DemuxAndRetainHandle( + IntPtr handle, + out SafeSecCertificateHandle certHandle, + out SafeSecIdentityHandle identityHandle) + { + int result = AppleCryptoNative_X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle); + + SafeTemporaryKeychainHandle.TrackItem(certHandle); + SafeTemporaryKeychainHandle.TrackItem(identityHandle); + + switch (result) + { + case 1: + return true; + case 0: + return false; + default: + Debug.Fail($"AppleCryptoNative_X509DemuxAndRetainHandle returned {result}"); + throw new CryptographicException(); + } + } } } From c814a74584886f87125abfec781c2b7694b06fd2 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 4 May 2021 10:03:30 +0200 Subject: [PATCH 07/27] Unify some platform support macros --- .../Security/Cryptography/PlatformSupport.cs | 7 +++- .../TestUtilities/System/PlatformDetection.cs | 2 + .../tests/ChainTests.cs | 10 ++--- .../tests/DynamicChainTests.cs | 12 +++--- .../RevocationTests/DynamicRevocationTests.cs | 39 ++++++++++--------- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/PlatformSupport.cs b/src/libraries/Common/tests/System/Security/Cryptography/PlatformSupport.cs index b46fe55b0cafa..2885d74cd639a 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/PlatformSupport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/PlatformSupport.cs @@ -8,8 +8,11 @@ namespace Test.Cryptography { internal static class PlatformSupport { - // Platforms that support OpenSSL - all Unix except OSX and Android - internal const TestPlatforms OpenSSL = TestPlatforms.AnyUnix & ~(TestPlatforms.OSX | TestPlatforms.Android); + // Platforms that use Apple Cryptography + internal const TestPlatforms AppleCrypto = TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst; + + // Platforms that support OpenSSL - all Unix except OSX/iOS/tvOS/MacCatalyst and Android + internal const TestPlatforms OpenSSL = TestPlatforms.AnyUnix & ~(AppleCrypto | TestPlatforms.Android); // Whether or not the current platform supports RC2 internal static readonly bool IsRC2Supported = !PlatformDetection.IsAndroid; diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index d5d5817907197..a3016b422813a 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -137,6 +137,8 @@ public static bool IsNonZeroLowerBoundArraySupported public static bool IsOpenSslSupported => IsLinux || IsFreeBSD || Isillumos || IsSolaris; + public static bool UsesAppleCrypto => IsOSX || IsMacCatalyst || IsiOS || IstvOS; + // Changed to `true` when linking public static bool IsBuiltWithAggressiveTrimming => false; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs index be9a014d08a94..52fa5ffce847f 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs @@ -13,8 +13,6 @@ namespace System.Security.Cryptography.X509Certificates.Tests { public static class ChainTests { - private static bool IsApple { get; } = OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsMacCatalyst(); - private static bool TrustsMicrosoftDotComRoot { get @@ -272,7 +270,7 @@ public static void SystemTrustCertificateWithCustomRootTrust(bool addCertificate Assert.False(chain.Build(microsoftDotCom)); // Linux and Windows do not search the default system root stores when CustomRootTrust is enabled - if (IsApple) + if (PlatformDetection.UsesAppleCrypto) { Assert.Equal(3, chain.ChainElements.Count); Assert.Equal(X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags()); @@ -771,7 +769,7 @@ public static void InvalidSelfSignedSignature() { expectedFlags = X509ChainStatusFlags.NotSignatureValid; } - else if (IsApple) + else if (PlatformDetection.UsesAppleCrypto) { // For OSX alone expectedFlags here means OR instead of AND. // Because the error code changed in 10.13.4 from UntrustedRoot to PartialChain @@ -809,7 +807,7 @@ public static void InvalidSelfSignedSignature() X509ChainStatusFlags allFlags = chain.AllStatusFlags(); - if (IsApple) + if (PlatformDetection.UsesAppleCrypto) { // If we're on 10.13.3 or older we get UntrustedRoot. // If we're on 10.13.4 or newer we get PartialChain. @@ -960,7 +958,7 @@ void CheckChain() bool valid = chain.Build(cert); X509ChainStatusFlags allFlags = chain.AllStatusFlags(); - if (IsApple) + if (PlatformDetection.UsesAppleCrypto) { // OSX considers this to be valid because it doesn't report NotSignatureValid, // just PartialChain ("I couldn't find an issuer that made the signature work"), diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs index 466390092bd25..ceeddacd28c79 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs @@ -12,8 +12,6 @@ namespace System.Security.Cryptography.X509Certificates.Tests { public static class DynamicChainTests { - private static bool IsApple { get; } = OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsMacCatalyst(); - private static X509Extension BasicConstraintsCA => new X509BasicConstraintsExtension( certificateAuthority: true, hasPathLengthConstraint: false, @@ -115,7 +113,7 @@ DateTime RewindIfNeeded(DateTime input, X509Certificate2 cert, X509ChainStatusFl intermediateCert = TamperIfNeeded(intermediateCert, intermediateErrors); rootCert = TamperIfNeeded(rootCert, rootErrors); - if (IsApple) + if (PlatformDetection.UsesAppleCrypto) { // For the lower levels, turn NotSignatureValid into PartialChain, // and clear all errors at higher levels. @@ -140,7 +138,7 @@ DateTime RewindIfNeeded(DateTime input, X509Certificate2 cert, X509ChainStatusFl rootErrors &= ~X509ChainStatusFlags.NotSignatureValid; // On 10.13+ it becomes PartialChain, and UntrustedRoot goes away. - if (IsApple) + if (PlatformDetection.UsesAppleCrypto) { rootErrors &= ~X509ChainStatusFlags.UntrustedRoot; rootErrors |= X509ChainStatusFlags.PartialChain; @@ -561,7 +559,7 @@ public static void NameConstraintViolation_ExcludedTree_Dns() } [Fact] - [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "macOS appears to just completely ignore min/max.")] + [SkipOnPlatform(PlatformSupport.AppleCrypto, "macOS appears to just completely ignore min/max.")] public static void NameConstraintViolation_PermittedTree_HasMin() { SubjectAlternativeNameBuilder builder = new SubjectAlternativeNameBuilder(); @@ -846,7 +844,7 @@ private static X509ChainStatusFlags PlatformBasicConstraints(X509ChainStatusFlag private static X509ChainStatusFlags PlatformNameConstraints(X509ChainStatusFlags flags) { - if (IsApple) + if (PlatformDetection.UsesAppleCrypto) { const X509ChainStatusFlags AnyNameConstraintFlags = X509ChainStatusFlags.HasExcludedNameConstraint | @@ -873,7 +871,7 @@ private static X509ChainStatusFlags PlatformNameConstraints(X509ChainStatusFlags private static X509ChainStatusFlags PlatformPolicyConstraints(X509ChainStatusFlags flags) { - if (IsApple) + if (PlatformDetection.UsesAppleCrypto) { const X509ChainStatusFlags AnyPolicyConstraintFlags = X509ChainStatusFlags.NoIssuanceChainPolicy; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs index 7bf6fce647409..5fc168b03e4a5 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Security.Cryptography.X509Certificates.Tests.Common; +using Test.Cryptography; using Xunit; namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests @@ -122,7 +123,7 @@ public static void NothingRevoked(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeIntermediate(PkiOptions pkiOptions) { SimpleTest( @@ -191,7 +192,7 @@ public static void RevokeLeafWithAiaFetchingDisabled(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeIntermediateAndEndEntity(PkiOptions pkiOptions) { SimpleTest( @@ -220,7 +221,7 @@ public static void RevokeIntermediateAndEndEntity(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeRoot(PkiOptions pkiOptions) { SimpleTest( @@ -257,7 +258,7 @@ public static void RevokeRoot(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeRootAndEndEntity(PkiOptions pkiOptions) { SimpleTest( @@ -292,7 +293,7 @@ public static void RevokeRootAndEndEntity(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeRootAndIntermediate(PkiOptions pkiOptions) { SimpleTest( @@ -328,7 +329,7 @@ public static void RevokeRootAndIntermediate(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeEverything(PkiOptions pkiOptions) { SimpleTest( @@ -451,7 +452,7 @@ public static void RevokeEndEntity_IssuerUnrelatedOcsp(PkiOptions pkiOptions) [Theory] [InlineData(PkiOptions.OcspEverywhere)] [InlineData(PkiOptions.IssuerRevocationViaOcsp | PkiOptions.AllEndEntityRevocation)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeEndEntity_RootUnrelatedOcsp(PkiOptions pkiOptions) { SimpleTest( @@ -554,7 +555,7 @@ public static IEnumerable PolicyErrorsNotTimeValidData } [Theory] [MemberData(nameof(PolicyErrorsNotTimeValidData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeIntermediate_PolicyErrors_NotTimeValid(bool policyErrors, bool notTimeValid) { SimpleTest( @@ -639,7 +640,7 @@ public static void RevokeIntermediate_PolicyErrors_NotTimeValid(bool policyError [Theory] [MemberData(nameof(PolicyErrorsNotTimeValidData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeEndEntity_PolicyErrors_NotTimeValid(bool policyErrors, bool notTimeValid) { SimpleTest( @@ -721,7 +722,7 @@ public static void RevokeEndEntity_PolicyErrors_NotTimeValid(bool policyErrors, [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeEndEntity_RootRevocationOffline(PkiOptions pkiOptions) { BuildPrivatePki( @@ -798,7 +799,7 @@ public static void RevokeEndEntity_RootRevocationOffline(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void NothingRevoked_RootRevocationOffline(PkiOptions pkiOptions) { BuildPrivatePki( @@ -888,7 +889,7 @@ public static void RevokeEndEntityWithInvalidRevocationSignature(PkiOptions pkiO [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeIntermediateWithInvalidRevocationSignature(PkiOptions pkiOptions) { SimpleTest( @@ -917,7 +918,7 @@ public static void RevokeEndEntityWithInvalidRevocationName(PkiOptions pkiOption [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeIntermediateWithInvalidRevocationName(PkiOptions pkiOptions) { SimpleTest( @@ -968,7 +969,7 @@ public static void RevokeEndEntityWithExpiredRevocation(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void RevokeIntermediateWithExpiredRevocation(PkiOptions pkiOptions) { SimpleTest( @@ -1035,7 +1036,7 @@ public static void CheckEndEntityWithExpiredRevocation(PkiOptions pkiOptions) [Theory] [MemberData(nameof(AllViableRevocation))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void CheckIntermediateWithExpiredRevocation(PkiOptions pkiOptions) { bool usingCrl = pkiOptions.HasFlag(PkiOptions.IssuerRevocationViaCrl) || pkiOptions.HasFlag(PkiOptions.EndEntityRevocationViaCrl); @@ -1061,7 +1062,7 @@ public static void CheckIntermediateWithExpiredRevocation(PkiOptions pkiOptions) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void TestRevocationWithNoNextUpdate_NotRevoked() { SimpleTest( @@ -1098,7 +1099,7 @@ public static void TestRevocationWithNoNextUpdate_NotRevoked() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", PlatformSupport.AppleCrypto)] public static void TestRevocationWithNoNextUpdate_Revoked() { SimpleTest( @@ -1134,7 +1135,7 @@ public static void TestRevocationWithNoNextUpdate_Revoked() } [Fact] - [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Android and macOS do not support offline revocation chain building.")] + [SkipOnPlatform(TestPlatforms.Android | PlatformSupport.AppleCrypto, "Android and macOS do not support offline revocation chain building.")] public static void TestRevocation_Offline_NotRevoked() { SimpleTest( @@ -1178,7 +1179,7 @@ public static void TestRevocation_Offline_NotRevoked() } [Fact] - [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Android and macOS do not support offline revocation chain building.")] + [SkipOnPlatform(TestPlatforms.Android | PlatformSupport.AppleCrypto, "Android and macOS do not support offline revocation chain building.")] public static void TestRevocation_Offline_Revoked() { SimpleTest( From c0ed20461a3bb9949d2f280edc35db119a8d00d9 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 4 May 2021 10:10:19 +0200 Subject: [PATCH 08/27] One more test cleanup --- .../tests/CtorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs index a515d76d53aee..4da9a049088b6 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs @@ -359,7 +359,7 @@ public static void InvalidCertificateBlob() // TODO (3233): Test that Message is also set correctly //Assert.Equal("Cannot find the requested object.", ex.Message); } - else if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsMacCatalyst()) + else if (PlatformDetection.UsesAppleCrypto) { Assert.Equal(-25257, ex.HResult); } From f0bbee95906937316738294dd697d12d69b53a13 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 4 May 2021 10:17:20 +0200 Subject: [PATCH 09/27] Fix couple of leaks in native code and handle allocation failures --- .../pal_keychain_ios.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c index 399ca3d3e9f3e..483bffe41f2b3 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c @@ -100,6 +100,11 @@ AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t i kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (query == NULL) + { + return errSecAllocate; + } + if (!isReadOnlyMode) { CFTypeID inputType = CFGetTypeID(certOrIdentity); @@ -140,6 +145,12 @@ AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t i kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (query == NULL) + { + CFRelease(publicKeyLabel); + return errSecAllocate; + } + result = NULL; keyStatus = SecItemCopyMatching(query, &result); @@ -158,10 +169,18 @@ AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t i kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (query == NULL) + { + CFRelease(publicKeyLabel); + return errSecAllocate; + } + SecItemDelete(query); CFRelease(query); } + + CFRelease(publicKeyLabel); } } else From eb828221ec360e80d1d57700f160fa9ee848a36c Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 4 May 2021 10:53:47 +0200 Subject: [PATCH 10/27] More interop cleanup --- .../Interop.X509.iOS.cs | 63 ++-------- .../pal_x509_ios.c | 108 ++++++++++-------- .../pal_x509_ios.h | 13 +-- 3 files changed, 77 insertions(+), 107 deletions(-) diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs index e17be32baeb40..71f388cc3ecb4 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.iOS.cs @@ -22,8 +22,7 @@ private static int AppleCryptoNative_X509ImportCertificate( X509ContentType contentType, SafeCreateHandle cfPfxPassphrase, out SafeSecCertificateHandle pCertOut, - out SafeSecIdentityHandle pPrivateKeyOut, - out int pOSStatus) + out SafeSecIdentityHandle pPrivateKeyOut) { return AppleCryptoNative_X509ImportCertificate( ref MemoryMarshal.GetReference(keyBlob), @@ -31,8 +30,7 @@ ref MemoryMarshal.GetReference(keyBlob), contentType, cfPfxPassphrase, out pCertOut, - out pPrivateKeyOut, - out pOSStatus); + out pPrivateKeyOut); } [DllImport(Libraries.AppleCryptoNative)] @@ -42,8 +40,7 @@ private static extern int AppleCryptoNative_X509ImportCertificate( X509ContentType contentType, SafeCreateHandle cfPfxPassphrase, out SafeSecCertificateHandle pCertOut, - out SafeSecIdentityHandle pPrivateKeyOut, - out int pOSStatus); + out SafeSecIdentityHandle pPrivateKeyOut); [DllImport(Libraries.AppleCryptoNative)] private static extern int AppleCryptoNative_X509ImportCollection( @@ -51,8 +48,7 @@ private static extern int AppleCryptoNative_X509ImportCollection( int cbKeyBlob, X509ContentType contentType, SafeCreateHandle cfPfxPassphrase, - out SafeCFArrayHandle pCollectionOut, - out int pOSStatus); + out SafeCFArrayHandle pCollectionOut); internal static SafeSecCertificateHandle X509ImportCertificate( ReadOnlySpan bytes, @@ -95,19 +91,16 @@ private static SafeSecCertificateHandle X509ImportCertificate( out SafeSecIdentityHandle identityHandle) { SafeSecCertificateHandle certHandle; - int osStatus; - SafeCreateHandle cfPassphrase = importPassword ?? s_emptyExportString; - int ret = AppleCryptoNative_X509ImportCertificate( + int osStatus = AppleCryptoNative_X509ImportCertificate( bytes, contentType, cfPassphrase, out certHandle, - out identityHandle, - out osStatus); + out identityHandle); - if (ret == 1) + if (osStatus == 0) { return certHandle; } @@ -115,21 +108,7 @@ private static SafeSecCertificateHandle X509ImportCertificate( certHandle.Dispose(); identityHandle.Dispose(); - const int SeeOSStatus = 0; - const int ImportReturnedEmpty = -2; - const int ImportReturnedNull = -3; - - switch (ret) - { - case SeeOSStatus: - throw CreateExceptionForOSStatus(osStatus); - case ImportReturnedNull: - case ImportReturnedEmpty: - throw new CryptographicException(); - default: - Debug.Fail($"Unexpected return value {ret}"); - throw new CryptographicException(); - } + throw CreateExceptionForOSStatus(osStatus); } internal static SafeCFArrayHandle X509ImportCollection( @@ -139,8 +118,6 @@ internal static SafeCFArrayHandle X509ImportCollection( { SafeCreateHandle cfPassphrase = s_emptyExportString; bool releasePassword = false; - - int ret; SafeCFArrayHandle collectionHandle; int osStatus; @@ -157,15 +134,14 @@ internal static SafeCFArrayHandle X509ImportCollection( } } - ret = AppleCryptoNative_X509ImportCollection( + osStatus = AppleCryptoNative_X509ImportCollection( ref MemoryMarshal.GetReference(bytes), bytes.Length, contentType, cfPassphrase, - out collectionHandle, - out osStatus); + out collectionHandle); - if (ret == 1) + if (osStatus == 0) { return collectionHandle; } @@ -184,22 +160,7 @@ ref MemoryMarshal.GetReference(bytes), } collectionHandle.Dispose(); - - const int SeeOSStatus = 0; - const int ImportReturnedEmpty = -2; - const int ImportReturnedNull = -3; - - switch (ret) - { - case SeeOSStatus: - throw CreateExceptionForOSStatus(osStatus); - case ImportReturnedNull: - case ImportReturnedEmpty: - throw new CryptographicException(); - default: - Debug.Fail($"Unexpected return value {ret}"); - throw new CryptographicException(); - } + throw CreateExceptionForOSStatus(osStatus); } internal static SafeSecCertificateHandle X509GetCertFromIdentity(SafeSecIdentityHandle identity) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c index 194aab74b34aa..bda91d9239a8e 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c @@ -12,44 +12,57 @@ int32_t AppleCryptoNative_X509ImportCertificate(uint8_t* pbData, PAL_X509ContentType contentType, CFStringRef cfPfxPassphrase, SecCertificateRef* pCertOut, - SecIdentityRef* pIdentityOut, - int32_t* pOSStatus) + SecIdentityRef* pIdentityOut) { + OSStatus status; + assert(pCertOut != NULL); assert(pIdentityOut != NULL); - assert(pOSStatus != NULL); assert(pbData != NULL); assert(cbData >= 0); *pCertOut = NULL; *pIdentityOut = NULL; - *pOSStatus = noErr; + + if (contentType != PAL_Certificate && contentType != PAL_Pkcs12) + { + return errSecUnknownFormat; + } CFDataRef cfData = CFDataCreateWithBytesNoCopy(NULL, pbData, cbData, kCFAllocatorNull); if (cfData == NULL) { - *pOSStatus = errSecAllocate; - return 0; + return errSecAllocate; } if (contentType == PAL_Certificate) { *pCertOut = SecCertificateCreateWithData(NULL, cfData); CFRelease(cfData); - *pOSStatus = *pCertOut == NULL ? errSecUnknownFormat : 0; - return *pCertOut != NULL; + return *pCertOut == NULL ? errSecUnknownFormat : noErr; } - else if (contentType == PAL_Pkcs12) + else // PAL_Pkcs12 { + const void *keys[] = { kSecImportExportPassphrase }; + const void *values[] = { cfPfxPassphrase }; + CFDictionaryRef attrs = CFDictionaryCreate( + kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + if (attrs == NULL) + { + CFRelease(cfData); + return errSecAllocate; + } + CFArrayRef p12Items = NULL; - CFMutableDictionaryRef attrs = CFDictionaryCreateMutable( - kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(attrs, kSecImportExportPassphrase, cfPfxPassphrase); - *pOSStatus = SecPKCS12Import(cfData, attrs, &p12Items); + status = SecPKCS12Import(cfData, attrs, &p12Items); + CFRelease(cfData); CFRelease(attrs); - if (*pOSStatus == noErr) + + if (status == noErr) { if (CFArrayGetCount(p12Items) > 0) { @@ -58,104 +71,105 @@ int32_t AppleCryptoNative_X509ImportCertificate(uint8_t* pbData, } CFRelease(p12Items); } - return *pIdentityOut != NULL; - } - CFRelease(cfData); - *pOSStatus = errSecUnknownFormat; - return 0; + return status; + } } - int32_t AppleCryptoNative_X509ImportCollection(uint8_t* pbData, int32_t cbData, PAL_X509ContentType contentType, CFStringRef cfPfxPassphrase, - CFArrayRef* pCollectionOut, - int32_t* pOSStatus) + CFArrayRef* pCollectionOut) { + OSStatus status; CFMutableArrayRef outItems; assert(pCollectionOut != NULL); - assert(pOSStatus != NULL); assert(pbData != NULL); assert(cbData >= 0); *pCollectionOut = NULL; - *pOSStatus = noErr; + + if (contentType != PAL_Certificate && contentType != PAL_Pkcs12) + { + return errSecUnknownFormat; + } CFDataRef cfData = CFDataCreateWithBytesNoCopy(NULL, pbData, cbData, kCFAllocatorNull); if (cfData == NULL) { - *pOSStatus = errSecAllocate; - return 0; + return errSecAllocate; } if (contentType == PAL_Certificate) { SecCertificateRef certificate = SecCertificateCreateWithData(NULL, cfData); + CFRelease(cfData); if (certificate != NULL) - { + { outItems = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); if (outItems == NULL) { CFRelease(certificate); - *pOSStatus = errSecAllocate; - return 0; + return errSecAllocate; } CFArrayAppendValue(outItems, certificate); *pCollectionOut = outItems; - return 1; + return noErr; } else { - *pOSStatus = errSecUnknownFormat; - return 0; + return errSecUnknownFormat; } } - else if (contentType == PAL_Pkcs12) + else // PAL_Pkcs12 { + const void *keys[] = { kSecImportExportPassphrase }; + const void *values[] = { cfPfxPassphrase }; + CFDictionaryRef attrs = CFDictionaryCreate( + kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + if (attrs == NULL) + { + CFRelease(cfData); + return errSecAllocate; + } + CFArrayRef p12Items = NULL; - CFMutableDictionaryRef attrs = CFDictionaryCreateMutable( - kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(attrs, kSecImportExportPassphrase, cfPfxPassphrase); - *pOSStatus = SecPKCS12Import(cfData, attrs, &p12Items); + status = SecPKCS12Import(cfData, attrs, &p12Items); + CFRelease(cfData); CFRelease(attrs); - if (*pOSStatus == noErr) + if (status == noErr) { outItems = CFArrayCreateMutable(NULL, CFArrayGetCount(p12Items), &kCFTypeArrayCallBacks); if (outItems == NULL) { CFRelease(p12Items); - *pOSStatus = errSecAllocate; - return 0; + return errSecAllocate; } for (int i = 0; i < CFArrayGetCount(p12Items); i++) { CFDictionaryRef item_dict = CFArrayGetValueAtIndex(p12Items, i); SecIdentityRef identity = (SecIdentityRef)CFRetain(CFDictionaryGetValue(item_dict, kSecImportItemIdentity)); + assert(identity != NULL); CFArrayAppendValue(outItems, identity); } CFRelease(p12Items); *pCollectionOut = outItems; - - return 1; } - return 0; + return status; } - - CFRelease(cfData); - *pOSStatus = errSecUnknownFormat; - return 0; } \ No newline at end of file diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h index 8b1fc59db34ec..6f511573fccdd 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h @@ -20,21 +20,18 @@ For a PKCS#12 blob (PFX) the first public+private pair found is returned, or the If cfPfxPassphrase represents the NULL (but not empty) passphrase and a PFX import gets a password error then the empty passphrase is automatically attempted. -Returns 1 on success, 0 on failure, -2 on a successful read of an empty collection, other values reprepresent invalid -state. +Returns the last OSStatus value. Output: pCertOut: If the best matched value was a certificate, receives the SecCertificateRef, otherwise receives NULL pIdentityOut: If the best matched value was an identity, receives the SecIdentityRef, otherwise receives NULL -pOSStatus: Receives the return of the last call to SecItemImport */ PALEXPORT int32_t AppleCryptoNative_X509ImportCertificate(uint8_t* pbData, int32_t cbData, PAL_X509ContentType contentType, CFStringRef cfPfxPassphrase, SecCertificateRef* pCertOut, - SecIdentityRef* pIdentityOut, - int32_t* pOSStatus); + SecIdentityRef* pIdentityOut); /* Read cbData bytes of data from pbData and interpret it to a collection of certificates (or identities). @@ -42,16 +39,14 @@ Read cbData bytes of data from pbData and interpret it to a collection of certif If cfPfxPassphrase represents the NULL (but not empty) passphrase and a PFX import gets a password error then the empty passphrase is automatically attempted. -Returns 1 on success (including empty PKCS7 collections), 0 on failure, other values indicate invalid state. +Returns the last OSStatus value. Output: pCollectionOut: Receives an array which contains SecCertificateRef, SecIdentityRef, and possibly other values which were read out of the provided blob -pOSStatus: Receives the output of SecItemImport for the last attempted read */ PALEXPORT int32_t AppleCryptoNative_X509ImportCollection(uint8_t* pbData, int32_t cbData, PAL_X509ContentType contentType, CFStringRef cfPfxPassphrase, - CFArrayRef* pCollectionOut, - int32_t* pOSStatus); + CFArrayRef* pCollectionOut); From 48fe4e789ab3c8392de6d14637912b74aad939f5 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 4 May 2021 10:57:21 +0200 Subject: [PATCH 11/27] clang-format --- .../pal_keychain_ios.c | 71 +++++++++++-------- .../pal_keychain_ios.h | 11 ++- .../pal_x509_ios.c | 31 ++++---- .../pal_x509_ios.h | 2 +- 4 files changed, 66 insertions(+), 49 deletions(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c index 483bffe41f2b3..588154eb431e6 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.c @@ -5,19 +5,21 @@ #include "pal_utilities.h" #include "pal_x509.h" -static int32_t -EnumerateKeychain(CFStringRef matchType, CFArrayRef* pCertsOut) +static int32_t EnumerateKeychain(CFStringRef matchType, CFArrayRef* pCertsOut) { assert(pCertsOut != NULL); assert(matchType != NULL); *pCertsOut = NULL; - const void *keys[] = { kSecReturnRef, kSecMatchLimit, kSecClass }; - const void *values[] = { kCFBooleanTrue, kSecMatchLimitAll, matchType }; - CFDictionaryRef query = CFDictionaryCreate( - kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + const void* keys[] = {kSecReturnRef, kSecMatchLimit, kSecClass}; + const void* values[] = {kCFBooleanTrue, kSecMatchLimitAll, matchType}; + CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, + keys, + values, + sizeof(keys) / sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); if (query == NULL) { @@ -70,11 +72,14 @@ int32_t AppleCryptoNative_X509StoreAddCertificate(CFTypeRef certOrIdentity) assert(certOrIdentity != NULL); - const void *keys[] = { kSecValueRef }; - const void *values[] = { certOrIdentity }; - CFDictionaryRef query = CFDictionaryCreate( - kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + const void* keys[] = {kSecValueRef}; + const void* values[] = {certOrIdentity}; + CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, + keys, + values, + sizeof(keys) / sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); if (query == NULL) { @@ -86,19 +91,21 @@ int32_t AppleCryptoNative_X509StoreAddCertificate(CFTypeRef certOrIdentity) return status == errSecDuplicateItem ? noErr : status; } -int32_t -AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t isReadOnlyMode) +int32_t AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t isReadOnlyMode) { OSStatus status; CFTypeRef result = NULL; assert(certOrIdentity != NULL); - const void *keys[] = { kSecValueRef }; - const void *values[] = { certOrIdentity }; - CFDictionaryRef query = CFDictionaryCreate( - kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + const void* keys[] = {kSecValueRef}; + const void* values[] = {certOrIdentity}; + CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, + keys, + values, + sizeof(keys) / sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); if (query == NULL) { @@ -139,11 +146,14 @@ AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t i { OSStatus keyStatus; - const void *keys[] = { kSecClass, kSecAttrPublicKeyHash }; - const void *values[] = { kSecClassCertificate, publicKeyLabel }; - query = CFDictionaryCreate( - kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + const void* keys[] = {kSecClass, kSecAttrPublicKeyHash}; + const void* values[] = {kSecClassCertificate, publicKeyLabel}; + query = CFDictionaryCreate(kCFAllocatorDefault, + keys, + values, + sizeof(keys) / sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); if (query == NULL) { @@ -163,11 +173,14 @@ AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t i if (keyStatus == errSecItemNotFound) { - const void *keys[] = { kSecClass, kSecAttrApplicationLabel }; - const void *values[] = { kSecClassKey, publicKeyLabel }; - query = CFDictionaryCreate( - kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + const void* keys[] = {kSecClass, kSecAttrApplicationLabel}; + const void* values[] = {kSecClassKey, publicKeyLabel}; + query = CFDictionaryCreate(kCFAllocatorDefault, + keys, + values, + sizeof(keys) / sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); if (query == NULL) { diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.h index 910fee2553a2d..a788853fbfe04 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain_ios.h @@ -3,8 +3,8 @@ #pragma once -#include "pal_types.h" #include "pal_compiler.h" +#include "pal_types.h" #include @@ -17,8 +17,7 @@ Returns the last OSStatus value (noErr on success). pCertsOut: When the return value is not noErr, NULL. Otherwise NULL on "no certs found", or a CFArrayRef for the matches (including a single match). */ -PALEXPORT int32_t -AppleCryptoNative_SecKeychainEnumerateCerts(CFArrayRef* pCertsOut); +PALEXPORT int32_t AppleCryptoNative_SecKeychainEnumerateCerts(CFArrayRef* pCertsOut); /* Enumerate the certificate objects within the given keychain. @@ -39,13 +38,11 @@ Add a certificate from the specified keychain. Returns the last OSStatus value (noErr on success). */ -PALEXPORT int32_t -AppleCryptoNative_X509StoreAddCertificate(CFTypeRef certOrIdentity); +PALEXPORT int32_t AppleCryptoNative_X509StoreAddCertificate(CFTypeRef certOrIdentity); /* Remove a certificate from the specified keychain. Returns the last OSStatus value (noErr on success). */ -PALEXPORT int32_t -AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t isReadOnlyMode); +PALEXPORT int32_t AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, uint8_t isReadOnlyMode); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c index bda91d9239a8e..8ffa1751e1325 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.c @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#include "pal_x509.h" #include "pal_x509_ios.h" #include "pal_utilities.h" +#include "pal_x509.h" #include #include @@ -44,11 +44,14 @@ int32_t AppleCryptoNative_X509ImportCertificate(uint8_t* pbData, } else // PAL_Pkcs12 { - const void *keys[] = { kSecImportExportPassphrase }; - const void *values[] = { cfPfxPassphrase }; - CFDictionaryRef attrs = CFDictionaryCreate( - kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + const void* keys[] = {kSecImportExportPassphrase}; + const void* values[] = {cfPfxPassphrase}; + CFDictionaryRef attrs = CFDictionaryCreate(kCFAllocatorDefault, + keys, + values, + sizeof(keys) / sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); if (attrs == NULL) { @@ -130,11 +133,14 @@ int32_t AppleCryptoNative_X509ImportCollection(uint8_t* pbData, } else // PAL_Pkcs12 { - const void *keys[] = { kSecImportExportPassphrase }; - const void *values[] = { cfPfxPassphrase }; - CFDictionaryRef attrs = CFDictionaryCreate( - kCFAllocatorDefault, keys, values, sizeof(keys)/sizeof(*keys), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + const void* keys[] = {kSecImportExportPassphrase}; + const void* values[] = {cfPfxPassphrase}; + CFDictionaryRef attrs = CFDictionaryCreate(kCFAllocatorDefault, + keys, + values, + sizeof(keys) / sizeof(*keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); if (attrs == NULL) { @@ -161,7 +167,8 @@ int32_t AppleCryptoNative_X509ImportCollection(uint8_t* pbData, for (int i = 0; i < CFArrayGetCount(p12Items); i++) { CFDictionaryRef item_dict = CFArrayGetValueAtIndex(p12Items, i); - SecIdentityRef identity = (SecIdentityRef)CFRetain(CFDictionaryGetValue(item_dict, kSecImportItemIdentity)); + SecIdentityRef identity = + (SecIdentityRef)CFRetain(CFDictionaryGetValue(item_dict, kSecImportItemIdentity)); assert(identity != NULL); CFArrayAppendValue(outItems, identity); } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h index 6f511573fccdd..f2822ddb8278c 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509_ios.h @@ -3,9 +3,9 @@ #pragma once +#include "pal_compiler.h" #include "pal_digest.h" #include "pal_seckey.h" -#include "pal_compiler.h" #include #include From 7d315e0988ade91ae34d23818aa890dffa6b917c Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 4 May 2021 13:48:14 +0200 Subject: [PATCH 12/27] Remove dead code --- .../Pal.iOS/StorePal.TrustedStore.cs | 43 ------------- .../Internal/Cryptography/Pal.iOS/X509Pal.cs | 61 +++++-------------- ...urity.Cryptography.X509Certificates.csproj | 1 - 3 files changed, 16 insertions(+), 89 deletions(-) delete mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.TrustedStore.cs diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.TrustedStore.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.TrustedStore.cs deleted file mode 100644 index a4debf88bb62a..0000000000000 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.TrustedStore.cs +++ /dev/null @@ -1,43 +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; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using Microsoft.Win32.SafeHandles; - -namespace Internal.Cryptography.Pal -{ - internal sealed partial class StorePal - { - private sealed class TrustedStore : IStorePal - { - internal TrustedStore() - { - } - - public SafeHandle? SafeHandle => null; - - public void Dispose() - { - } - - public void Add(ICertificatePal cert) - { - throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); - } - - public void Remove(ICertificatePal cert) - { - throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); - } - - public void CloneTo(X509Certificate2Collection collection) - { - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs index f9d5ce9dadf34..f7b97f0646812 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs @@ -34,40 +34,31 @@ public ECDiffieHellman DecodeECDiffieHellmanPublicKey(ICertificatePal? certifica public AsymmetricAlgorithm DecodePublicKey(Oid oid, byte[] encodedKeyValue, byte[] encodedParameters, ICertificatePal? certificatePal) { - AppleCertificatePal? applePal = certificatePal as AppleCertificatePal; + if (oid.Value != Oids.Rsa) + { + throw new NotSupportedException(SR.NotSupported_KeyAlgorithm); + } - if (applePal != null) + if (certificatePal is AppleCertificatePal applePal) { SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPublicKey(applePal.CertificateHandle); - - switch (oid.Value) - { - case Oids.Rsa: - Debug.Assert(!key.IsInvalid); - return new RSAImplementation.RSASecurityTransforms(key); - case Oids.Dsa: - if (key.IsInvalid) - { - // SecCertificateCopyKey returns null for DSA, so fall back to manually building it. - return DecodeDsaPublicKey(encodedKeyValue, encodedParameters); - } - return new DSAImplementation.DSASecurityTransforms(key); - } - - key.Dispose(); + Debug.Assert(!key.IsInvalid); + return new RSAImplementation.RSASecurityTransforms(key); } else { - switch (oid.Value) + RSA rsa = RSA.Create(); + try { - case Oids.Rsa: - return DecodeRsaPublicKey(encodedKeyValue); - case Oids.Dsa: - return DecodeDsaPublicKey(encodedKeyValue, encodedParameters); + rsa.ImportRSAPublicKey(new ReadOnlySpan(encodedKeyValue), out _); + return rsa; + } + catch (Exception) + { + rsa.Dispose(); + throw; } } - - throw new NotSupportedException(SR.NotSupported_KeyAlgorithm); } private static SafeSecKeyRefHandle DecodeECPublicKey(ICertificatePal? certificatePal) @@ -102,26 +93,6 @@ private static SafeSecKeyRefHandle DecodeECPublicKey(ICertificatePal? certificat return key; } - private static AsymmetricAlgorithm DecodeRsaPublicKey(byte[] encodedKeyValue) - { - RSA rsa = RSA.Create(); - try - { - rsa.ImportRSAPublicKey(new ReadOnlySpan(encodedKeyValue), out _); - return rsa; - } - catch (Exception) - { - rsa.Dispose(); - throw; - } - } - - private static AsymmetricAlgorithm DecodeDsaPublicKey(byte[] encodedKeyValue, byte[] encodedParameters) - { - throw new PlatformNotSupportedException(); - } - public string X500DistinguishedNameDecode(byte[] encodedDistinguishedName, X500DistinguishedNameFlags flag) { return X500NameEncoder.X500DistinguishedNameDecode(encodedDistinguishedName, true, flag); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 407d5e1860c90..2d725fdf254bf 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -621,7 +621,6 @@ - From cd1b4f886fd1afe1a64c53cc2bafbaca00cd3af8 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Thu, 6 May 2021 22:34:36 +0200 Subject: [PATCH 13/27] Extract common code from AppleCertificatePal --- .../Pal.OSX/AppleCertificatePal.Common.cs | 406 ++++++++++++++++++ .../Pal.OSX/AppleCertificatePal.cs | 383 +---------------- .../Pal.iOS/AppleCertificatePal.cs | 386 +---------------- .../Cryptography/Pal.iOS/CertificatePal.cs | 45 -- ...urity.Cryptography.X509Certificates.csproj | 2 + 5 files changed, 412 insertions(+), 810 deletions(-) create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Common.cs delete mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/CertificatePal.cs diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Common.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Common.cs new file mode 100644 index 0000000000000..cb949b8a93f7a --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Common.cs @@ -0,0 +1,406 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Formats.Asn1; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class AppleCertificatePal : ICertificatePal + { + private SafeSecIdentityHandle? _identityHandle; + private SafeSecCertificateHandle _certHandle; + private CertificateData _certData; + private bool _readCertData; + + public static ICertificatePal? FromHandle(IntPtr handle) + { + return FromHandle(handle, true); + } + + internal static ICertificatePal? FromHandle(IntPtr handle, bool throwOnFail) + { + if (handle == IntPtr.Zero) + throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); + + SafeSecCertificateHandle certHandle; + SafeSecIdentityHandle identityHandle; + + if (Interop.AppleCrypto.X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle)) + { + Debug.Assert( + certHandle.IsInvalid != identityHandle.IsInvalid, + $"certHandle.IsInvalid ({certHandle.IsInvalid}) should differ from identityHandle.IsInvalid ({identityHandle.IsInvalid})"); + + if (certHandle.IsInvalid) + { + certHandle.Dispose(); + return new AppleCertificatePal(identityHandle); + } + + identityHandle.Dispose(); + return new AppleCertificatePal(certHandle); + } + + certHandle.Dispose(); + identityHandle.Dispose(); + + if (throwOnFail) + { + throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); + } + + return null; + } + + public static ICertificatePal? FromOtherCert(X509Certificate cert) + { + Debug.Assert(cert.Pal != null); + + ICertificatePal? pal = FromHandle(cert.Handle); + GC.KeepAlive(cert); // ensure cert's safe handle isn't finalized while raw handle is in use + return pal; + } + + public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) + { + Debug.Assert(password != null); + + byte[] fileBytes = System.IO.File.ReadAllBytes(fileName); + return FromBlob(fileBytes, password, keyStorageFlags); + } + + internal AppleCertificatePal(SafeSecCertificateHandle certHandle) + { + Debug.Assert(!certHandle.IsInvalid); + + _certHandle = certHandle; + } + + internal AppleCertificatePal(SafeSecIdentityHandle identityHandle) + { + Debug.Assert(!identityHandle.IsInvalid); + + _identityHandle = identityHandle; + _certHandle = Interop.AppleCrypto.X509GetCertFromIdentity(identityHandle); + } + + public void Dispose() + { + _certHandle?.Dispose(); + _identityHandle?.Dispose(); + + _certHandle = null!; + _identityHandle = null; + + DisposeTempKeychain(); + } + + internal SafeSecCertificateHandle CertificateHandle => _certHandle; + internal SafeSecIdentityHandle? IdentityHandle => _identityHandle; + + public bool HasPrivateKey => !(_identityHandle?.IsInvalid ?? true); + + public IntPtr Handle + { + get + { + if (HasPrivateKey) + { + return _identityHandle!.DangerousGetHandle(); + } + + return _certHandle?.DangerousGetHandle() ?? IntPtr.Zero; + } + } + + public string Issuer + { + get + { + EnsureCertData(); + return _certData.IssuerName; + } + } + + public string Subject + { + get + { + EnsureCertData(); + return _certData.SubjectName; + } + } + + public string LegacyIssuer => IssuerName.Decode(X500DistinguishedNameFlags.None); + + public string LegacySubject => SubjectName.Decode(X500DistinguishedNameFlags.None); + + public string KeyAlgorithm + { + get + { + EnsureCertData(); + return _certData.PublicKeyAlgorithm.AlgorithmId!; + } + } + + public byte[] KeyAlgorithmParameters + { + get + { + EnsureCertData(); + return _certData.PublicKeyAlgorithm.Parameters; + } + } + + public byte[] PublicKeyValue + { + get + { + EnsureCertData(); + return _certData.PublicKey; + } + } + + public byte[] SerialNumber + { + get + { + EnsureCertData(); + return _certData.SerialNumber; + } + } + + public string SignatureAlgorithm + { + get + { + EnsureCertData(); + return _certData.SignatureAlgorithm.AlgorithmId!; + } + } + + public string FriendlyName + { + get { return ""; } + set + { + throw new PlatformNotSupportedException( + SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(FriendlyName))); + } + } + + public int Version + { + get + { + EnsureCertData(); + return _certData.Version + 1; + } + } + + public X500DistinguishedName SubjectName + { + get + { + EnsureCertData(); + return _certData.Subject; + } + } + + public X500DistinguishedName IssuerName + { + get + { + EnsureCertData(); + return _certData.Issuer; + } + } + + public PolicyData GetPolicyData() + { + PolicyData policyData = default; + EnsureCertData(); + + foreach (X509Extension extension in _certData.Extensions) + { + switch (extension.Oid!.Value) + { + case Oids.ApplicationCertPolicies: + policyData.ApplicationCertPolicies = extension.RawData; + break; + case Oids.CertPolicies: + policyData.CertPolicies = extension.RawData; + break; + case Oids.CertPolicyMappings: + policyData.CertPolicyMappings = extension.RawData; + break; + case Oids.CertPolicyConstraints: + policyData.CertPolicyConstraints = extension.RawData; + break; + case Oids.EnhancedKeyUsage: + policyData.EnhancedKeyUsage = extension.RawData; + break; + case Oids.InhibitAnyPolicyExtension: + policyData.InhibitAnyPolicyExtension = extension.RawData; + break; + } + } + + return policyData; + } + + public IEnumerable Extensions { + get + { + EnsureCertData(); + return _certData.Extensions; + } + } + + public byte[] RawData + { + get + { + EnsureCertData(); + return _certData.RawData.CloneByteArray(); + } + } + + public DateTime NotAfter + { + get + { + EnsureCertData(); + return _certData.NotAfter.ToLocalTime(); + } + } + + public DateTime NotBefore + { + get + { + EnsureCertData(); + return _certData.NotBefore.ToLocalTime(); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is required for Compat")] + public byte[] Thumbprint + { + get + { + EnsureCertData(); + return SHA1.HashData(_certData.RawData); + } + } + + public bool Archived + { + get { return false; } + set + { + throw new PlatformNotSupportedException( + SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(Archived))); + } + } + + public byte[] SubjectPublicKeyInfo + { + get + { + EnsureCertData(); + + return _certData.SubjectPublicKeyInfo; + } + } + + public RSA? GetRSAPrivateKey() + { + if (_identityHandle == null) + return null; + + Debug.Assert(!_identityHandle.IsInvalid); + SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); + SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); + Debug.Assert(!publicKey.IsInvalid); + + return new RSAImplementation.RSASecurityTransforms(publicKey, privateKey); + } + + public ECDsa? GetECDsaPrivateKey() + { + if (_identityHandle == null) + return null; + + Debug.Assert(!_identityHandle.IsInvalid); + SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); + SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); + Debug.Assert(!publicKey.IsInvalid); + + return new ECDsaImplementation.ECDsaSecurityTransforms(publicKey, privateKey); + } + + public ECDiffieHellman? GetECDiffieHellmanPrivateKey() + { + if (_identityHandle == null) + return null; + + Debug.Assert(!_identityHandle.IsInvalid); + SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); + SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); + Debug.Assert(!publicKey.IsInvalid); + + return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(publicKey, privateKey); + } + + public string GetNameInfo(X509NameType nameType, bool forIssuer) + { + EnsureCertData(); + return _certData.GetNameInfo(nameType, forIssuer); + } + + public void AppendPrivateKeyInfo(StringBuilder sb) + { + if (!HasPrivateKey) + { + return; + } + + // There's nothing really to say about the key, just acknowledge there is one. + sb.AppendLine(); + sb.AppendLine(); + sb.AppendLine("[Private Key]"); + } + + public byte[] Export(X509ContentType contentType, SafePasswordHandle password) + { + using (IExportPal storePal = StorePal.FromCertificate(this)) + { + byte[]? exported = storePal.Export(contentType, password); + Debug.Assert(exported != null); + return exported; + } + } + + private void EnsureCertData() + { + if (_readCertData) + return; + + Debug.Assert(!_certHandle.IsInvalid); + _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle)); + _readCertData = true; + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs index 1a69f66df5d14..450f85fe350a3 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs @@ -17,61 +17,8 @@ namespace Internal.Cryptography.Pal { internal sealed partial class AppleCertificatePal : ICertificatePal { - private SafeSecIdentityHandle? _identityHandle; - private SafeSecCertificateHandle _certHandle; - private CertificateData _certData; - private bool _readCertData; private SafeKeychainHandle? _tempKeychain; - public static ICertificatePal? FromHandle(IntPtr handle) - { - return FromHandle(handle, true); - } - - internal static ICertificatePal? FromHandle(IntPtr handle, bool throwOnFail) - { - if (handle == IntPtr.Zero) - throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); - - SafeSecCertificateHandle certHandle; - SafeSecIdentityHandle identityHandle; - - if (Interop.AppleCrypto.X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle)) - { - Debug.Assert( - certHandle.IsInvalid != identityHandle.IsInvalid, - $"certHandle.IsInvalid ({certHandle.IsInvalid}) should differ from identityHandle.IsInvalid ({identityHandle.IsInvalid})"); - - if (certHandle.IsInvalid) - { - certHandle.Dispose(); - return new AppleCertificatePal(identityHandle); - } - - identityHandle.Dispose(); - return new AppleCertificatePal(certHandle); - } - - certHandle.Dispose(); - identityHandle.Dispose(); - - if (throwOnFail) - { - throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); - } - - return null; - } - - public static ICertificatePal? FromOtherCert(X509Certificate cert) - { - Debug.Assert(cert.Pal != null); - - ICertificatePal? pal = FromHandle(cert.Handle); - GC.KeepAlive(cert); // ensure cert's safe handle isn't finalized while raw handle is in use - return pal; - } - public static ICertificatePal FromBlob( ReadOnlySpan rawData, SafePasswordHandle password, @@ -150,37 +97,8 @@ public static ICertificatePal FromBlob( throw new CryptographicException(); } - public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) + public void DisposeTempKeychain() { - Debug.Assert(password != null); - - byte[] fileBytes = System.IO.File.ReadAllBytes(fileName); - return FromBlob(fileBytes, password, keyStorageFlags); - } - - internal AppleCertificatePal(SafeSecCertificateHandle certHandle) - { - Debug.Assert(!certHandle.IsInvalid); - - _certHandle = certHandle; - } - - internal AppleCertificatePal(SafeSecIdentityHandle identityHandle) - { - Debug.Assert(!identityHandle.IsInvalid); - - _identityHandle = identityHandle; - _certHandle = Interop.AppleCrypto.X509GetCertFromIdentity(identityHandle); - } - - public void Dispose() - { - _certHandle?.Dispose(); - _identityHandle?.Dispose(); - - _certHandle = null!; - _identityHandle = null; - SafeKeychainHandle? tempKeychain = Interlocked.Exchange(ref _tempKeychain, null); if (tempKeychain != null) { @@ -188,226 +106,6 @@ public void Dispose() } } - internal SafeSecCertificateHandle CertificateHandle => _certHandle; - internal SafeSecIdentityHandle? IdentityHandle => _identityHandle; - - public bool HasPrivateKey => !(_identityHandle?.IsInvalid ?? true); - - public IntPtr Handle - { - get - { - if (HasPrivateKey) - { - return _identityHandle!.DangerousGetHandle(); - } - - return _certHandle?.DangerousGetHandle() ?? IntPtr.Zero; - } - } - - public string Issuer - { - get - { - EnsureCertData(); - return _certData.IssuerName; - } - } - - public string Subject - { - get - { - EnsureCertData(); - return _certData.SubjectName; - } - } - - public string LegacyIssuer => IssuerName.Decode(X500DistinguishedNameFlags.None); - - public string LegacySubject => SubjectName.Decode(X500DistinguishedNameFlags.None); - - public string KeyAlgorithm - { - get - { - EnsureCertData(); - return _certData.PublicKeyAlgorithm.AlgorithmId!; - } - } - - public byte[] KeyAlgorithmParameters - { - get - { - EnsureCertData(); - return _certData.PublicKeyAlgorithm.Parameters; - } - } - - public byte[] PublicKeyValue - { - get - { - EnsureCertData(); - return _certData.PublicKey; - } - } - - public byte[] SerialNumber - { - get - { - EnsureCertData(); - return _certData.SerialNumber; - } - } - - public string SignatureAlgorithm - { - get - { - EnsureCertData(); - return _certData.SignatureAlgorithm.AlgorithmId!; - } - } - - public string FriendlyName - { - get { return ""; } - set - { - throw new PlatformNotSupportedException( - SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(FriendlyName))); - } - } - - public int Version - { - get - { - EnsureCertData(); - return _certData.Version + 1; - } - } - - public X500DistinguishedName SubjectName - { - get - { - EnsureCertData(); - return _certData.Subject; - } - } - - public X500DistinguishedName IssuerName - { - get - { - EnsureCertData(); - return _certData.Issuer; - } - } - - public PolicyData GetPolicyData() - { - PolicyData policyData = default; - EnsureCertData(); - - foreach (X509Extension extension in _certData.Extensions) - { - switch (extension.Oid!.Value) - { - case Oids.ApplicationCertPolicies: - policyData.ApplicationCertPolicies = extension.RawData; - break; - case Oids.CertPolicies: - policyData.CertPolicies = extension.RawData; - break; - case Oids.CertPolicyMappings: - policyData.CertPolicyMappings = extension.RawData; - break; - case Oids.CertPolicyConstraints: - policyData.CertPolicyConstraints = extension.RawData; - break; - case Oids.EnhancedKeyUsage: - policyData.EnhancedKeyUsage = extension.RawData; - break; - case Oids.InhibitAnyPolicyExtension: - policyData.InhibitAnyPolicyExtension = extension.RawData; - break; - } - } - - return policyData; - } - - public IEnumerable Extensions { - get - { - EnsureCertData(); - return _certData.Extensions; - } - } - - public byte[] RawData - { - get - { - EnsureCertData(); - return _certData.RawData.CloneByteArray(); - } - } - - public DateTime NotAfter - { - get - { - EnsureCertData(); - return _certData.NotAfter.ToLocalTime(); - } - } - - public DateTime NotBefore - { - get - { - EnsureCertData(); - return _certData.NotBefore.ToLocalTime(); - } - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is required for Compat")] - public byte[] Thumbprint - { - get - { - EnsureCertData(); - return SHA1.HashData(_certData.RawData); - } - } - - public bool Archived - { - get { return false; } - set - { - throw new PlatformNotSupportedException( - SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(Archived))); - } - } - - public byte[] SubjectPublicKeyInfo - { - get - { - EnsureCertData(); - - return _certData.SubjectPublicKeyInfo; - } - } - internal unsafe byte[] ExportPkcs8(ReadOnlySpan password) { Debug.Assert(_identityHandle != null); @@ -445,19 +143,6 @@ internal static unsafe byte[] ExportPkcs8(SafeSecKeyRefHandle key, ReadOnlySpan< } } - public RSA? GetRSAPrivateKey() - { - if (_identityHandle == null) - return null; - - Debug.Assert(!_identityHandle.IsInvalid); - SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); - SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); - Debug.Assert(!publicKey.IsInvalid); - - return new RSAImplementation.RSASecurityTransforms(publicKey, privateKey); - } - public DSA? GetDSAPrivateKey() { if (_identityHandle == null) @@ -476,32 +161,6 @@ internal static unsafe byte[] ExportPkcs8(SafeSecKeyRefHandle key, ReadOnlySpan< return new DSAImplementation.DSASecurityTransforms(publicKey, privateKey); } - public ECDsa? GetECDsaPrivateKey() - { - if (_identityHandle == null) - return null; - - Debug.Assert(!_identityHandle.IsInvalid); - SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); - SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); - Debug.Assert(!publicKey.IsInvalid); - - return new ECDsaImplementation.ECDsaSecurityTransforms(publicKey, privateKey); - } - - public ECDiffieHellman? GetECDiffieHellmanPrivateKey() - { - if (_identityHandle == null) - return null; - - Debug.Assert(!_identityHandle.IsInvalid); - SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); - SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); - Debug.Assert(!publicKey.IsInvalid); - - return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(publicKey, privateKey); - } - public ICertificatePal CopyWithPrivateKey(DSA privateKey) { var typedKey = privateKey as DSAImplementation.DSASecurityTransforms; @@ -655,45 +314,5 @@ private ICertificatePal CopyWithPrivateKey(SafeSecKeyRefHandle? privateKey) return newPal; } } - - public string GetNameInfo(X509NameType nameType, bool forIssuer) - { - EnsureCertData(); - return _certData.GetNameInfo(nameType, forIssuer); - } - - public void AppendPrivateKeyInfo(StringBuilder sb) - { - if (!HasPrivateKey) - { - return; - } - - // There's nothing really to say about the key, just acknowledge there is one. - sb.AppendLine(); - sb.AppendLine(); - sb.AppendLine("[Private Key]"); - } - - public byte[] Export(X509ContentType contentType, SafePasswordHandle password) - { - using (IExportPal storePal = StorePal.FromCertificate(this)) - { - byte[]? exported = storePal.Export(contentType, password); - Debug.Assert(exported != null); - return exported; - } - } - - private void EnsureCertData() - { - if (_readCertData) - return; - - Debug.Assert(!_certHandle.IsInvalid); - _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle)); - _readCertData = true; - } - } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs index af09f37981c3f..e7beb6095e2da 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs @@ -20,60 +20,6 @@ namespace Internal.Cryptography.Pal { internal sealed partial class AppleCertificatePal : ICertificatePal { - private SafeSecIdentityHandle? _identityHandle; - private SafeSecCertificateHandle _certHandle; - private CertificateData _certData; - private bool _readCertData; - - public static ICertificatePal? FromHandle(IntPtr handle) - { - return FromHandle(handle, true); - } - - internal static ICertificatePal? FromHandle(IntPtr handle, bool throwOnFail) - { - if (handle == IntPtr.Zero) - throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); - - SafeSecCertificateHandle certHandle; - SafeSecIdentityHandle identityHandle; - - if (Interop.AppleCrypto.X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle)) - { - Debug.Assert( - certHandle.IsInvalid != identityHandle.IsInvalid, - $"certHandle.IsInvalid ({certHandle.IsInvalid}) should differ from identityHandle.IsInvalid ({identityHandle.IsInvalid})"); - - if (certHandle.IsInvalid) - { - certHandle.Dispose(); - return new AppleCertificatePal(identityHandle); - } - - identityHandle.Dispose(); - return new AppleCertificatePal(certHandle); - } - - certHandle.Dispose(); - identityHandle.Dispose(); - - if (throwOnFail) - { - throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); - } - - return null; - } - - public static ICertificatePal? FromOtherCert(X509Certificate cert) - { - Debug.Assert(cert.Pal != null); - - ICertificatePal? pal = FromHandle(cert.Handle); - GC.KeepAlive(cert); // ensure cert's safe handle isn't finalized while raw handle is in use - return pal; - } - private static bool IsPkcs12(ReadOnlySpan rawData) { try @@ -210,269 +156,9 @@ public static ICertificatePal FromBlob( return result ?? FromDerBlob(rawData, password, keyStorageFlags); } - public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) - { - Debug.Assert(password != null); - - byte[] fileBytes = System.IO.File.ReadAllBytes(fileName); - return FromBlob(fileBytes, password, keyStorageFlags); - } - - internal AppleCertificatePal(SafeSecCertificateHandle certHandle) - { - Debug.Assert(!certHandle.IsInvalid); - - _certHandle = certHandle; - } - - internal AppleCertificatePal(SafeSecIdentityHandle identityHandle) - { - Debug.Assert(!identityHandle.IsInvalid); - - _identityHandle = identityHandle; - _certHandle = Interop.AppleCrypto.X509GetCertFromIdentity(identityHandle); - } - - public void Dispose() - { - _certHandle?.Dispose(); - _identityHandle?.Dispose(); - - _certHandle = null!; - _identityHandle = null; - } - - internal SafeSecCertificateHandle CertificateHandle => _certHandle; - internal SafeSecIdentityHandle? IdentityHandle => _identityHandle; - - public bool HasPrivateKey => !(_identityHandle?.IsInvalid ?? true); - - public IntPtr Handle - { - get - { - if (HasPrivateKey) - { - return _identityHandle!.DangerousGetHandle(); - } - - return _certHandle?.DangerousGetHandle() ?? IntPtr.Zero; - } - } - - public string Issuer - { - get - { - EnsureCertData(); - return _certData.IssuerName; - } - } - - public string Subject - { - get - { - EnsureCertData(); - return _certData.SubjectName; - } - } - - public string LegacyIssuer => IssuerName.Decode(X500DistinguishedNameFlags.None); - - public string LegacySubject => SubjectName.Decode(X500DistinguishedNameFlags.None); - - public string KeyAlgorithm - { - get - { - EnsureCertData(); - return _certData.PublicKeyAlgorithm.AlgorithmId!; - } - } - - public byte[] KeyAlgorithmParameters - { - get - { - EnsureCertData(); - return _certData.PublicKeyAlgorithm.Parameters; - } - } - - public byte[] PublicKeyValue - { - get - { - EnsureCertData(); - return _certData.PublicKey; - } - } - - public byte[] SerialNumber - { - get - { - EnsureCertData(); - return _certData.SerialNumber; - } - } - - public string SignatureAlgorithm - { - get - { - EnsureCertData(); - return _certData.SignatureAlgorithm.AlgorithmId!; - } - } - - public string FriendlyName - { - get { return ""; } - set - { - throw new PlatformNotSupportedException( - SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(FriendlyName))); - } - } - - public int Version + public void DisposeTempKeychain() { - get - { - EnsureCertData(); - return _certData.Version + 1; - } - } - - public X500DistinguishedName SubjectName - { - get - { - EnsureCertData(); - return _certData.Subject; - } - } - - public X500DistinguishedName IssuerName - { - get - { - EnsureCertData(); - return _certData.Issuer; - } - } - - public PolicyData GetPolicyData() - { - PolicyData policyData = default; - EnsureCertData(); - - foreach (X509Extension extension in _certData.Extensions) - { - switch (extension.Oid!.Value) - { - case Oids.ApplicationCertPolicies: - policyData.ApplicationCertPolicies = extension.RawData; - break; - case Oids.CertPolicies: - policyData.CertPolicies = extension.RawData; - break; - case Oids.CertPolicyMappings: - policyData.CertPolicyMappings = extension.RawData; - break; - case Oids.CertPolicyConstraints: - policyData.CertPolicyConstraints = extension.RawData; - break; - case Oids.EnhancedKeyUsage: - policyData.EnhancedKeyUsage = extension.RawData; - break; - case Oids.InhibitAnyPolicyExtension: - policyData.InhibitAnyPolicyExtension = extension.RawData; - break; - } - } - - return policyData; - } - - public IEnumerable Extensions { - get - { - EnsureCertData(); - return _certData.Extensions; - } - } - - public byte[] RawData - { - get - { - EnsureCertData(); - return _certData.RawData.CloneByteArray(); - } - } - - public DateTime NotAfter - { - get - { - EnsureCertData(); - return _certData.NotAfter.ToLocalTime(); - } - } - - public DateTime NotBefore - { - get - { - EnsureCertData(); - return _certData.NotBefore.ToLocalTime(); - } - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is required for Compat")] - public byte[] Thumbprint - { - get - { - EnsureCertData(); - return SHA1.HashData(_certData.RawData); - } - } - - public bool Archived - { - get { return false; } - set - { - throw new PlatformNotSupportedException( - SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(Archived))); - } - } - - public byte[] SubjectPublicKeyInfo - { - get - { - EnsureCertData(); - - return _certData.SubjectPublicKeyInfo; - } - } - - public RSA? GetRSAPrivateKey() - { - if (_identityHandle == null) - return null; - - Debug.Assert(!_identityHandle.IsInvalid); - SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); - SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); - Debug.Assert(!publicKey.IsInvalid); - - return new RSAImplementation.RSASecurityTransforms(publicKey, privateKey); + // No temporary keychain on iOS } public DSA? GetDSAPrivateKey() @@ -483,36 +169,9 @@ public byte[] SubjectPublicKeyInfo throw new PlatformNotSupportedException(); } - public ECDsa? GetECDsaPrivateKey() - { - if (_identityHandle == null) - return null; - - Debug.Assert(!_identityHandle.IsInvalid); - SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); - SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); - Debug.Assert(!publicKey.IsInvalid); - - return new ECDsaImplementation.ECDsaSecurityTransforms(publicKey, privateKey); - } - - public ECDiffieHellman? GetECDiffieHellmanPrivateKey() - { - if (_identityHandle == null) - return null; - - Debug.Assert(!_identityHandle.IsInvalid); - SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); - SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); - Debug.Assert(!publicKey.IsInvalid); - - return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(publicKey, privateKey); - } - public ICertificatePal CopyWithPrivateKey(DSA privateKey) { - //return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); - throw new PlatformNotSupportedException("TODO CopyWithPrivateKey"); + throw new PlatformNotSupportedException(); } public ICertificatePal CopyWithPrivateKey(ECDsa privateKey) @@ -529,44 +188,5 @@ public ICertificatePal CopyWithPrivateKey(RSA privateKey) { return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); } - - public string GetNameInfo(X509NameType nameType, bool forIssuer) - { - EnsureCertData(); - return _certData.GetNameInfo(nameType, forIssuer); - } - - public void AppendPrivateKeyInfo(StringBuilder sb) - { - if (!HasPrivateKey) - { - return; - } - - // There's nothing really to say about the key, just acknowledge there is one. - sb.AppendLine(); - sb.AppendLine(); - sb.AppendLine("[Private Key]"); - } - - public byte[] Export(X509ContentType contentType, SafePasswordHandle password) - { - using (IExportPal storePal = StorePal.FromCertificate(this)) - { - byte[]? exported = storePal.Export(contentType, password); - Debug.Assert(exported != null); - return exported; - } - } - - private void EnsureCertData() - { - if (_readCertData) - return; - - Debug.Assert(!_certHandle.IsInvalid); - _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle)); - _readCertData = true; - } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/CertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/CertificatePal.cs deleted file mode 100644 index 0689088c77f52..0000000000000 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/CertificatePal.cs +++ /dev/null @@ -1,45 +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; -using System.Collections.Generic; -using System.Diagnostics; -using System.Security.Cryptography; -using System.Security.Cryptography.Apple; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using Microsoft.Win32.SafeHandles; - -namespace Internal.Cryptography.Pal -{ - internal sealed partial class CertificatePal - { - public static ICertificatePal? FromHandle(IntPtr handle) - { - return FromHandle(handle, true); - } - - internal static ICertificatePal? FromHandle(IntPtr handle, bool throwOnFail) - { - return AppleCertificatePal.FromHandle(handle, throwOnFail); - } - - public static ICertificatePal? FromOtherCert(X509Certificate cert) - { - return AppleCertificatePal.FromOtherCert(cert); - } - - public static ICertificatePal FromBlob( - ReadOnlySpan rawData, - SafePasswordHandle password, - X509KeyStorageFlags keyStorageFlags) - { - return AppleCertificatePal.FromBlob(rawData, password, keyStorageFlags); - } - - public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) - { - return AppleCertificatePal.FromFile(fileName, password, keyStorageFlags); - } - } -} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 2d725fdf254bf..3f9ef380b490f 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -592,6 +592,7 @@ + @@ -618,6 +619,7 @@ + From 149d4704e6c01a799f53f294368198c7d8ccfdaa Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Fri, 7 May 2021 10:20:25 +0200 Subject: [PATCH 14/27] Improve the AppleCertificatePal split --- .../Pal.OSX/AppleCertificatePal.Common.cs | 406 --------------- .../AppleCertificatePal.ImportExport.cs | 161 ++++++ .../Pal.OSX/AppleCertificatePal.Keys.cs | 177 +++++++ .../Pal.OSX/AppleCertificatePal.cs | 486 +++++++++++------- ...cs => AppleCertificatePal.ImportExport.cs} | 28 - .../Pal.iOS/AppleCertificatePal.Keys.cs | 39 ++ ...urity.Cryptography.X509Certificates.csproj | 8 +- 7 files changed, 669 insertions(+), 636 deletions(-) delete mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Common.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.ImportExport.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Keys.cs rename src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/{AppleCertificatePal.cs => AppleCertificatePal.ImportExport.cs} (85%) create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Keys.cs diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Common.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Common.cs deleted file mode 100644 index cb949b8a93f7a..0000000000000 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Common.cs +++ /dev/null @@ -1,406 +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; -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics; -using System.Formats.Asn1; -using System.Security.Cryptography; -using System.Security.Cryptography.Apple; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading; -using Microsoft.Win32.SafeHandles; - -namespace Internal.Cryptography.Pal -{ - internal sealed partial class AppleCertificatePal : ICertificatePal - { - private SafeSecIdentityHandle? _identityHandle; - private SafeSecCertificateHandle _certHandle; - private CertificateData _certData; - private bool _readCertData; - - public static ICertificatePal? FromHandle(IntPtr handle) - { - return FromHandle(handle, true); - } - - internal static ICertificatePal? FromHandle(IntPtr handle, bool throwOnFail) - { - if (handle == IntPtr.Zero) - throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); - - SafeSecCertificateHandle certHandle; - SafeSecIdentityHandle identityHandle; - - if (Interop.AppleCrypto.X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle)) - { - Debug.Assert( - certHandle.IsInvalid != identityHandle.IsInvalid, - $"certHandle.IsInvalid ({certHandle.IsInvalid}) should differ from identityHandle.IsInvalid ({identityHandle.IsInvalid})"); - - if (certHandle.IsInvalid) - { - certHandle.Dispose(); - return new AppleCertificatePal(identityHandle); - } - - identityHandle.Dispose(); - return new AppleCertificatePal(certHandle); - } - - certHandle.Dispose(); - identityHandle.Dispose(); - - if (throwOnFail) - { - throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); - } - - return null; - } - - public static ICertificatePal? FromOtherCert(X509Certificate cert) - { - Debug.Assert(cert.Pal != null); - - ICertificatePal? pal = FromHandle(cert.Handle); - GC.KeepAlive(cert); // ensure cert's safe handle isn't finalized while raw handle is in use - return pal; - } - - public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) - { - Debug.Assert(password != null); - - byte[] fileBytes = System.IO.File.ReadAllBytes(fileName); - return FromBlob(fileBytes, password, keyStorageFlags); - } - - internal AppleCertificatePal(SafeSecCertificateHandle certHandle) - { - Debug.Assert(!certHandle.IsInvalid); - - _certHandle = certHandle; - } - - internal AppleCertificatePal(SafeSecIdentityHandle identityHandle) - { - Debug.Assert(!identityHandle.IsInvalid); - - _identityHandle = identityHandle; - _certHandle = Interop.AppleCrypto.X509GetCertFromIdentity(identityHandle); - } - - public void Dispose() - { - _certHandle?.Dispose(); - _identityHandle?.Dispose(); - - _certHandle = null!; - _identityHandle = null; - - DisposeTempKeychain(); - } - - internal SafeSecCertificateHandle CertificateHandle => _certHandle; - internal SafeSecIdentityHandle? IdentityHandle => _identityHandle; - - public bool HasPrivateKey => !(_identityHandle?.IsInvalid ?? true); - - public IntPtr Handle - { - get - { - if (HasPrivateKey) - { - return _identityHandle!.DangerousGetHandle(); - } - - return _certHandle?.DangerousGetHandle() ?? IntPtr.Zero; - } - } - - public string Issuer - { - get - { - EnsureCertData(); - return _certData.IssuerName; - } - } - - public string Subject - { - get - { - EnsureCertData(); - return _certData.SubjectName; - } - } - - public string LegacyIssuer => IssuerName.Decode(X500DistinguishedNameFlags.None); - - public string LegacySubject => SubjectName.Decode(X500DistinguishedNameFlags.None); - - public string KeyAlgorithm - { - get - { - EnsureCertData(); - return _certData.PublicKeyAlgorithm.AlgorithmId!; - } - } - - public byte[] KeyAlgorithmParameters - { - get - { - EnsureCertData(); - return _certData.PublicKeyAlgorithm.Parameters; - } - } - - public byte[] PublicKeyValue - { - get - { - EnsureCertData(); - return _certData.PublicKey; - } - } - - public byte[] SerialNumber - { - get - { - EnsureCertData(); - return _certData.SerialNumber; - } - } - - public string SignatureAlgorithm - { - get - { - EnsureCertData(); - return _certData.SignatureAlgorithm.AlgorithmId!; - } - } - - public string FriendlyName - { - get { return ""; } - set - { - throw new PlatformNotSupportedException( - SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(FriendlyName))); - } - } - - public int Version - { - get - { - EnsureCertData(); - return _certData.Version + 1; - } - } - - public X500DistinguishedName SubjectName - { - get - { - EnsureCertData(); - return _certData.Subject; - } - } - - public X500DistinguishedName IssuerName - { - get - { - EnsureCertData(); - return _certData.Issuer; - } - } - - public PolicyData GetPolicyData() - { - PolicyData policyData = default; - EnsureCertData(); - - foreach (X509Extension extension in _certData.Extensions) - { - switch (extension.Oid!.Value) - { - case Oids.ApplicationCertPolicies: - policyData.ApplicationCertPolicies = extension.RawData; - break; - case Oids.CertPolicies: - policyData.CertPolicies = extension.RawData; - break; - case Oids.CertPolicyMappings: - policyData.CertPolicyMappings = extension.RawData; - break; - case Oids.CertPolicyConstraints: - policyData.CertPolicyConstraints = extension.RawData; - break; - case Oids.EnhancedKeyUsage: - policyData.EnhancedKeyUsage = extension.RawData; - break; - case Oids.InhibitAnyPolicyExtension: - policyData.InhibitAnyPolicyExtension = extension.RawData; - break; - } - } - - return policyData; - } - - public IEnumerable Extensions { - get - { - EnsureCertData(); - return _certData.Extensions; - } - } - - public byte[] RawData - { - get - { - EnsureCertData(); - return _certData.RawData.CloneByteArray(); - } - } - - public DateTime NotAfter - { - get - { - EnsureCertData(); - return _certData.NotAfter.ToLocalTime(); - } - } - - public DateTime NotBefore - { - get - { - EnsureCertData(); - return _certData.NotBefore.ToLocalTime(); - } - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is required for Compat")] - public byte[] Thumbprint - { - get - { - EnsureCertData(); - return SHA1.HashData(_certData.RawData); - } - } - - public bool Archived - { - get { return false; } - set - { - throw new PlatformNotSupportedException( - SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(Archived))); - } - } - - public byte[] SubjectPublicKeyInfo - { - get - { - EnsureCertData(); - - return _certData.SubjectPublicKeyInfo; - } - } - - public RSA? GetRSAPrivateKey() - { - if (_identityHandle == null) - return null; - - Debug.Assert(!_identityHandle.IsInvalid); - SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); - SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); - Debug.Assert(!publicKey.IsInvalid); - - return new RSAImplementation.RSASecurityTransforms(publicKey, privateKey); - } - - public ECDsa? GetECDsaPrivateKey() - { - if (_identityHandle == null) - return null; - - Debug.Assert(!_identityHandle.IsInvalid); - SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); - SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); - Debug.Assert(!publicKey.IsInvalid); - - return new ECDsaImplementation.ECDsaSecurityTransforms(publicKey, privateKey); - } - - public ECDiffieHellman? GetECDiffieHellmanPrivateKey() - { - if (_identityHandle == null) - return null; - - Debug.Assert(!_identityHandle.IsInvalid); - SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); - SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); - Debug.Assert(!publicKey.IsInvalid); - - return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(publicKey, privateKey); - } - - public string GetNameInfo(X509NameType nameType, bool forIssuer) - { - EnsureCertData(); - return _certData.GetNameInfo(nameType, forIssuer); - } - - public void AppendPrivateKeyInfo(StringBuilder sb) - { - if (!HasPrivateKey) - { - return; - } - - // There's nothing really to say about the key, just acknowledge there is one. - sb.AppendLine(); - sb.AppendLine(); - sb.AppendLine("[Private Key]"); - } - - public byte[] Export(X509ContentType contentType, SafePasswordHandle password) - { - using (IExportPal storePal = StorePal.FromCertificate(this)) - { - byte[]? exported = storePal.Export(contentType, password); - Debug.Assert(exported != null); - return exported; - } - } - - private void EnsureCertData() - { - if (_readCertData) - return; - - Debug.Assert(!_certHandle.IsInvalid); - _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle)); - _readCertData = true; - } - } -} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.ImportExport.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.ImportExport.cs new file mode 100644 index 0000000000000..f6b19f9bb0394 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.ImportExport.cs @@ -0,0 +1,161 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Formats.Asn1; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class AppleCertificatePal : ICertificatePal + { + private SafeKeychainHandle? _tempKeychain; + + public static ICertificatePal FromBlob( + ReadOnlySpan rawData, + SafePasswordHandle password, + X509KeyStorageFlags keyStorageFlags) + { + Debug.Assert(password != null); + + X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); + + if (contentType == X509ContentType.Pkcs7) + { + // In single mode for a PKCS#7 signed or signed-and-enveloped file we're supposed to return + // the certificate which signed the PKCS#7 file. + // + // X509Certificate2Collection::Export(X509ContentType.Pkcs7) claims to be a signed PKCS#7, + // but doesn't emit a signature block. So this is hard to test. + // + // TODO(2910): Figure out how to extract the signing certificate, when it's present. + throw new CryptographicException(SR.Cryptography_X509_PKCS7_NoSigner); + } + + if (contentType == X509ContentType.Pkcs12) + { + if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet) + { + throw new PlatformNotSupportedException(SR.Cryptography_X509_NoEphemeralPfx); + } + + 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) + { + AppleCertificatePal ret = ImportPkcs12(rawData, password, exportable, keychain); + if (!persist) + { + // If we used temporary keychain we need to prevent deletion. + // on 10.15+ if keychain is unlinked, certain certificate operations may fail. + bool success = false; + keychain.DangerousAddRef(ref success); + if (success) + { + ret._tempKeychain = keychain; + } + } + + return ret; + } + } + + SafeSecIdentityHandle identityHandle; + SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( + rawData, + contentType, + SafePasswordHandle.InvalidHandle, + SafeTemporaryKeychainHandle.InvalidHandle, + exportable: true, + 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(); + } + + public void DisposeTempKeychain() + { + SafeKeychainHandle? tempKeychain = Interlocked.Exchange(ref _tempKeychain, null); + if (tempKeychain != null) + { + tempKeychain.Dispose(); + } + } + + internal unsafe byte[] ExportPkcs8(ReadOnlySpan password) + { + Debug.Assert(_identityHandle != null); + + using (SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle)) + { + return ExportPkcs8(key, password); + } + } + + internal static unsafe byte[] ExportPkcs8(SafeSecKeyRefHandle key, ReadOnlySpan password) + { + using (SafeCFDataHandle data = Interop.AppleCrypto.SecKeyExportData(key, exportPrivate: true, password)) + { + ReadOnlySpan systemExport = Interop.CoreFoundation.CFDataDangerousGetSpan(data); + + fixed (byte* ptr = systemExport) + { + using (PointerMemoryManager manager = new PointerMemoryManager(ptr, systemExport.Length)) + { + // Apple's PKCS8 export exports using PBES2, which Win7, Win8.1, and Apple all fail to + // understand in their PKCS12 readers, so re-encrypt using the Win7 PKCS12-PBE parameters. + // + // Since Apple only reliably exports keys with encrypted PKCS#8 there's not a + // "so export it plaintext and only encrypt it once" option. + AsnWriter writer = KeyFormatHelper.ReencryptPkcs8( + password, + manager.Memory, + password, + UnixExportProvider.s_windowsPbe); + + return writer.Encode(); + } + } + } + } + + internal AppleCertificatePal? MoveToKeychain(SafeKeychainHandle keychain, SafeSecKeyRefHandle? privateKey) + { + SafeSecIdentityHandle? identity = Interop.AppleCrypto.X509MoveToKeychain( + _certHandle, + keychain, + privateKey); + + if (identity != null) + { + return new AppleCertificatePal(identity); + } + + return null; + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Keys.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Keys.cs new file mode 100644 index 0000000000000..4231c82f0ac95 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Keys.cs @@ -0,0 +1,177 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Formats.Asn1; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class AppleCertificatePal : ICertificatePal + { + public DSA? GetDSAPrivateKey() + { + if (_identityHandle == null) + return null; + + Debug.Assert(!_identityHandle.IsInvalid); + SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); + SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); + + if (publicKey.IsInvalid) + { + // SecCertificateCopyKey returns null for DSA, so fall back to manually building it. + publicKey = Interop.AppleCrypto.ImportEphemeralKey(_certData.SubjectPublicKeyInfo, false); + } + + return new DSAImplementation.DSASecurityTransforms(publicKey, privateKey); + } + + public ICertificatePal CopyWithPrivateKey(DSA privateKey) + { + var typedKey = privateKey as DSAImplementation.DSASecurityTransforms; + + if (typedKey != null) + { + return CopyWithPrivateKey(typedKey.GetKeys().PrivateKey); + } + + DSAParameters dsaParameters = privateKey.ExportParameters(true); + + using (PinAndClear.Track(dsaParameters.X!)) + using (typedKey = new DSAImplementation.DSASecurityTransforms()) + { + typedKey.ImportParameters(dsaParameters); + return CopyWithPrivateKey(typedKey.GetKeys().PrivateKey); + } + } + + public ICertificatePal CopyWithPrivateKey(ECDsa privateKey) + { + var typedKey = privateKey as ECDsaImplementation.ECDsaSecurityTransforms; + + if (typedKey != null) + { + return CopyWithPrivateKey(typedKey.GetKeys().PrivateKey); + } + + byte[] ecPrivateKey = privateKey.ExportECPrivateKey(); + + using (PinAndClear.Track(ecPrivateKey)) + using (SafeSecKeyRefHandle privateSecKey = Interop.AppleCrypto.ImportEphemeralKey(ecPrivateKey, true)) + { + return CopyWithPrivateKey(privateSecKey); + } + } + + public ICertificatePal CopyWithPrivateKey(ECDiffieHellman privateKey) + { + var typedKey = privateKey as ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms; + + if (typedKey != null) + { + return CopyWithPrivateKey(typedKey.GetKeys().PrivateKey); + } + + byte[] ecPrivateKey = privateKey.ExportECPrivateKey(); + + using (PinAndClear.Track(ecPrivateKey)) + using (SafeSecKeyRefHandle privateSecKey = Interop.AppleCrypto.ImportEphemeralKey(ecPrivateKey, true)) + { + return CopyWithPrivateKey(privateSecKey); + } + } + + public ICertificatePal CopyWithPrivateKey(RSA privateKey) + { + var typedKey = privateKey as RSAImplementation.RSASecurityTransforms; + + if (typedKey != null) + { + return CopyWithPrivateKey(typedKey.GetKeys().PrivateKey); + } + + byte[] rsaPrivateKey = privateKey.ExportRSAPrivateKey(); + + using (PinAndClear.Track(rsaPrivateKey)) + using (SafeSecKeyRefHandle privateSecKey = Interop.AppleCrypto.ImportEphemeralKey(rsaPrivateKey, true)) + { + return CopyWithPrivateKey(privateSecKey); + } + } + + private ICertificatePal CopyWithPrivateKey(SafeSecKeyRefHandle? privateKey) + { + if (privateKey == null) + { + // Both Windows and Linux/OpenSSL are unaware if they bound a public or private key. + // Here, we do know. So throw if we can't do what they asked. + throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey); + } + + SafeKeychainHandle keychain = Interop.AppleCrypto.SecKeychainItemCopyKeychain(privateKey); + + // If we're using a key already in a keychain don't add the certificate to that keychain here, + // do it in the temporary add/remove in the shim. + SafeKeychainHandle cloneKeychain = SafeTemporaryKeychainHandle.InvalidHandle; + + if (keychain.IsInvalid) + { + keychain = Interop.AppleCrypto.CreateTemporaryKeychain(); + cloneKeychain = keychain; + } + + // Because SecIdentityRef only has private constructors we need to have the cert and the key + // in the same keychain. That almost certainly means we're going to need to add this cert to a + // keychain, and when a cert that isn't part of a keychain gets added to a keychain then the + // interior pointer of "what keychain did I come from?" used by SecKeychainItemCopyKeychain gets + // set. That makes this function have side effects, which is not desired. + // + // It also makes reference tracking on temporary keychains broken, since the cert can + // DangerousRelease a handle it didn't DangerousAddRef on. And so CopyWithPrivateKey makes + // a temporary keychain, then deletes it before anyone has a chance to (e.g.) export the + // new identity as a PKCS#12 blob. + // + // Solution: Clone the cert, like we do in Windows. + SafeSecCertificateHandle tempHandle; + + { + byte[] export = RawData; + const bool exportable = false; + SafeSecIdentityHandle identityHandle; + tempHandle = Interop.AppleCrypto.X509ImportCertificate( + export, + X509ContentType.Cert, + SafePasswordHandle.InvalidHandle, + cloneKeychain, + exportable, + out identityHandle); + + Debug.Assert(identityHandle.IsInvalid, "identityHandle should be IsInvalid"); + identityHandle.Dispose(); + + Debug.Assert(!tempHandle.IsInvalid, "tempHandle should not be IsInvalid"); + } + + using (keychain) + using (tempHandle) + { + SafeSecIdentityHandle identityHandle = Interop.AppleCrypto.X509CopyWithPrivateKey( + tempHandle, + privateKey, + keychain); + + AppleCertificatePal newPal = new AppleCertificatePal(identityHandle); + return newPal; + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs index 450f85fe350a3..cb949b8a93f7a 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.cs @@ -17,302 +17,390 @@ namespace Internal.Cryptography.Pal { internal sealed partial class AppleCertificatePal : ICertificatePal { - private SafeKeychainHandle? _tempKeychain; + private SafeSecIdentityHandle? _identityHandle; + private SafeSecCertificateHandle _certHandle; + private CertificateData _certData; + private bool _readCertData; - public static ICertificatePal FromBlob( - ReadOnlySpan rawData, - SafePasswordHandle password, - X509KeyStorageFlags keyStorageFlags) + public static ICertificatePal? FromHandle(IntPtr handle) { - Debug.Assert(password != null); + return FromHandle(handle, true); + } - X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); + internal static ICertificatePal? FromHandle(IntPtr handle, bool throwOnFail) + { + if (handle == IntPtr.Zero) + throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); - if (contentType == X509ContentType.Pkcs7) - { - // In single mode for a PKCS#7 signed or signed-and-enveloped file we're supposed to return - // the certificate which signed the PKCS#7 file. - // - // X509Certificate2Collection::Export(X509ContentType.Pkcs7) claims to be a signed PKCS#7, - // but doesn't emit a signature block. So this is hard to test. - // - // TODO(2910): Figure out how to extract the signing certificate, when it's present. - throw new CryptographicException(SR.Cryptography_X509_PKCS7_NoSigner); - } + SafeSecCertificateHandle certHandle; + SafeSecIdentityHandle identityHandle; - if (contentType == X509ContentType.Pkcs12) + if (Interop.AppleCrypto.X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle)) { - if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet) - { - throw new PlatformNotSupportedException(SR.Cryptography_X509_NoEphemeralPfx); - } - - bool exportable = (keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable; + Debug.Assert( + certHandle.IsInvalid != identityHandle.IsInvalid, + $"certHandle.IsInvalid ({certHandle.IsInvalid}) should differ from identityHandle.IsInvalid ({identityHandle.IsInvalid})"); - bool persist = - (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet; - - SafeKeychainHandle keychain = persist - ? Interop.AppleCrypto.SecKeychainCopyDefault() - : Interop.AppleCrypto.CreateTemporaryKeychain(); - - using (keychain) + if (certHandle.IsInvalid) { - AppleCertificatePal ret = ImportPkcs12(rawData, password, exportable, keychain); - if (!persist) - { - // If we used temporary keychain we need to prevent deletion. - // on 10.15+ if keychain is unlinked, certain certificate operations may fail. - bool success = false; - keychain.DangerousAddRef(ref success); - if (success) - { - ret._tempKeychain = keychain; - } - } - - return ret; + certHandle.Dispose(); + return new AppleCertificatePal(identityHandle); } - } - SafeSecIdentityHandle identityHandle; - SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate( - rawData, - contentType, - SafePasswordHandle.InvalidHandle, - SafeTemporaryKeychainHandle.InvalidHandle, - exportable: true, - 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(); - } + identityHandle.Dispose(); - public void DisposeTempKeychain() - { - SafeKeychainHandle? tempKeychain = Interlocked.Exchange(ref _tempKeychain, null); - if (tempKeychain != null) + if (throwOnFail) { - tempKeychain.Dispose(); + throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); } + + return null; } - internal unsafe byte[] ExportPkcs8(ReadOnlySpan password) + public static ICertificatePal? FromOtherCert(X509Certificate cert) { - Debug.Assert(_identityHandle != null); + Debug.Assert(cert.Pal != null); - using (SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle)) - { - return ExportPkcs8(key, password); - } + ICertificatePal? pal = FromHandle(cert.Handle); + GC.KeepAlive(cert); // ensure cert's safe handle isn't finalized while raw handle is in use + return pal; } - internal static unsafe byte[] ExportPkcs8(SafeSecKeyRefHandle key, ReadOnlySpan password) + public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { - using (SafeCFDataHandle data = Interop.AppleCrypto.SecKeyExportData(key, exportPrivate: true, password)) - { - ReadOnlySpan systemExport = Interop.CoreFoundation.CFDataDangerousGetSpan(data); + Debug.Assert(password != null); + + byte[] fileBytes = System.IO.File.ReadAllBytes(fileName); + return FromBlob(fileBytes, password, keyStorageFlags); + } + + internal AppleCertificatePal(SafeSecCertificateHandle certHandle) + { + Debug.Assert(!certHandle.IsInvalid); + + _certHandle = certHandle; + } + + internal AppleCertificatePal(SafeSecIdentityHandle identityHandle) + { + Debug.Assert(!identityHandle.IsInvalid); + + _identityHandle = identityHandle; + _certHandle = Interop.AppleCrypto.X509GetCertFromIdentity(identityHandle); + } - fixed (byte* ptr = systemExport) + public void Dispose() + { + _certHandle?.Dispose(); + _identityHandle?.Dispose(); + + _certHandle = null!; + _identityHandle = null; + + DisposeTempKeychain(); + } + + internal SafeSecCertificateHandle CertificateHandle => _certHandle; + internal SafeSecIdentityHandle? IdentityHandle => _identityHandle; + + public bool HasPrivateKey => !(_identityHandle?.IsInvalid ?? true); + + public IntPtr Handle + { + get + { + if (HasPrivateKey) { - using (PointerMemoryManager manager = new PointerMemoryManager(ptr, systemExport.Length)) - { - // Apple's PKCS8 export exports using PBES2, which Win7, Win8.1, and Apple all fail to - // understand in their PKCS12 readers, so re-encrypt using the Win7 PKCS12-PBE parameters. - // - // Since Apple only reliably exports keys with encrypted PKCS#8 there's not a - // "so export it plaintext and only encrypt it once" option. - AsnWriter writer = KeyFormatHelper.ReencryptPkcs8( - password, - manager.Memory, - password, - UnixExportProvider.s_windowsPbe); - - return writer.Encode(); - } + return _identityHandle!.DangerousGetHandle(); } + + return _certHandle?.DangerousGetHandle() ?? IntPtr.Zero; } } - public DSA? GetDSAPrivateKey() + public string Issuer { - if (_identityHandle == null) - return null; - - Debug.Assert(!_identityHandle.IsInvalid); - SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); - SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); + get + { + EnsureCertData(); + return _certData.IssuerName; + } + } - if (publicKey.IsInvalid) + public string Subject + { + get { - // SecCertificateCopyKey returns null for DSA, so fall back to manually building it. - publicKey = Interop.AppleCrypto.ImportEphemeralKey(_certData.SubjectPublicKeyInfo, false); + EnsureCertData(); + return _certData.SubjectName; } + } + + public string LegacyIssuer => IssuerName.Decode(X500DistinguishedNameFlags.None); + + public string LegacySubject => SubjectName.Decode(X500DistinguishedNameFlags.None); - return new DSAImplementation.DSASecurityTransforms(publicKey, privateKey); + public string KeyAlgorithm + { + get + { + EnsureCertData(); + return _certData.PublicKeyAlgorithm.AlgorithmId!; + } } - public ICertificatePal CopyWithPrivateKey(DSA privateKey) + public byte[] KeyAlgorithmParameters { - var typedKey = privateKey as DSAImplementation.DSASecurityTransforms; + get + { + EnsureCertData(); + return _certData.PublicKeyAlgorithm.Parameters; + } + } - if (typedKey != null) + public byte[] PublicKeyValue + { + get { - return CopyWithPrivateKey(typedKey.GetKeys().PrivateKey); + EnsureCertData(); + return _certData.PublicKey; } + } - DSAParameters dsaParameters = privateKey.ExportParameters(true); + public byte[] SerialNumber + { + get + { + EnsureCertData(); + return _certData.SerialNumber; + } + } - using (PinAndClear.Track(dsaParameters.X!)) - using (typedKey = new DSAImplementation.DSASecurityTransforms()) + public string SignatureAlgorithm + { + get { - typedKey.ImportParameters(dsaParameters); - return CopyWithPrivateKey(typedKey.GetKeys().PrivateKey); + EnsureCertData(); + return _certData.SignatureAlgorithm.AlgorithmId!; } } - public ICertificatePal CopyWithPrivateKey(ECDsa privateKey) + public string FriendlyName { - var typedKey = privateKey as ECDsaImplementation.ECDsaSecurityTransforms; + get { return ""; } + set + { + throw new PlatformNotSupportedException( + SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(FriendlyName))); + } + } - if (typedKey != null) + public int Version + { + get { - return CopyWithPrivateKey(typedKey.GetKeys().PrivateKey); + EnsureCertData(); + return _certData.Version + 1; } + } - byte[] ecPrivateKey = privateKey.ExportECPrivateKey(); + public X500DistinguishedName SubjectName + { + get + { + EnsureCertData(); + return _certData.Subject; + } + } - using (PinAndClear.Track(ecPrivateKey)) - using (SafeSecKeyRefHandle privateSecKey = Interop.AppleCrypto.ImportEphemeralKey(ecPrivateKey, true)) + public X500DistinguishedName IssuerName + { + get { - return CopyWithPrivateKey(privateSecKey); + EnsureCertData(); + return _certData.Issuer; } } - public ICertificatePal CopyWithPrivateKey(ECDiffieHellman privateKey) + public PolicyData GetPolicyData() { - var typedKey = privateKey as ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms; + PolicyData policyData = default; + EnsureCertData(); - if (typedKey != null) + foreach (X509Extension extension in _certData.Extensions) { - return CopyWithPrivateKey(typedKey.GetKeys().PrivateKey); + switch (extension.Oid!.Value) + { + case Oids.ApplicationCertPolicies: + policyData.ApplicationCertPolicies = extension.RawData; + break; + case Oids.CertPolicies: + policyData.CertPolicies = extension.RawData; + break; + case Oids.CertPolicyMappings: + policyData.CertPolicyMappings = extension.RawData; + break; + case Oids.CertPolicyConstraints: + policyData.CertPolicyConstraints = extension.RawData; + break; + case Oids.EnhancedKeyUsage: + policyData.EnhancedKeyUsage = extension.RawData; + break; + case Oids.InhibitAnyPolicyExtension: + policyData.InhibitAnyPolicyExtension = extension.RawData; + break; + } } - byte[] ecPrivateKey = privateKey.ExportECPrivateKey(); + return policyData; + } - using (PinAndClear.Track(ecPrivateKey)) - using (SafeSecKeyRefHandle privateSecKey = Interop.AppleCrypto.ImportEphemeralKey(ecPrivateKey, true)) + public IEnumerable Extensions { + get { - return CopyWithPrivateKey(privateSecKey); + EnsureCertData(); + return _certData.Extensions; } } - public ICertificatePal CopyWithPrivateKey(RSA privateKey) + public byte[] RawData { - var typedKey = privateKey as RSAImplementation.RSASecurityTransforms; - - if (typedKey != null) + get { - return CopyWithPrivateKey(typedKey.GetKeys().PrivateKey); + EnsureCertData(); + return _certData.RawData.CloneByteArray(); } + } - byte[] rsaPrivateKey = privateKey.ExportRSAPrivateKey(); - - using (PinAndClear.Track(rsaPrivateKey)) - using (SafeSecKeyRefHandle privateSecKey = Interop.AppleCrypto.ImportEphemeralKey(rsaPrivateKey, true)) + public DateTime NotAfter + { + get { - return CopyWithPrivateKey(privateSecKey); + EnsureCertData(); + return _certData.NotAfter.ToLocalTime(); } } - internal AppleCertificatePal? MoveToKeychain(SafeKeychainHandle keychain, SafeSecKeyRefHandle? privateKey) + public DateTime NotBefore { - SafeSecIdentityHandle? identity = Interop.AppleCrypto.X509MoveToKeychain( - _certHandle, - keychain, - privateKey); + get + { + EnsureCertData(); + return _certData.NotBefore.ToLocalTime(); + } + } - if (identity != null) + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is required for Compat")] + public byte[] Thumbprint + { + get { - return new AppleCertificatePal(identity); + EnsureCertData(); + return SHA1.HashData(_certData.RawData); } + } - return null; + public bool Archived + { + get { return false; } + set + { + throw new PlatformNotSupportedException( + SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(Archived))); + } } - private ICertificatePal CopyWithPrivateKey(SafeSecKeyRefHandle? privateKey) + public byte[] SubjectPublicKeyInfo { - if (privateKey == null) + get { - // Both Windows and Linux/OpenSSL are unaware if they bound a public or private key. - // Here, we do know. So throw if we can't do what they asked. - throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey); + EnsureCertData(); + + return _certData.SubjectPublicKeyInfo; } + } - SafeKeychainHandle keychain = Interop.AppleCrypto.SecKeychainItemCopyKeychain(privateKey); + public RSA? GetRSAPrivateKey() + { + if (_identityHandle == null) + return null; - // If we're using a key already in a keychain don't add the certificate to that keychain here, - // do it in the temporary add/remove in the shim. - SafeKeychainHandle cloneKeychain = SafeTemporaryKeychainHandle.InvalidHandle; + Debug.Assert(!_identityHandle.IsInvalid); + SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); + SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); + Debug.Assert(!publicKey.IsInvalid); - if (keychain.IsInvalid) + return new RSAImplementation.RSASecurityTransforms(publicKey, privateKey); + } + + public ECDsa? GetECDsaPrivateKey() + { + if (_identityHandle == null) + return null; + + Debug.Assert(!_identityHandle.IsInvalid); + SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); + SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); + Debug.Assert(!publicKey.IsInvalid); + + return new ECDsaImplementation.ECDsaSecurityTransforms(publicKey, privateKey); + } + + public ECDiffieHellman? GetECDiffieHellmanPrivateKey() + { + if (_identityHandle == null) + return null; + + Debug.Assert(!_identityHandle.IsInvalid); + SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle); + SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle); + Debug.Assert(!publicKey.IsInvalid); + + return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(publicKey, privateKey); + } + + public string GetNameInfo(X509NameType nameType, bool forIssuer) + { + EnsureCertData(); + return _certData.GetNameInfo(nameType, forIssuer); + } + + public void AppendPrivateKeyInfo(StringBuilder sb) + { + if (!HasPrivateKey) { - keychain = Interop.AppleCrypto.CreateTemporaryKeychain(); - cloneKeychain = keychain; + return; } - // Because SecIdentityRef only has private constructors we need to have the cert and the key - // in the same keychain. That almost certainly means we're going to need to add this cert to a - // keychain, and when a cert that isn't part of a keychain gets added to a keychain then the - // interior pointer of "what keychain did I come from?" used by SecKeychainItemCopyKeychain gets - // set. That makes this function have side effects, which is not desired. - // - // It also makes reference tracking on temporary keychains broken, since the cert can - // DangerousRelease a handle it didn't DangerousAddRef on. And so CopyWithPrivateKey makes - // a temporary keychain, then deletes it before anyone has a chance to (e.g.) export the - // new identity as a PKCS#12 blob. - // - // Solution: Clone the cert, like we do in Windows. - SafeSecCertificateHandle tempHandle; + // There's nothing really to say about the key, just acknowledge there is one. + sb.AppendLine(); + sb.AppendLine(); + sb.AppendLine("[Private Key]"); + } + public byte[] Export(X509ContentType contentType, SafePasswordHandle password) + { + using (IExportPal storePal = StorePal.FromCertificate(this)) { - byte[] export = RawData; - const bool exportable = false; - SafeSecIdentityHandle identityHandle; - tempHandle = Interop.AppleCrypto.X509ImportCertificate( - export, - X509ContentType.Cert, - SafePasswordHandle.InvalidHandle, - cloneKeychain, - exportable, - out identityHandle); - - Debug.Assert(identityHandle.IsInvalid, "identityHandle should be IsInvalid"); - identityHandle.Dispose(); - - Debug.Assert(!tempHandle.IsInvalid, "tempHandle should not be IsInvalid"); + byte[]? exported = storePal.Export(contentType, password); + Debug.Assert(exported != null); + return exported; } + } - using (keychain) - using (tempHandle) - { - SafeSecIdentityHandle identityHandle = Interop.AppleCrypto.X509CopyWithPrivateKey( - tempHandle, - privateKey, - keychain); + private void EnsureCertData() + { + if (_readCertData) + return; - AppleCertificatePal newPal = new AppleCertificatePal(identityHandle); - return newPal; - } + Debug.Assert(!_certHandle.IsInvalid); + _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle)); + _readCertData = true; } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs similarity index 85% rename from src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs rename to src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs index e7beb6095e2da..04b445699ed4a 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs @@ -160,33 +160,5 @@ public void DisposeTempKeychain() { // No temporary keychain on iOS } - - public DSA? GetDSAPrivateKey() - { - if (_identityHandle == null) - return null; - - throw new PlatformNotSupportedException(); - } - - public ICertificatePal CopyWithPrivateKey(DSA privateKey) - { - throw new PlatformNotSupportedException(); - } - - public ICertificatePal CopyWithPrivateKey(ECDsa privateKey) - { - return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); - } - - public ICertificatePal CopyWithPrivateKey(ECDiffieHellman privateKey) - { - return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); - } - - public ICertificatePal CopyWithPrivateKey(RSA privateKey) - { - return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); - } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Keys.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Keys.cs new file mode 100644 index 0000000000000..98a8df28cf255 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Keys.cs @@ -0,0 +1,39 @@ +// 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.Security.Cryptography; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class AppleCertificatePal : ICertificatePal + { + public DSA? GetDSAPrivateKey() + { + if (_identityHandle == null) + return null; + + throw new PlatformNotSupportedException(); + } + + public ICertificatePal CopyWithPrivateKey(DSA privateKey) + { + throw new PlatformNotSupportedException(); + } + + public ICertificatePal CopyWithPrivateKey(ECDsa privateKey) + { + return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); + } + + public ICertificatePal CopyWithPrivateKey(ECDiffieHellman privateKey) + { + return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); + } + + public ICertificatePal CopyWithPrivateKey(RSA privateKey) + { + return ImportPkcs12(new UnixPkcs12Reader.CertAndKey { Cert = this, Key = privateKey }); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 3f9ef380b490f..288a4f38616f1 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -592,7 +592,8 @@ - + + @@ -616,10 +617,11 @@ Link="Common\System\Security\Cryptography\EccSecurityTransforms.iOS.cs" /> - + + + - From c89564609a1bc6e0dcec742ec6c5e085a6f69345 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Fri, 7 May 2021 10:25:49 +0200 Subject: [PATCH 15/27] Remove DSA completely from the iOS side of code --- .../Internal/Cryptography/Pal.iOS/ApplePkcs12Reader.cs | 3 --- ...ystem.Security.Cryptography.X509Certificates.csproj | 10 ++++------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12Reader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12Reader.cs index cb83e638f39fa..bb7acb4e27d9c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12Reader.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12Reader.cs @@ -50,9 +50,6 @@ protected override AsymmetricAlgorithm LoadKey(ReadOnlyMemory pkcs8) 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(); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 288a4f38616f1..9dbc9c14afa98 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -498,10 +498,6 @@ Link="Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.X509Chain.cs" /> - - + + - Date: Fri, 7 May 2021 10:52:35 +0200 Subject: [PATCH 16/27] Reuse more code on import/export --- .../Pal.iOS/AppleCertificateExporter.cs | 56 +++++++++ .../Pal.iOS/AppleCertificatePal.Pkcs12.cs | 47 +++---- .../Pal.iOS/ApplePkcs12CertLoader.cs | 47 +++++++ .../Pal.iOS/StorePal.ExportPal.cs | 59 --------- .../Pal.iOS/StorePal.LoaderPal.cs | 115 ------------------ .../Internal/Cryptography/Pal.iOS/StorePal.cs | 39 +++++- ...urity.Cryptography.X509Certificates.csproj | 5 +- 7 files changed, 165 insertions(+), 203 deletions(-) create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificateExporter.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12CertLoader.cs delete mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.ExportPal.cs delete mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.LoaderPal.cs diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificateExporter.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificateExporter.cs new file mode 100644 index 0000000000000..45fa8f05b93a6 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificateExporter.cs @@ -0,0 +1,56 @@ +// 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.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed class AppleCertificateExporter : UnixExportProvider + { + public AppleCertificateExporter(ICertificatePalCore cert) + : base(cert) + { + } + + public AppleCertificateExporter(X509Certificate2Collection certs) + : base(certs) + { + } + + protected override byte[] ExportPkcs7() + { + throw new CryptographicException( + SR.Cryptography_X509_PKCS7_Unsupported, + new PlatformNotSupportedException(SR.Cryptography_X509_PKCS7_Unsupported)); + } + + protected override byte[] ExportPkcs8(ICertificatePalCore certificatePal, ReadOnlySpan password) + { + Debug.Assert(certificatePal.HasPrivateKey); + ICertificatePal pal = (ICertificatePal)certificatePal; + AsymmetricAlgorithm algorithm; + + switch (pal.KeyAlgorithm) + { + case Oids.Rsa: + algorithm = pal.GetRSAPrivateKey()!; + break; + case Oids.EcPublicKey: + algorithm = pal.GetECDsaPrivateKey()!; + break; + case Oids.Dsa: + default: + throw new CryptographicException(SR.Format(SR.Cryptography_UnknownKeyAlgorithm, pal.KeyAlgorithm)); + }; + + using (algorithm) + { + return algorithm.ExportEncryptedPkcs8PrivateKey(password, s_windowsPbe); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs index a3c66516cf119..467735d8da92b 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Collections.Generic; using System.Diagnostics; using System.Formats.Asn1; using System.Security.Cryptography; @@ -10,6 +11,7 @@ using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Asn1.Pkcs12; +using System.Text; using Microsoft.Win32.SafeHandles; namespace Internal.Cryptography.Pal @@ -35,7 +37,7 @@ internal static AppleCertificatePal ImportPkcs12(UnixPkcs12Reader.CertAndKey cer if (certAndKey.Key != null) { - Pkcs12SmallExport exporter = new Pkcs12SmallExport(new TempExportPal(pal), certAndKey.Key); + AppleCertificateExporter exporter = new AppleCertificateExporter(new TempExportPal(pal, certAndKey.Key)); byte[] smallPfx = exporter.Export(X509ContentType.Pkcs12, s_passwordExportHandle)!; SafeSecIdentityHandle identityHandle; @@ -58,34 +60,23 @@ internal static AppleCertificatePal ImportPkcs12(UnixPkcs12Reader.CertAndKey cer return pal; } - private sealed class Pkcs12SmallExport : UnixExportProvider - { - private readonly AsymmetricAlgorithm _privateKey; - - internal Pkcs12SmallExport(ICertificatePalCore cert, AsymmetricAlgorithm privateKey) - : base(cert) - { - _privateKey = privateKey; - } - - protected override byte[] ExportPkcs7() => throw new NotImplementedException(); - - protected override byte[] ExportPkcs8(ICertificatePalCore certificatePal, ReadOnlySpan password) - { - return _privateKey.ExportEncryptedPkcs8PrivateKey(password, s_windowsPbe); - } - } - - private sealed class TempExportPal : ICertificatePalCore + private sealed class TempExportPal : ICertificatePal { private readonly ICertificatePal _realPal; + private readonly AsymmetricAlgorithm _privateKey; - internal TempExportPal(AppleCertificatePal realPal) + internal TempExportPal(AppleCertificatePal realPal, AsymmetricAlgorithm privateKey) { + Debug.Assert(privateKey != null); _realPal = realPal; + _privateKey = privateKey; } public bool HasPrivateKey => true; + public RSA? GetRSAPrivateKey() => _privateKey as RSA; + public DSA? GetDSAPrivateKey() => _privateKey as DSA; + public ECDsa? GetECDsaPrivateKey() => _privateKey as ECDsa; + public ECDiffieHellman? GetECDiffieHellmanPrivateKey() => _privateKey as ECDiffieHellman; public void Dispose() { @@ -109,6 +100,20 @@ public void Dispose() public byte[] RawData => _realPal.RawData; public byte[] Export(X509ContentType contentType, SafePasswordHandle password) => _realPal.Export(contentType, password); + + public int Version => _realPal.Version; + public bool Archived { get => _realPal.Archived; set => _realPal.Archived = value; } + public string FriendlyName { get => _realPal.FriendlyName; set => _realPal.FriendlyName = value; } + public X500DistinguishedName SubjectName => _realPal.SubjectName; + public X500DistinguishedName IssuerName => _realPal.IssuerName; + public IEnumerable Extensions => _realPal.Extensions; + public string GetNameInfo(X509NameType nameType, bool forIssuer) => _realPal.GetNameInfo(nameType, forIssuer); + public void AppendPrivateKeyInfo(StringBuilder sb) => _realPal.AppendPrivateKeyInfo(sb); + public ICertificatePal CopyWithPrivateKey(DSA privateKey) => _realPal.CopyWithPrivateKey(privateKey); + public ICertificatePal CopyWithPrivateKey(ECDsa privateKey) => _realPal.CopyWithPrivateKey(privateKey); + public ICertificatePal CopyWithPrivateKey(RSA privateKey) => _realPal.CopyWithPrivateKey(privateKey); + public ICertificatePal CopyWithPrivateKey(ECDiffieHellman privateKey) => _realPal.CopyWithPrivateKey(privateKey); + public PolicyData GetPolicyData() => _realPal.GetPolicyData(); } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12CertLoader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12CertLoader.cs new file mode 100644 index 0000000000000..ac420ca1c2423 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/ApplePkcs12CertLoader.cs @@ -0,0 +1,47 @@ +// 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.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + 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()) + { + AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert!; + collection.Add(new X509Certificate2(AppleCertificatePal.ImportPkcs12(certAndKey))); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.ExportPal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.ExportPal.cs deleted file mode 100644 index 7dac40ebfc901..0000000000000 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.ExportPal.cs +++ /dev/null @@ -1,59 +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; -using System.Diagnostics; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using Microsoft.Win32.SafeHandles; - -namespace Internal.Cryptography.Pal -{ - internal sealed partial class StorePal - { - private sealed class AppleCertificateExporter : UnixExportProvider - { - public AppleCertificateExporter(ICertificatePalCore cert) - : base(cert) - { - } - - public AppleCertificateExporter(X509Certificate2Collection certs) - : base(certs) - { - } - - protected override byte[] ExportPkcs7() - { - throw new CryptographicException( - SR.Cryptography_X509_PKCS7_Unsupported, - new PlatformNotSupportedException(SR.Cryptography_X509_PKCS7_Unsupported)); - } - - protected override byte[] ExportPkcs8(ICertificatePalCore certificatePal, ReadOnlySpan password) - { - Debug.Assert(certificatePal.HasPrivateKey); - AppleCertificatePal pal = (AppleCertificatePal)certificatePal; - AsymmetricAlgorithm algorithm; - - switch (pal.KeyAlgorithm) - { - case Oids.Rsa: - algorithm = pal.GetRSAPrivateKey()!; - break; - case Oids.EcPublicKey: - algorithm = pal.GetECDsaPrivateKey()!; - break; - case Oids.Dsa: - default: - throw new CryptographicException(SR.Format(SR.Cryptography_UnknownKeyAlgorithm, pal.KeyAlgorithm)); - }; - - using (algorithm) - { - return algorithm.ExportEncryptedPkcs8PrivateKey(password, s_windowsPbe); - } - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.LoaderPal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.LoaderPal.cs deleted file mode 100644 index ec830c82087aa..0000000000000 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.LoaderPal.cs +++ /dev/null @@ -1,115 +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; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Apple; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using Microsoft.Win32.SafeHandles; - -namespace Internal.Cryptography.Pal -{ - internal sealed partial class StorePal - { - private sealed class AppleCertLoader : ILoaderPal - { - private readonly SafeCFArrayHandle _collectionHandle; - - public AppleCertLoader(SafeCFArrayHandle collectionHandle) - { - _collectionHandle = collectionHandle; - } - - public void Dispose() - { - _collectionHandle.Dispose(); - } - - public void MoveTo(X509Certificate2Collection collection) - { - long longCount = Interop.CoreFoundation.CFArrayGetCount(_collectionHandle); - - if (longCount > int.MaxValue) - throw new CryptographicException(); - - int count = (int)longCount; - - // Apple returns things in the opposite order from Windows, so read backwards. - for (int i = count - 1; i >= 0; i--) - { - IntPtr handle = Interop.CoreFoundation.CFArrayGetValueAtIndex(_collectionHandle, i); - - if (handle != IntPtr.Zero) - { - ICertificatePal? certPal = CertificatePal.FromHandle(handle, throwOnFail: false); - - if (certPal != null) - { - X509Certificate2 cert = new X509Certificate2(certPal); - collection.Add(cert); - } - } - } - } - } - - private sealed class ApplePemCertLoader : ILoaderPal - { - private readonly List _collection; - - public ApplePemCertLoader(List collection) - { - _collection = collection; - } - - public void Dispose() - { - } - - public void MoveTo(X509Certificate2Collection collection) - { - foreach (ICertificatePal pal in _collection) - { - X509Certificate2 cert = new X509Certificate2(pal); - collection.Add(cert); - } - } - } - - private 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()) - { - AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert!; - collection.Add(new X509Certificate2(AppleCertificatePal.ImportPkcs12(certAndKey))); - } - } - } - } -} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs index 59eba05d67508..ad178cc9ff981 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs @@ -21,20 +21,20 @@ public static IStorePal FromHandle(IntPtr storeHandle) public static ILoaderPal FromBlob(ReadOnlySpan rawData, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { - List? pemCerts = null; + List? certificateList = null; AppleCertificatePal.TryDecodePem( rawData, derData => { - pemCerts = pemCerts ?? new List(); - pemCerts.Add(AppleCertificatePal.FromBlob(derData, password, keyStorageFlags)); + certificateList = certificateList ?? new List(); + certificateList.Add(AppleCertificatePal.FromBlob(derData, password, keyStorageFlags)); return true; }); - if (pemCerts != null) + if (certificateList != null) { - return new ApplePemCertLoader(pemCerts); + return new CertCollectionLoader(certificateList); } X509ContentType contentType = AppleCertificatePal.GetDerCertContentType(rawData); @@ -67,7 +67,34 @@ public static ILoaderPal FromBlob(ReadOnlySpan rawData, SafePasswordHandle contentType, password); - return new AppleCertLoader(certs); + using (certs) + { + long longCount = Interop.CoreFoundation.CFArrayGetCount(certs); + + if (longCount > int.MaxValue) + throw new CryptographicException(); + + int count = (int)longCount; + + // Apple returns things in the opposite order from Windows, so read backwards. + certificateList = new List(count); + for (int i = count - 1; i >= 0; i--) + { + IntPtr handle = Interop.CoreFoundation.CFArrayGetValueAtIndex(certs, i); + + if (handle != IntPtr.Zero) + { + ICertificatePal? certPal = CertificatePal.FromHandle(handle, throwOnFail: false); + + if (certPal != null) + { + certificateList.Add(certPal); + } + } + } + } + + return new CertCollectionLoader(certificateList); } public static ILoaderPal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 9dbc9c14afa98..bfcf1d071dddd 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -615,20 +615,21 @@ Link="Common\System\Security\Cryptography\EccSecurityTransforms.iOS.cs" /> + + - - + From 510e3a32b8de68efc26c09c7f1f52859bcd1dbc7 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Fri, 7 May 2021 10:57:03 +0200 Subject: [PATCH 17/27] Split X509Pal into partial files and reuse them on iOS --- .../Cryptography/Pal.OSX/X509Pal.ECKey.cs | 63 +++++++++++++++++++ .../Cryptography/Pal.OSX/X509Pal.X500Name.cs | 34 ++++++++++ .../Internal/Cryptography/Pal.OSX/X509Pal.cs | 61 ------------------ .../Internal/Cryptography/Pal.iOS/X509Pal.cs | 61 ------------------ ...urity.Cryptography.X509Certificates.csproj | 4 ++ 5 files changed, 101 insertions(+), 122 deletions(-) create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.ECKey.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.X500Name.cs diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.ECKey.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.ECKey.cs new file mode 100644 index 0000000000000..d4295ada4b50d --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.ECKey.cs @@ -0,0 +1,63 @@ +// 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.Buffers; +using System.Diagnostics; +using System.Formats.Asn1; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using System.Security.Cryptography.Asn1; +using System.Security.Cryptography.Asn1.Pkcs12; +using System.Security.Cryptography.X509Certificates; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class X509Pal + { + private sealed partial class AppleX509Pal : ManagedX509ExtensionProcessor, IX509Pal + { + public ECDsa DecodeECDsaPublicKey(ICertificatePal? certificatePal) + { + return new ECDsaImplementation.ECDsaSecurityTransforms(DecodeECPublicKey(certificatePal)); + } + + public ECDiffieHellman DecodeECDiffieHellmanPublicKey(ICertificatePal? certificatePal) + { + return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(DecodeECPublicKey(certificatePal)); + } + + private static SafeSecKeyRefHandle DecodeECPublicKey(ICertificatePal? certificatePal) + { + const int errSecInvalidKeyRef = -67712; + const int errSecUnsupportedKeySize = -67735; + + if (certificatePal is null) + throw new NotSupportedException(SR.NotSupported_KeyAlgorithm); + + AppleCertificatePal applePal = (AppleCertificatePal)certificatePal; + SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPublicKey(applePal.CertificateHandle); + + // If X509GetPublicKey uses the new SecCertificateCopyKey API it can return an invalid + // key reference for unsupported algorithms. This currently happens for the BrainpoolP160r1 + // algorithm in the test suite (as of macOS Mojave Developer Preview 4). + if (key.IsInvalid) + { + throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecInvalidKeyRef); + } + // EccGetKeySizeInBits can fail for two reasons. First, the Apple implementation has changed + // and we receive values from API that were not previously handled. In that case the + // implementation will need to be adjusted to handle these values. Second, we deliberately + // return 0 from the native code to prevent hitting buggy API implementations in Apple code + // later. + if (Interop.AppleCrypto.EccGetKeySizeInBits(key) == 0) + { + key.Dispose(); + throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnsupportedKeySize); + } + + return key; + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.X500Name.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.X500Name.cs new file mode 100644 index 0000000000000..0bcaae2ab5fc5 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.X500Name.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. + +using System; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class X509Pal + { + private sealed partial class AppleX509Pal : ManagedX509ExtensionProcessor, IX509Pal + { + public string X500DistinguishedNameDecode(byte[] encodedDistinguishedName, X500DistinguishedNameFlags flag) + { + return X500NameEncoder.X500DistinguishedNameDecode(encodedDistinguishedName, true, flag); + } + + public byte[] X500DistinguishedNameEncode(string distinguishedName, X500DistinguishedNameFlags flag) + { + return X500NameEncoder.X500DistinguishedNameEncode(distinguishedName, flag); + } + + public string X500DistinguishedNameFormat(byte[] encodedDistinguishedName, bool multiLine) + { + return X500NameEncoder.X500DistinguishedNameDecode( + encodedDistinguishedName, + true, + multiLine ? X500DistinguishedNameFlags.UseNewLines : X500DistinguishedNameFlags.None, + multiLine); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs index c730b7ff5b6f7..2b654b2b3b9c2 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs @@ -23,16 +23,6 @@ private X509Pal() private sealed partial class AppleX509Pal : ManagedX509ExtensionProcessor, IX509Pal { - public ECDsa DecodeECDsaPublicKey(ICertificatePal? certificatePal) - { - return new ECDsaImplementation.ECDsaSecurityTransforms(DecodeECPublicKey(certificatePal)); - } - - public ECDiffieHellman DecodeECDiffieHellmanPublicKey(ICertificatePal? certificatePal) - { - return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(DecodeECPublicKey(certificatePal)); - } - public AsymmetricAlgorithm DecodePublicKey(Oid oid, byte[] encodedKeyValue, byte[] encodedParameters, ICertificatePal? certificatePal) { @@ -72,38 +62,6 @@ public AsymmetricAlgorithm DecodePublicKey(Oid oid, byte[] encodedKeyValue, byte throw new NotSupportedException(SR.NotSupported_KeyAlgorithm); } - private static SafeSecKeyRefHandle DecodeECPublicKey(ICertificatePal? certificatePal) - { - const int errSecInvalidKeyRef = -67712; - const int errSecUnsupportedKeySize = -67735; - - if (certificatePal is null) - throw new NotSupportedException(SR.NotSupported_KeyAlgorithm); - - AppleCertificatePal applePal = (AppleCertificatePal)certificatePal; - SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPublicKey(applePal.CertificateHandle); - - // If X509GetPublicKey uses the new SecCertificateCopyKey API it can return an invalid - // key reference for unsupported algorithms. This currently happens for the BrainpoolP160r1 - // algorithm in the test suite (as of macOS Mojave Developer Preview 4). - if (key.IsInvalid) - { - throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecInvalidKeyRef); - } - // EccGetKeySizeInBits can fail for two reasons. First, the Apple implementation has changed - // and we receive values from API that were not previously handled. In that case the - // implementation will need to be adjusted to handle these values. Second, we deliberately - // return 0 from the native code to prevent hitting buggy API implementations in Apple code - // later. - if (Interop.AppleCrypto.EccGetKeySizeInBits(key) == 0) - { - key.Dispose(); - throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnsupportedKeySize); - } - - return key; - } - private static AsymmetricAlgorithm DecodeRsaPublicKey(byte[] encodedKeyValue) { RSA rsa = RSA.Create(); @@ -154,25 +112,6 @@ private static AsymmetricAlgorithm DecodeDsaPublicKey(byte[] encodedKeyValue, by } } - public string X500DistinguishedNameDecode(byte[] encodedDistinguishedName, X500DistinguishedNameFlags flag) - { - return X500NameEncoder.X500DistinguishedNameDecode(encodedDistinguishedName, true, flag); - } - - public byte[] X500DistinguishedNameEncode(string distinguishedName, X500DistinguishedNameFlags flag) - { - return X500NameEncoder.X500DistinguishedNameEncode(distinguishedName, flag); - } - - public string X500DistinguishedNameFormat(byte[] encodedDistinguishedName, bool multiLine) - { - return X500NameEncoder.X500DistinguishedNameDecode( - encodedDistinguishedName, - true, - multiLine ? X500DistinguishedNameFlags.UseNewLines : X500DistinguishedNameFlags.None, - multiLine); - } - public X509ContentType GetCertContentType(ReadOnlySpan rawData) { const int errSecUnknownFormat = -25257; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs index f7b97f0646812..a8710ee926740 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs @@ -21,16 +21,6 @@ private X509Pal() private sealed partial class AppleX509Pal : ManagedX509ExtensionProcessor, IX509Pal { - public ECDsa DecodeECDsaPublicKey(ICertificatePal? certificatePal) - { - return new ECDsaImplementation.ECDsaSecurityTransforms(DecodeECPublicKey(certificatePal)); - } - - public ECDiffieHellman DecodeECDiffieHellmanPublicKey(ICertificatePal? certificatePal) - { - return new ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms(DecodeECPublicKey(certificatePal)); - } - public AsymmetricAlgorithm DecodePublicKey(Oid oid, byte[] encodedKeyValue, byte[] encodedParameters, ICertificatePal? certificatePal) { @@ -61,57 +51,6 @@ public AsymmetricAlgorithm DecodePublicKey(Oid oid, byte[] encodedKeyValue, byte } } - private static SafeSecKeyRefHandle DecodeECPublicKey(ICertificatePal? certificatePal) - { - const int errSecInvalidKeyRef = -67712; - const int errSecUnsupportedKeySize = -67735; - - if (certificatePal is null) - throw new NotSupportedException(SR.NotSupported_KeyAlgorithm); - - AppleCertificatePal applePal = (AppleCertificatePal)certificatePal; - SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPublicKey(applePal.CertificateHandle); - - // If X509GetPublicKey uses the new SecCertificateCopyKey API it can return an invalid - // key reference for unsupported algorithms. This currently happens for the BrainpoolP160r1 - // algorithm in the test suite (as of macOS Mojave Developer Preview 4). - if (key.IsInvalid) - { - throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecInvalidKeyRef); - } - // EccGetKeySizeInBits can fail for two reasons. First, the Apple implementation has changed - // and we receive values from API that were not previously handled. In that case the - // implementation will need to be adjusted to handle these values. Second, we deliberately - // return 0 from the native code to prevent hitting buggy API implementations in Apple code - // later. - if (Interop.AppleCrypto.EccGetKeySizeInBits(key) == 0) - { - key.Dispose(); - throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnsupportedKeySize); - } - - return key; - } - - public string X500DistinguishedNameDecode(byte[] encodedDistinguishedName, X500DistinguishedNameFlags flag) - { - return X500NameEncoder.X500DistinguishedNameDecode(encodedDistinguishedName, true, flag); - } - - public byte[] X500DistinguishedNameEncode(string distinguishedName, X500DistinguishedNameFlags flag) - { - return X500NameEncoder.X500DistinguishedNameEncode(distinguishedName, flag); - } - - public string X500DistinguishedNameFormat(byte[] encodedDistinguishedName, bool multiLine) - { - return X500NameEncoder.X500DistinguishedNameDecode( - encodedDistinguishedName, - true, - multiLine ? X500DistinguishedNameFlags.UseNewLines : X500DistinguishedNameFlags.None, - multiLine); - } - public X509ContentType GetCertContentType(ReadOnlySpan rawData) { const int errSecUnknownFormat = -25257; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index bfcf1d071dddd..0165790fc1093 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -605,6 +605,8 @@ + + + + From 35df973119ce974fb2a4c1f1734f958f7c585abb Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Fri, 7 May 2021 11:19:54 +0200 Subject: [PATCH 18/27] Reuse TempExportPal --- .../Pal.OSX/AppleCertificatePal.Pkcs12.cs | 35 ----------- .../AppleCertificatePal.TempExportPal.cs | 49 ++++++++++++++++ .../Pal.iOS/AppleCertificateExporter.cs | 13 +++++ .../Pal.iOS/AppleCertificatePal.Pkcs12.cs | 58 +------------------ ...urity.Cryptography.X509Certificates.csproj | 2 + 5 files changed, 65 insertions(+), 92 deletions(-) create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.TempExportPal.cs diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Pkcs12.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Pkcs12.cs index cd4a1e8526f14..4febd7080f082 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Pkcs12.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.Pkcs12.cs @@ -108,40 +108,5 @@ protected override byte[] ExportPkcs8(ICertificatePalCore certificatePal, ReadOn return AppleCertificatePal.ExportPkcs8(_privateKey, password); } } - - private sealed class TempExportPal : ICertificatePalCore - { - private readonly ICertificatePal _realPal; - - internal TempExportPal(AppleCertificatePal realPal) - { - _realPal = realPal; - } - - public bool HasPrivateKey => true; - - public void Dispose() - { - // No-op. - } - - // Forwarders to make the interface compliant. - public IntPtr Handle => _realPal.Handle; - public string Issuer => _realPal.Issuer; - public string Subject => _realPal.Subject; - public string LegacyIssuer => _realPal.LegacyIssuer; - public string LegacySubject => _realPal.LegacySubject; - public byte[] Thumbprint => _realPal.Thumbprint; - public string KeyAlgorithm => _realPal.KeyAlgorithm; - public byte[] KeyAlgorithmParameters => _realPal.KeyAlgorithmParameters; - public byte[] PublicKeyValue => _realPal.PublicKeyValue; - public byte[] SerialNumber => _realPal.SerialNumber; - public string SignatureAlgorithm => _realPal.SignatureAlgorithm; - public DateTime NotAfter => _realPal.NotAfter; - public DateTime NotBefore => _realPal.NotBefore; - public byte[] RawData => _realPal.RawData; - public byte[] Export(X509ContentType contentType, SafePasswordHandle password) => - _realPal.Export(contentType, password); - } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.TempExportPal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.TempExportPal.cs new file mode 100644 index 0000000000000..58553f50307b0 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/AppleCertificatePal.TempExportPal.cs @@ -0,0 +1,49 @@ +// 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.Security.Cryptography.Apple; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class AppleCertificatePal : ICertificatePal + { + private sealed class TempExportPal : ICertificatePalCore + { + private readonly ICertificatePal _realPal; + + internal TempExportPal(AppleCertificatePal realPal) + { + _realPal = realPal; + } + + public bool HasPrivateKey => true; + + public void Dispose() + { + // No-op. + } + + // Forwarders to make the interface compliant. + public IntPtr Handle => _realPal.Handle; + public string Issuer => _realPal.Issuer; + public string Subject => _realPal.Subject; + public string LegacyIssuer => _realPal.LegacyIssuer; + public string LegacySubject => _realPal.LegacySubject; + public byte[] Thumbprint => _realPal.Thumbprint; + public string KeyAlgorithm => _realPal.KeyAlgorithm; + public byte[] KeyAlgorithmParameters => _realPal.KeyAlgorithmParameters; + public byte[] PublicKeyValue => _realPal.PublicKeyValue; + public byte[] SerialNumber => _realPal.SerialNumber; + public string SignatureAlgorithm => _realPal.SignatureAlgorithm; + public DateTime NotAfter => _realPal.NotAfter; + public DateTime NotBefore => _realPal.NotBefore; + public byte[] RawData => _realPal.RawData; + public byte[] Export(X509ContentType contentType, SafePasswordHandle password) => + _realPal.Export(contentType, password); + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificateExporter.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificateExporter.cs index 45fa8f05b93a6..54bf659a1fcca 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificateExporter.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificateExporter.cs @@ -11,11 +11,19 @@ namespace Internal.Cryptography.Pal { internal sealed class AppleCertificateExporter : UnixExportProvider { + private AsymmetricAlgorithm? _privateKey; + public AppleCertificateExporter(ICertificatePalCore cert) : base(cert) { } + public AppleCertificateExporter(ICertificatePalCore cert, AsymmetricAlgorithm privateKey) + : base(cert) + { + _privateKey = privateKey; + } + public AppleCertificateExporter(X509Certificate2Collection certs) : base(certs) { @@ -30,6 +38,11 @@ protected override byte[] ExportPkcs7() protected override byte[] ExportPkcs8(ICertificatePalCore certificatePal, ReadOnlySpan password) { + if (_privateKey != null) + { + return _privateKey.ExportEncryptedPkcs8PrivateKey(password, s_windowsPbe); + } + Debug.Assert(certificatePal.HasPrivateKey); ICertificatePal pal = (ICertificatePal)certificatePal; AsymmetricAlgorithm algorithm; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs index 467735d8da92b..87e45b117e836 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pkcs12.cs @@ -37,7 +37,7 @@ internal static AppleCertificatePal ImportPkcs12(UnixPkcs12Reader.CertAndKey cer if (certAndKey.Key != null) { - AppleCertificateExporter exporter = new AppleCertificateExporter(new TempExportPal(pal, certAndKey.Key)); + AppleCertificateExporter exporter = new AppleCertificateExporter(new TempExportPal(pal), certAndKey.Key); byte[] smallPfx = exporter.Export(X509ContentType.Pkcs12, s_passwordExportHandle)!; SafeSecIdentityHandle identityHandle; @@ -59,61 +59,5 @@ internal static AppleCertificatePal ImportPkcs12(UnixPkcs12Reader.CertAndKey cer return pal; } - - private sealed class TempExportPal : ICertificatePal - { - private readonly ICertificatePal _realPal; - private readonly AsymmetricAlgorithm _privateKey; - - internal TempExportPal(AppleCertificatePal realPal, AsymmetricAlgorithm privateKey) - { - Debug.Assert(privateKey != null); - _realPal = realPal; - _privateKey = privateKey; - } - - public bool HasPrivateKey => true; - public RSA? GetRSAPrivateKey() => _privateKey as RSA; - public DSA? GetDSAPrivateKey() => _privateKey as DSA; - public ECDsa? GetECDsaPrivateKey() => _privateKey as ECDsa; - public ECDiffieHellman? GetECDiffieHellmanPrivateKey() => _privateKey as ECDiffieHellman; - - public void Dispose() - { - // No-op. - } - - // Forwarders to make the interface compliant. - public IntPtr Handle => _realPal.Handle; - public string Issuer => _realPal.Issuer; - public string Subject => _realPal.Subject; - public string LegacyIssuer => _realPal.LegacyIssuer; - public string LegacySubject => _realPal.LegacySubject; - public byte[] Thumbprint => _realPal.Thumbprint; - public string KeyAlgorithm => _realPal.KeyAlgorithm; - public byte[] KeyAlgorithmParameters => _realPal.KeyAlgorithmParameters; - public byte[] PublicKeyValue => _realPal.PublicKeyValue; - public byte[] SerialNumber => _realPal.SerialNumber; - public string SignatureAlgorithm => _realPal.SignatureAlgorithm; - public DateTime NotAfter => _realPal.NotAfter; - public DateTime NotBefore => _realPal.NotBefore; - public byte[] RawData => _realPal.RawData; - public byte[] Export(X509ContentType contentType, SafePasswordHandle password) => - _realPal.Export(contentType, password); - - public int Version => _realPal.Version; - public bool Archived { get => _realPal.Archived; set => _realPal.Archived = value; } - public string FriendlyName { get => _realPal.FriendlyName; set => _realPal.FriendlyName = value; } - public X500DistinguishedName SubjectName => _realPal.SubjectName; - public X500DistinguishedName IssuerName => _realPal.IssuerName; - public IEnumerable Extensions => _realPal.Extensions; - public string GetNameInfo(X509NameType nameType, bool forIssuer) => _realPal.GetNameInfo(nameType, forIssuer); - public void AppendPrivateKeyInfo(StringBuilder sb) => _realPal.AppendPrivateKeyInfo(sb); - public ICertificatePal CopyWithPrivateKey(DSA privateKey) => _realPal.CopyWithPrivateKey(privateKey); - public ICertificatePal CopyWithPrivateKey(ECDsa privateKey) => _realPal.CopyWithPrivateKey(privateKey); - public ICertificatePal CopyWithPrivateKey(RSA privateKey) => _realPal.CopyWithPrivateKey(privateKey); - public ICertificatePal CopyWithPrivateKey(ECDiffieHellman privateKey) => _realPal.CopyWithPrivateKey(privateKey); - public PolicyData GetPolicyData() => _realPal.GetPolicyData(); - } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 0165790fc1093..1cccca90ef289 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -595,6 +595,7 @@ + @@ -623,6 +624,7 @@ + From dd2dd09cf65f79ed618dae54a3ee1412cd7eed7e Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Fri, 7 May 2021 11:37:08 +0200 Subject: [PATCH 19/27] Update comments --- .../Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs | 5 ++++- .../Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs index 04b445699ed4a..e2a5e9c9b42bb 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs @@ -113,7 +113,10 @@ private static ICertificatePal FromDerBlob( if (contentType == X509ContentType.Pkcs12) { - // TODO: keyStorageFlags + // TODO: + // We ignore keyStorageFlags which is tracked in https://github.com/dotnet/runtime/issues/52434. + // The keys are always imported as ephemeral and never persisted. Exportability is ignored for + // the moment and it needs to be investigated how to map it to iOS keychain primitives. return ImportPkcs12(rawData, password); } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs index dea33f0d90410..00159e5e7c89e 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs @@ -13,6 +13,7 @@ namespace Internal.Cryptography.Pal { internal sealed partial class AppleCertificatePal : ICertificatePal { + // Byte representation of "-----BEGIN " private static byte[] pemBegin = new byte[] { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20 }; internal delegate bool DerCallback(ReadOnlySpan derData); @@ -26,6 +27,9 @@ internal static bool TryDecodePem(ReadOnlySpan rawData, DerCallback derCal return false; } + // Look for the PEM marker. This doesn't guarantee it will be a valid PEM since we don't check whether + // the marker is at the beginning of line or whether the line is a complete marker. It's just a quick + // check to avoid conversion from bytes to characters if the content is DER encoded. if (rawData.IndexOf(pemBegin) < 0) { return false; From d71b4b5493e3348c4e6d8129cbe045ad8ec9aa85 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Fri, 7 May 2021 15:59:21 +0200 Subject: [PATCH 20/27] Group shared files in .csproj --- ...urity.Cryptography.X509Certificates.csproj | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 1cccca90ef289..58fd788578721 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -573,6 +573,13 @@ Common\System\Security\Cryptography\Asn1\SpecifiedECDomain.xml + + + + + + + - - - - - - - - - - - - - - From c7c1f2bcb45401126368aebe8a9fa7b2d28b9203 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Fri, 7 May 2021 15:59:44 +0200 Subject: [PATCH 21/27] Update excluded tests --- src/libraries/tests.proj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 66358822d62cd..ca0c1686f4c61 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -118,7 +118,6 @@ - @@ -137,10 +136,8 @@ - - From f1c1999b8da50b0ac5f0321294afe2cd2ba94ae3 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 8 May 2021 11:38:07 +0200 Subject: [PATCH 22/27] Revert test exclusion --- src/libraries/tests.proj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index ca0c1686f4c61..2fe79b522ba11 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -118,6 +118,7 @@ + From ce15038efc2bc3c446920359b50577b0088abf01 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 8 May 2021 11:43:13 +0200 Subject: [PATCH 23/27] Improve implementation of AppleCryptoNative_X509GetPublicKey on Mac Catalyst --- .../pal_x509.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c index 4029b18680fa9..7d3e61f4b5d57 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c @@ -6,9 +6,11 @@ #include #include +#if !defined(TARGET_MACCATALYST) static pthread_once_t once = PTHREAD_ONCE_INIT; static SecKeyRef (*secCertificateCopyKey)(SecCertificateRef); static OSStatus (*secCertificateCopyPublicKey)(SecCertificateRef, SecKeyRef*); +#endif int32_t AppleCryptoNative_X509DemuxAndRetainHandle(CFTypeRef handle, SecCertificateRef* pCertOut, SecIdentityRef* pIdentityOut) @@ -40,6 +42,7 @@ AppleCryptoNative_X509DemuxAndRetainHandle(CFTypeRef handle, SecCertificateRef* return 1; } +#if !defined(TARGET_MACCATALYST) static void InitCertificateCopy() { #if defined(TARGET_IOS) || defined(TARGET_TVOS) @@ -54,6 +57,7 @@ static void InitCertificateCopy() secCertificateCopyPublicKey = (OSStatus (*)(SecCertificateRef, SecKeyRef*))dlsym(RTLD_DEFAULT, "SecCertificateCopyPublicKey"); #endif } +#endif int32_t AppleCryptoNative_X509GetPublicKey(SecCertificateRef cert, SecKeyRef* pPublicKeyOut, int32_t* pOSStatusOut) @@ -66,7 +70,8 @@ AppleCryptoNative_X509GetPublicKey(SecCertificateRef cert, SecKeyRef* pPublicKey if (cert == NULL || pPublicKeyOut == NULL || pOSStatusOut == NULL) return kErrorUnknownState; - pthread_once (&once, InitCertificateCopy); +#if !defined(TARGET_MACCATALYST) + pthread_once(&once, InitCertificateCopy); // SecCertificateCopyPublicKey was deprecated in 10.14, so use SecCertificateCopyKey on the systems that have it (10.14+), // and SecCertificateCopyPublicKey on the systems that don’t. if (secCertificateCopyKey != NULL) @@ -82,6 +87,10 @@ AppleCryptoNative_X509GetPublicKey(SecCertificateRef cert, SecKeyRef* pPublicKey return kErrorBadInput; } return (*pOSStatusOut == noErr); +#else + *pPublicKeyOut = SecCertificateCopyKey(cert); + return 1; +#endif } PAL_X509ContentType AppleCryptoNative_X509GetContentType(uint8_t* pbData, int32_t cbData) From fa57851cd20373a720eefeb363083ac1ad6bac19 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 12 May 2021 06:38:31 +0200 Subject: [PATCH 24/27] Address feedback --- .../Security/Cryptography/PlatformSupport.cs | 1 + .../CMakeLists.txt | 16 +++++------ .../AppleCertificatePal.ImportExport.cs | 11 ++++---- .../Pal.iOS/AppleCertificatePal.Pem.cs | 10 +++++-- .../Internal/Cryptography/Pal.iOS/StorePal.cs | 4 +-- .../Internal/Cryptography/Pal.iOS/X509Pal.cs | 4 +-- .../PrivateKeyAssociationTests.cs | 2 +- .../tests/ChainTests.cs | 2 +- .../tests/CollectionImportTests.cs | 28 +++++++++---------- .../tests/CollectionTests.cs | 4 +-- .../tests/FindTests.cs | 2 +- .../tests/PfxTests.cs | 4 +-- .../tests/PublicKeyTests.cs | 16 +++++------ .../tests/RevocationTests/AiaTests.cs | 2 +- .../tests/X509Certificate2PemTests.cs | 4 +-- 15 files changed, 56 insertions(+), 54 deletions(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/PlatformSupport.cs b/src/libraries/Common/tests/System/Security/Cryptography/PlatformSupport.cs index 2885d74cd639a..9d1bf1a06a73d 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/PlatformSupport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/PlatformSupport.cs @@ -10,6 +10,7 @@ internal static class PlatformSupport { // Platforms that use Apple Cryptography internal const TestPlatforms AppleCrypto = TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst; + internal const TestPlatforms MobileAppleCrypto = TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst; // Platforms that support OpenSSL - all Unix except OSX/iOS/tvOS/MacCatalyst and Android internal const TestPlatforms OpenSSL = TestPlatforms.AnyUnix & ~(AppleCrypto | TestPlatforms.Android); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt index a0d7bfb8fb7b9..a9029ec629068 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt @@ -21,7 +21,13 @@ set(NATIVECRYPTO_SOURCES pal_x509chain.c ) -if (NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CMAKE_TARGET_TVOS) +if (CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) + set(NATIVECRYPTO_SOURCES + ${NATIVECRYPTO_SOURCES} + pal_keychain_ios.c + pal_x509_ios.c + ) +else() set(NATIVECRYPTO_SOURCES ${NATIVECRYPTO_SOURCES} pal_keychain_macos.c @@ -32,14 +38,6 @@ if (NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CM ) endif() -if (CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) - set(NATIVECRYPTO_SOURCES - ${NATIVECRYPTO_SOURCES} - pal_keychain_ios.c - pal_x509_ios.c - ) -endif() - if (CLR_CMAKE_TARGET_MACCATALYST) add_definitions(-DTARGET_MACCATALYST) endif() diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs index e2a5e9c9b42bb..dfdb5b554f4a7 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.ImportExport.cs @@ -95,15 +95,14 @@ internal static X509ContentType GetDerCertContentType(ReadOnlySpan rawData return contentType; } - private static ICertificatePal FromDerBlob( + internal static ICertificatePal FromDerBlob( ReadOnlySpan rawData, + X509ContentType contentType, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { Debug.Assert(password != null); - X509ContentType contentType = GetDerCertContentType(rawData); - if (contentType == X509ContentType.Pkcs7) { throw new CryptographicException( @@ -150,13 +149,13 @@ public static ICertificatePal FromBlob( ICertificatePal? result = null; TryDecodePem( rawData, - derData => + (derData, contentType) => { - result = FromDerBlob(derData, password, keyStorageFlags); + result = FromDerBlob(derData, contentType, password, keyStorageFlags); return false; }); - return result ?? FromDerBlob(rawData, password, keyStorageFlags); + return result ?? FromDerBlob(rawData, GetDerCertContentType(rawData), password, keyStorageFlags); } public void DisposeTempKeychain() diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs index 00159e5e7c89e..254a0dad8ca07 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs @@ -16,7 +16,7 @@ internal sealed partial class AppleCertificatePal : ICertificatePal // Byte representation of "-----BEGIN " private static byte[] pemBegin = new byte[] { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20 }; - internal delegate bool DerCallback(ReadOnlySpan derData); + internal delegate bool DerCallback(ReadOnlySpan derData, X509ContentType contentType); internal static bool TryDecodePem(ReadOnlySpan rawData, DerCallback derCallback) { @@ -57,7 +57,11 @@ internal static bool TryDecodePem(ReadOnlySpan rawData, DerCallback derCal throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); } - bool cont = derCallback(certBytes.AsSpan(0, bytesWritten)); + X509ContentType contentType = + label.SequenceEqual(PemLabels.X509Certificate) ? + X509ContentType.Cert : + X509ContentType.Pkcs7; + bool cont = derCallback(certBytes.AsSpan(0, bytesWritten), contentType); CryptoPool.Return(certBytes, clearSize: 0); certBytes = null; @@ -71,7 +75,7 @@ internal static bool TryDecodePem(ReadOnlySpan rawData, DerCallback derCal } finally { - ArrayPool.Shared.Return(certPem); + ArrayPool.Shared.Return(certPem, clearArray: true); if (certBytes != null) { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs index ad178cc9ff981..efb7d68349a00 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/StorePal.cs @@ -25,10 +25,10 @@ public static ILoaderPal FromBlob(ReadOnlySpan rawData, SafePasswordHandle AppleCertificatePal.TryDecodePem( rawData, - derData => + (derData, contentType) => { certificateList = certificateList ?? new List(); - certificateList.Add(AppleCertificatePal.FromBlob(derData, password, keyStorageFlags)); + certificateList.Add(AppleCertificatePal.FromDerBlob(derData, contentType, password, keyStorageFlags)); return true; }); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs index a8710ee926740..283bd38c066dc 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/X509Pal.cs @@ -65,9 +65,9 @@ public X509ContentType GetCertContentType(ReadOnlySpan rawData) AppleCertificatePal.TryDecodePem( rawData, - derData => + (derData, contentType) => { - result = AppleCertificatePal.GetDerCertContentType(derData); + result = contentType; return false; }); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertificateCreation/PrivateKeyAssociationTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertificateCreation/PrivateKeyAssociationTests.cs index c98a473b3cd60..6763e700d6984 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertificateCreation/PrivateKeyAssociationTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertificateCreation/PrivateKeyAssociationTests.cs @@ -426,7 +426,7 @@ public static void AssociatePersistedKey_CNG_DSA() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void ThirdPartyProvider_DSA() { using (DSA dsaOther = new DSAOther()) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs index 52fa5ffce847f..74d7f6f150431 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs @@ -636,7 +636,7 @@ public static void BuildChain_FailOnlyApplicationPolicy() [ConditionalFact(nameof(TrustsMicrosoftDotComRoot))] [OuterLoop(/* Modifies user certificate store */)] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] public static void BuildChain_MicrosoftDotCom_WithRootCertInUserAndSystemRootCertStores() { // Verifies that when the same root cert is placed in both a user and machine root certificate store, diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs index cbd7de947f9ee..df9554a322699 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs @@ -69,7 +69,7 @@ public static void ImportX509PemFile() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7DerBytes_Empty() { using (ImportedCollection ic = Cert.Import(TestData.Pkcs7EmptyDerBytes)) @@ -80,7 +80,7 @@ public static void ImportPkcs7DerBytes_Empty() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7PemBytes_Empty() { using (ImportedCollection ic = Cert.Import(TestData.Pkcs7EmptyPemBytes)) @@ -91,7 +91,7 @@ public static void ImportPkcs7PemBytes_Empty() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7DerFile_Empty() { using (ImportedCollection ic = Cert.Import(TestFiles.Pkcs7EmptyDerFile)) @@ -102,7 +102,7 @@ public static void ImportPkcs7DerFile_Empty() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7PemFile_Empty() { using (ImportedCollection ic = Cert.Import(TestFiles.Pkcs7EmptyPemFile)) @@ -113,7 +113,7 @@ public static void ImportPkcs7PemFile_Empty() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7DerBytes_Single() { using (ImportedCollection ic = Cert.Import(TestData.Pkcs7SingleDerBytes)) @@ -126,7 +126,7 @@ public static void ImportPkcs7DerBytes_Single() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7PemBytes_Single() { using (ImportedCollection ic = Cert.Import(TestData.Pkcs7SinglePemBytes)) @@ -139,7 +139,7 @@ public static void ImportPkcs7PemBytes_Single() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7DerFile_Single() { using (ImportedCollection ic = Cert.Import(TestFiles.Pkcs7SingleDerFile)) @@ -152,7 +152,7 @@ public static void ImportPkcs7DerFile_Single() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7PemFile_Single() { using (ImportedCollection ic = Cert.Import(TestFiles.Pkcs7SinglePemFile)) @@ -165,7 +165,7 @@ public static void ImportPkcs7PemFile_Single() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7DerBytes_Chain() { using (ImportedCollection ic = Cert.Import(TestData.Pkcs7ChainDerBytes)) @@ -176,7 +176,7 @@ public static void ImportPkcs7DerBytes_Chain() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7DerByteSpan_Chain() { using (ImportedCollection ic = Cert.Import(TestData.Pkcs7ChainDerBytes.AsSpan())) @@ -187,7 +187,7 @@ public static void ImportPkcs7DerByteSpan_Chain() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7PemBytes_Chain() { using (ImportedCollection ic = Cert.Import(TestData.Pkcs7ChainPemBytes)) @@ -198,7 +198,7 @@ public static void ImportPkcs7PemBytes_Chain() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7PemByteSpan_Chain() { using (ImportedCollection ic = Cert.Import(TestData.Pkcs7ChainPemBytes.AsSpan())) @@ -209,7 +209,7 @@ public static void ImportPkcs7PemByteSpan_Chain() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7DerFile_Chain() { using (ImportedCollection ic = Cert.Import(TestFiles.Pkcs7ChainDerFile)) @@ -220,7 +220,7 @@ public static void ImportPkcs7DerFile_Chain() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportPkcs7PemFile_Chain() { using (ImportedCollection ic = Cert.Import(TestFiles.Pkcs7ChainPemFile)) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs index f7fc547bd6622..2e29273f3bf90 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs @@ -578,7 +578,7 @@ public static void ImportStoreSavedAsSerializedStoreData_Unix() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 import is not available")] public static void ImportStoreSavedAsPfxData() { using (var msCer = new X509Certificate2(TestData.MsCertificate)) @@ -698,7 +698,7 @@ public static void ExportSerializedStore_Unix() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "PKCS#7 import is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "PKCS#7 export is not available")] public static void ExportPkcs7() { TestExportStore(X509ContentType.Pkcs7); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs index 924cb8ea70bdc..da993b4273619 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs @@ -231,7 +231,7 @@ public static void FindByValidThumbprint_ValidOnly(bool validOnly) } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] public static void FindByValidThumbprint_RootCert() { using (X509Store machineRoot = new X509Store(StoreName.Root, StoreLocation.LocalMachine)) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs index cd83fdc4ae5b1..bd853f1e098be 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs @@ -231,7 +231,7 @@ public static void ECDHPrivateKeyProperty_WindowsPfx() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void DsaPrivateKeyProperty() { using (var cert = new X509Certificate2(TestData.Dsa1024Pfx, TestData.Dsa1024PfxPassword, Cert.EphemeralIfPossible)) @@ -327,7 +327,7 @@ public static void ReadECDsaPrivateKey_OpenSslPfx(X509KeyStorageFlags keyStorage } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void ReadDSAPrivateKey() { byte[] data = { 1, 2, 3, 4, 5 }; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs index 2db85fb176ccb..0f19b3bd35610 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs @@ -105,7 +105,7 @@ public static void TestPublicKey_Key_RSA() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void TestPublicKey_Key_DSA() { PublicKey pk = GetTestDsaKey(); @@ -565,7 +565,7 @@ public static void TestECDsa224PublicKey() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void TestDSAPublicKey() { using (var cert = new X509Certificate2(TestData.DssCer)) @@ -577,7 +577,7 @@ public static void TestDSAPublicKey() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void TestDSAPublicKey_VerifiesSignature() { byte[] data = { 1, 2, 3, 4, 5 }; @@ -597,7 +597,7 @@ public static void TestDSAPublicKey_VerifiesSignature() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void TestDSAPublicKey_RSACert() { using (var cert = new X509Certificate2(TestData.Rsa384CertificatePemBytes)) @@ -608,7 +608,7 @@ public static void TestDSAPublicKey_RSACert() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void TestDSAPublicKey_ECDSACert() { using (var cert = new X509Certificate2(TestData.ECDsa256Certificate)) @@ -669,7 +669,7 @@ public static void ExportSubjectPublicKeyInfo_RSA() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void ExportSubjectPublicKeyInfo_DSA() { using DSA dsa = DSA.Create(); @@ -742,7 +742,7 @@ public static void CreateFromSubjectPublicKeyInfo_Roundtrip_RSA() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void CreateFromSubjectPublicKeyInfo_Roundtrip_DSA() { using DSA dsa = DSA.Create(); @@ -788,7 +788,7 @@ public static void CreateFromSubjectPublicKeyInfo_Roundtrip_ECDH() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void CreateFromSubjectPublicKeyInfo_Roundtrip_DSA_InvalidKey() { // The DSA key is invalid here, but we should be able to round-trip the diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs index 2bcdb38e9e0db..b661c6ce80f00 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs @@ -48,7 +48,7 @@ public static void EmptyAiaResponseIsIgnored() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "CA store is not available on iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "CA store is not available on iOS/tvOS/MacCatalyst")] public static void DisableAiaOptionWorks() { CertificateAuthority.BuildPrivatePki( diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs index 24da8bc73fbcf..c53367a5b0d44 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs @@ -266,7 +266,7 @@ public static void CreateFromPem_ECDH_Pkcs8_Success() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void CreateFromPem_Dsa_Pkcs8_Success() { using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.DsaCertificate, TestData.DsaPkcs8Key)) @@ -277,7 +277,7 @@ public static void CreateFromPem_Dsa_Pkcs8_Success() } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "DSA is not available of iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")] public static void CreateFromPem_Dsa_EncryptedPkcs8_Success() { X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPem( From 847fb06a36fc8638bfbfc26f5be71633defcb099 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 12 May 2021 06:39:15 +0200 Subject: [PATCH 25/27] Address feedback --- .../tests/X509StoreTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs index 4169edd3e4674..195ab0886bbf9 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs @@ -405,7 +405,7 @@ public static void OpenMachineMyStore_NotSupported() [Theory] [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.OSX)] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] [InlineData(OpenFlags.ReadOnly, false)] [InlineData(OpenFlags.MaxAllowed, false)] [InlineData(OpenFlags.ReadWrite, true)] @@ -428,7 +428,7 @@ public static void OpenMachineRootStore_Permissions(OpenFlags permissions, bool } [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] public static void MachineRootStore_NonEmpty() { // This test will fail on systems where the administrator has gone out of their From a75f8591495b4b54be9e98fd539b7f93280e6259 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 12 May 2021 06:40:48 +0200 Subject: [PATCH 26/27] Shorten more SkipOnPlatform messages --- .../tests/ChainTests.cs | 2 +- .../tests/FindTests.cs | 2 +- .../tests/RevocationTests/AiaTests.cs | 2 +- .../tests/X509StoreTests.cs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs index 74d7f6f150431..38b2ac81eaa83 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs @@ -636,7 +636,7 @@ public static void BuildChain_FailOnlyApplicationPolicy() [ConditionalFact(nameof(TrustsMicrosoftDotComRoot))] [OuterLoop(/* Modifies user certificate store */)] - [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible")] public static void BuildChain_MicrosoftDotCom_WithRootCertInUserAndSystemRootCertStores() { // Verifies that when the same root cert is placed in both a user and machine root certificate store, diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs index da993b4273619..53d8f953adba7 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs @@ -231,7 +231,7 @@ public static void FindByValidThumbprint_ValidOnly(bool validOnly) } [Fact] - [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible")] public static void FindByValidThumbprint_RootCert() { using (X509Store machineRoot = new X509Store(StoreName.Root, StoreLocation.LocalMachine)) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs index b661c6ce80f00..9ffe7cd0efb2c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs @@ -48,7 +48,7 @@ public static void EmptyAiaResponseIsIgnored() } [Fact] - [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "CA store is not available on iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "CA store is not available")] public static void DisableAiaOptionWorks() { CertificateAuthority.BuildPrivatePki( diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs index 195ab0886bbf9..a484d323d8d1e 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs @@ -405,7 +405,7 @@ public static void OpenMachineMyStore_NotSupported() [Theory] [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.OSX)] - [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible")] [InlineData(OpenFlags.ReadOnly, false)] [InlineData(OpenFlags.MaxAllowed, false)] [InlineData(OpenFlags.ReadWrite, true)] @@ -428,7 +428,7 @@ public static void OpenMachineRootStore_Permissions(OpenFlags permissions, bool } [Fact] - [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible on iOS/tvOS/MacCatalyst")] + [SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "Root certificate store is not accessible")] public static void MachineRootStore_NonEmpty() { // This test will fail on systems where the administrator has gone out of their From 03d267cbf8e73a498398f993e0e50b78ed57ebb8 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 12 May 2021 16:07:28 +0200 Subject: [PATCH 27/27] Fix build of tests --- .../tests/CollectionImportTests.cs | 1 + .../tests/CollectionTests.cs | 1 + .../tests/FindTests.cs | 2 +- .../tests/RevocationTests/AiaTests.cs | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs index df9554a322699..c2618ee09f17b 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Test.Cryptography; using Xunit; namespace System.Security.Cryptography.X509Certificates.Tests diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs index 2e29273f3bf90..d35662102045c 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Text; +using Test.Cryptography; using Xunit; namespace System.Security.Cryptography.X509Certificates.Tests diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs index 53d8f953adba7..a734da8b711ea 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/FindTests.cs @@ -5,7 +5,7 @@ using System.IO; using System.Linq; using System.Text; - +using Test.Cryptography; using Xunit; namespace System.Security.Cryptography.X509Certificates.Tests diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs index 9ffe7cd0efb2c..0a2e8b78cffce 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Security.Cryptography.X509Certificates.Tests.Common; +using Test.Cryptography; using Xunit; namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests