Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Finish off the X509Certificates implementation on macOS.
Browse files Browse the repository at this point in the history
* Enable add/remove from a keychain-backed store.

* Improve X509KeyStorageFlags usage
  * PersistKeySet will save a PFX key (and certificate) into the default keychain
  * EphemeralKeySet will throw PNSE
  * Exportable is respected for PFX

* Add support for reading the Disallowed trust.
  * This will only read "full deny" behaviors

* Enable or permanent-assign all remaining ActiveIssue(-1) tests.

* Add DSA support to cert.PrivateKey from the Apple PAL
  * And add a test for it
  • Loading branch information
bartonjs committed Mar 4, 2017
1 parent 0a2bd23 commit 49ddd36
Show file tree
Hide file tree
Showing 26 changed files with 969 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ 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(
SafeSecCertificateHandle cert,
SafeKeychainHandle keychain,
out int pOSStatus);

internal static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeKeychainItemHandle item)
{
bool addedRef = false;
Expand Down Expand Up @@ -197,6 +209,51 @@ 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(SafeSecCertificateHandle certHandle, SafeKeychainHandle keychain)
{
int osStatus;
int ret = AppleCryptoNative_X509StoreRemoveCertificate(certHandle, keychain, out osStatus);

if (ret == 0)
{
throw CreateExceptionForOSStatus(osStatus);
}

const int SuccessOrNoMatch = 1;
const int UserTrustExists = 2;
const int AdminTrustExists = 3;

switch (ret)
{
case SuccessOrNoMatch:
break;
case UserTrustExists:
throw new CryptographicException(SR.Cryptography_X509Store_WouldModifyUserTrust);
case AdminTrustExists:
throw new CryptographicException(SR.Cryptography_X509Store_WouldModifyAdminTrust);
default:
Debug.Fail($"Unexpected result from AppleCryptoNative_X509StoreRemoveCertificate: {ret}");
throw new CryptographicException();
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,50 @@ private static extern int AppleCryptoNative_StoreEnumerateMachineRoot(
out SafeCFArrayHandle pCertsOut,
out int pOSStatusOut);

[DllImport(Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_StoreEnumerateUserDisallowed(
out SafeCFArrayHandle pCertsOut,
out int pOSStatusOut);

[DllImport(Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_StoreEnumerateMachineDisallowed(
out SafeCFArrayHandle pCertsOut,
out int pOSStatusOut);

private delegate int StoreEnumerator(out SafeCFArrayHandle pCertsOut, out int pOSStatusOut);

internal static SafeCFArrayHandle StoreEnumerateDisallowed(StoreLocation location)
{
return EnumerateStore(
location,
AppleCryptoNative_StoreEnumerateUserDisallowed,
AppleCryptoNative_StoreEnumerateMachineDisallowed);
}

internal static SafeCFArrayHandle StoreEnumerateRoot(StoreLocation location)
{
return EnumerateStore(
location,
AppleCryptoNative_StoreEnumerateUserRoot,
AppleCryptoNative_StoreEnumerateMachineRoot);
}

private static SafeCFArrayHandle EnumerateStore(
StoreLocation location,
StoreEnumerator userEnumerator,
StoreEnumerator machineEnumerator)
{
int result;
SafeCFArrayHandle matches;
int osStatus;

if (location == StoreLocation.CurrentUser)
{
result = AppleCryptoNative_StoreEnumerateUserRoot(out matches, out osStatus);
result = userEnumerator(out matches, out osStatus);
}
else if (location == StoreLocation.LocalMachine)
{
result = AppleCryptoNative_StoreEnumerateMachineRoot(out matches, out osStatus);
result = machineEnumerator(out matches, out osStatus);
}
else
{
Expand All @@ -52,7 +83,7 @@ internal static SafeCFArrayHandle StoreEnumerateRoot(StoreLocation location)
if (result == 0)
throw CreateExceptionForOSStatus(osStatus);

Debug.Fail($"Unexpected result from AppleCryptoNative_StoreEnumerateRoot ({location}): {result}");
Debug.Fail($"Unexpected result from {location} trust store enumeration: {result}");
throw new CryptographicException();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ private static extern int AppleCryptoNative_X509ImportCertificate(
int cbKeyBlob,
X509ContentType contentType,
SafeCreateHandle cfPfxPassphrase,
SafeTemporaryKeychainHandle tmpKeychain,
SafeKeychainHandle tmpKeychain,
int exportable,
out SafeSecCertificateHandle pCertOut,
out SafeSecIdentityHandle pPrivateKeyOut,
out int pOSStatus);
Expand All @@ -32,7 +33,8 @@ private static extern int AppleCryptoNative_X509ImportCollection(
int cbKeyBlob,
X509ContentType contentType,
SafeCreateHandle cfPfxPassphrase,
SafeTemporaryKeychainHandle tmpKeychain,
SafeKeychainHandle tmpKeychain,
int exportable,
out SafeCFArrayHandle pCollectionOut,
out int pOSStatus);

Expand Down Expand Up @@ -100,7 +102,8 @@ internal static SafeSecCertificateHandle X509ImportCertificate(
byte[] bytes,
X509ContentType contentType,
SafePasswordHandle importPassword,
SafeTemporaryKeychainHandle tmpKeychain,
SafeKeychainHandle keychain,
bool exportable,
out SafeSecIdentityHandle identityHandle)
{
SafeSecCertificateHandle certHandle;
Expand Down Expand Up @@ -128,7 +131,8 @@ internal static SafeSecCertificateHandle X509ImportCertificate(
bytes.Length,
contentType,
cfPassphrase,
tmpKeychain,
keychain,
exportable ? 1 : 0,
out certHandle,
out identityHandle,
out osStatus);
Expand Down Expand Up @@ -178,7 +182,8 @@ internal static SafeCFArrayHandle X509ImportCollection(
byte[] bytes,
X509ContentType contentType,
SafePasswordHandle importPassword,
SafeTemporaryKeychainHandle tmpKeychain)
SafeKeychainHandle keychain,
bool exportable)
{
SafeCreateHandle cfPassphrase = s_nullExportString;
bool releasePassword = false;
Expand All @@ -205,7 +210,8 @@ internal static SafeCFArrayHandle X509ImportCollection(
bytes.Length,
contentType,
cfPassphrase,
tmpKeychain,
keychain,
exportable ? 1 : 0,
out collectionHandle,
out osStatus);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@

namespace System.Security.Cryptography
{
#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS

public partial class DSA : AsymmetricAlgorithm
{
public static new DSA Create()
{
return new DSAImplementation.DSASecurityTransforms();
}
#endif

internal static partial class DSAImplementation
{
Expand All @@ -32,6 +35,11 @@ public DSASecurityTransforms(int keySize)
KeySize = keySize;
}

internal DSASecurityTransforms(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle privateKey)
{
SetKey(SecKeyPair.PublicPrivatePair(publicKey, privateKey));
}

public override KeySizes[] LegalKeySizes
{
get
Expand Down Expand Up @@ -258,7 +266,41 @@ private void SetKey(SecKeyPair newKeyPair)
}
}
}
#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
}
#else
internal static class KeySizeHelpers
{

public static bool IsLegalSize(this int size, KeySizes[] legalSizes)
{
for (int i = 0; i < legalSizes.Length; i++)
{
KeySizes currentSizes = legalSizes[i];

// If a cipher has only one valid key size, MinSize == MaxSize and SkipSize will be 0
if (currentSizes.SkipSize == 0)
{
if (currentSizes.MinSize == size)
return true;
}
else if (size >= currentSizes.MinSize && size <= currentSizes.MaxSize)
{
// If the number is in range, check to see if it's a legal increment above MinSize
int delta = size - currentSizes.MinSize;

// While it would be unusual to see KeySizes { 10, 20, 5 } and { 11, 14, 1 }, it could happen.
// So don't return false just because this one doesn't match.
if (delta % currentSizes.SkipSize == 0)
{
return true;
}
}
}
return false;
}
}
#endif

internal static class DsaKeyBlobHelpers
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,132 @@ extern "C" int32_t AppleCryptoNative_SecKeychainEnumerateIdentities(SecKeychainR
{
return EnumerateKeychain(keychain, kSecClassIdentity, pIdentitiesOut, pOSStatus);
}

static OSStatus DeleteInKeychain(CFTypeRef needle, SecKeychainRef haystack)
{
CFMutableDictionaryRef query = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

if (query == nullptr)
return errSecAllocate;

CFArrayRef searchList = CFArrayCreate(
nullptr, const_cast<const void**>(reinterpret_cast<void**>(&haystack)), 1, &kCFTypeArrayCallBacks);

if (searchList == nullptr)
{
CFRelease(query);
return errSecAllocate;
}

CFArrayRef itemMatch = CFArrayCreate(nullptr, reinterpret_cast<const void**>(&needle), 1, &kCFTypeArrayCallBacks);

if (itemMatch == nullptr)
{
CFRelease(searchList);
CFRelease(query);
return errSecAllocate;
}

CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue);
CFDictionarySetValue(query, kSecMatchSearchList, searchList);
CFDictionarySetValue(query, kSecMatchItemList, itemMatch);
CFDictionarySetValue(query, kSecClass, kSecClassIdentity);

OSStatus status = SecItemDelete(query);

if (status == errSecItemNotFound)
{
status = noErr;
}

if (status == noErr)
{
CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
status = SecItemDelete(query);
}

if (status == errSecItemNotFound)
{
status = noErr;
}

CFRelease(itemMatch);
CFRelease(searchList);
CFRelease(query);

return status;
}

extern "C" int32_t
AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, SecKeychainRef keychain, int32_t* pOSStatus)
{
if (certOrIdentity == nullptr || keychain == nullptr)
return -1;

SecCertificateRef cert = nullptr;
SecIdentityRef identity = nullptr;

auto inputType = CFGetTypeID(certOrIdentity);
OSStatus status = noErr;

if (inputType == SecCertificateGetTypeID())
{
cert = reinterpret_cast<SecCertificateRef>(const_cast<void*>(certOrIdentity));
CFRetain(cert);
}
else if (inputType == SecIdentityGetTypeID())
{
identity = reinterpret_cast<SecIdentityRef>(const_cast<void*>(certOrIdentity));
status = SecIdentityCopyCertificate(identity, &cert);

if (status != noErr)
{
*pOSStatus = status;
return 0;
}
}
else
{
return -1;
}

const int32_t kErrorUserTrust = 2;
const int32_t kErrorAdminTrust = 3;

CFArrayRef settings = nullptr;

if (status == noErr)
{
status = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainUser, &settings);
}

if (settings != nullptr)
{
CFRelease(settings);
settings = nullptr;
}

if (status == noErr)
{
CFRelease(cert);
return kErrorUserTrust;
}

status = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainAdmin, &settings);

if (settings != nullptr)
{
CFRelease(settings);
settings = nullptr;
}

if (status == noErr)
{
CFRelease(cert);
return kErrorAdminTrust;
}

*pOSStatus = DeleteInKeychain(cert, keychain);
return *pOSStatus == noErr;
}
Loading

0 comments on commit 49ddd36

Please sign in to comment.