private static ILoaderPal ImportPkcs12( ReadOnlySpan <byte> rawData, SafePasswordHandle password, bool exportable, bool ephemeralSpecified, SafeKeychainHandle keychain) { ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData); try { reader.Decrypt(password, ephemeralSpecified); return(new ApplePkcs12CertLoader(reader, keychain, password, exportable)); } catch { reader.Dispose(); keychain.Dispose(); throw; } }
private static int AppleCryptoNative_X509ImportCertificate( ReadOnlySpan <byte> 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)); }
internal static SafeCFArrayHandle KeychainEnumerateCerts(SafeKeychainHandle keychainHandle) { SafeCFArrayHandle matches; int osStatus; int result = AppleCryptoNative_SecKeychainEnumerateCerts(keychainHandle, out matches, out osStatus); if (result == 1) { return(matches); } matches.Dispose(); if (result == 0) { throw CreateExceptionForOSStatus(osStatus); } Debug.Fail($"Unexpected result from AppleCryptoNative_SecKeychainEnumerateCerts: {result}"); throw new CryptographicException(); }
internal static SafeSecCertificateHandle X509ImportCertificate( byte[] 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 partial int AppleCryptoNative_SecKeychainItemCopyKeychain( IntPtr item, out SafeKeychainHandle keychain);
public static ICertificatePal FromBlob( ReadOnlySpan <byte> 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(); }
private static partial int AppleCryptoNative_X509StoreAddCertificate( SafeKeychainItemHandle cert, SafeKeychainHandle keychain, out int pOSStatus);
private static partial int AppleCryptoNative_SetKeychainNeverLock(SafeKeychainHandle keychain);
private static partial int AppleCryptoNative_SecKeychainOpen( string keychainPath, out SafeKeychainHandle keychain);
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(); } }
private static extern int AppleCryptoNative_X509MoveToKeychain( SafeSecCertificateHandle certHandle, SafeKeychainHandle targetKeychain, SafeSecKeyRefHandle privateKeyHandle, out SafeSecIdentityHandle pIdentityHandleOut, out int pOSStatus);
internal static SafeCFArrayHandle X509ImportCollection( byte[] 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( 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(); } }
public void Dispose() { _keychainHandle?.Dispose(); _keychainHandle = null !; }
private static extern int AppleCryptoNative_RsaGenerateKey( int keySizeInBits, SafeKeychainHandle keychain, out SafeSecKeyRefHandle pPublicKey, out SafeSecKeyRefHandle pPrivateKey, out int pOSStatus);
private static partial int AppleCryptoNative_X509StoreRemoveCertificate( SafeKeychainItemHandle cert, SafeKeychainHandle keychain, [MarshalAs(UnmanagedType.Bool)] bool isReadOnlyMode, out int pOSStatus);
private static partial int AppleCryptoNative_SecKeychainCreate( string path, int utf8PassphraseLength, byte[] utf8Passphrase, out SafeKeychainHandle keychain);
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(); } }
private static extern int AppleCryptoNative_X509StoreRemoveCertificate( SafeKeychainItemHandle cert, SafeKeychainHandle keychain, out int pOSStatus);
private static partial int AppleCryptoNative_SecKeychainCopyDefault(out SafeKeychainHandle keychain);
private static extern int AppleCryptoNative_SetKeychainNeverLock(SafeKeychainHandle keychain);
private static partial int AppleCryptoNative_SecKeychainUnlock( SafeKeychainHandle keychain, int utf8PassphraseLength, byte[] utf8Passphrase);
private static extern int AppleCryptoNative_SecKeychainEnumerateCerts( SafeKeychainHandle keychain, out SafeCFArrayHandle matches, out int pOSStatus);
private static partial int AppleCryptoNative_SecKeychainEnumerateIdentities( SafeKeychainHandle keychain, out SafeCFArrayHandle matches, out int pOSStatus);
private ICertificatePal CopyWithPrivateKey(SecKeyPair keyPair) { if (keyPair.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(keyPair.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, keyPair.PrivateKey, keychain); AppleCertificatePal newPal = new AppleCertificatePal(identityHandle); return(newPal); } }
private static partial int AppleCryptoNative_X509StoreRemoveCertificate( SafeKeychainItemHandle cert, SafeKeychainHandle keychain, bool isReadOnlyMode, out int pOSStatus);
private static partial int AppleCryptoNative_X509CopyWithPrivateKey( SafeSecCertificateHandle certHandle, SafeSecKeyRefHandle privateKeyHandle, SafeKeychainHandle targetKeychain, out SafeSecIdentityHandle pIdentityHandleOut, out int pOSStatus);