public void MoveTo(X509Certificate2Collection collection) { foreach (UnixPkcs12Reader.CertAndKey certAndKey in _pkcs12.EnumerateAll()) { AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert !; SafeSecKeyRefHandle?safeSecKeyRefHandle = ApplePkcs12Reader.GetPrivateKey(certAndKey.Key); using (safeSecKeyRefHandle) { ICertificatePal newPal; // SecItemImport doesn't seem to respect non-exportable import for PKCS#8, // only PKCS#12. // // So, as part of reading this PKCS#12 we now need to write the minimum // PKCS#12 in a normalized form, and ask the OS to import it. if (!_exportable && safeSecKeyRefHandle != null) { newPal = AppleCertificatePal.ImportPkcs12NonExportable( pal, safeSecKeyRefHandle, _password, _keychain); } else { newPal = pal.MoveToKeychain(_keychain, safeSecKeyRefHandle) ?? pal; } X509Certificate2 cert = new X509Certificate2(newPal); collection.Add(cert); } } }
public void Dispose() { PrivateKey?.Dispose(); PrivateKey = null; PublicKey?.Dispose(); PublicKey = null !; }
internal static byte[] SecKeyExport( SafeSecKeyRefHandle?key, bool exportPrivate, string password) { using (SafeCFDataHandle cfData = SecKeyExportData(key, exportPrivate, password)) { return(CoreFoundation.CFGetData(cfData)); } }
internal AppleCertificatePal?MoveToKeychain(SafeKeychainHandle keychain, SafeSecKeyRefHandle?privateKey) { SafeSecIdentityHandle?identity = Interop.AppleCrypto.X509MoveToKeychain( _certHandle, keychain, privateKey); if (identity != null) { return(new AppleCertificatePal(identity)); } return(null); }
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 AppleCertificatePal ImportPkcs12( ReadOnlySpan <byte> rawData, SafePasswordHandle password, bool exportable, SafeKeychainHandle keychain) { using (ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData)) { reader.Decrypt(password, ephemeralSpecified: false); UnixPkcs12Reader.CertAndKey certAndKey = reader.GetSingleCert(); AppleCertificatePal pal = (AppleCertificatePal)certAndKey.Cert !; SafeSecKeyRefHandle?safeSecKeyRefHandle = ApplePkcs12Reader.GetPrivateKey(certAndKey.Key); AppleCertificatePal?newPal; using (safeSecKeyRefHandle) { // SecItemImport doesn't seem to respect non-exportable import for PKCS#8, // only PKCS#12. // // So, as part of reading this PKCS#12 we now need to write the minimum // PKCS#12 in a normalized form, and ask the OS to import it. if (!exportable && safeSecKeyRefHandle != null) { using (pal) { return(ImportPkcs12NonExportable(pal, safeSecKeyRefHandle, password, keychain)); } } newPal = pal.MoveToKeychain(keychain, safeSecKeyRefHandle); if (newPal != null) { pal.Dispose(); } } // If no new PAL came back, it means we moved the cert, but had no private key. return(newPal ?? pal); } }
internal static SafeCFDataHandle SecKeyExportData( SafeSecKeyRefHandle?key, bool exportPrivate, ReadOnlySpan <char> password) { SafeCreateHandle exportPassword = exportPrivate ? CoreFoundation.CFStringCreateFromSpan(password) : s_nullExportString; int ret; SafeCFDataHandle cfData; int osStatus; try { ret = AppleCryptoNative_SecKeyExport( key, exportPrivate ? 1 : 0, exportPassword, out cfData, out osStatus); } finally { if (exportPassword != s_nullExportString) { exportPassword.Dispose(); } } if (ret == 1) { return(cfData); } cfData.Dispose(); if (ret == 0) { throw CreateExceptionForOSStatus(osStatus); } Debug.Fail($"AppleCryptoNative_SecKeyExport returned {ret}"); throw new CryptographicException(); }
private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash?hasher) { if (!(otherPartyPublicKey is ECDiffieHellmanSecurityTransformsPublicKey secTransPubKey)) { secTransPubKey = new ECDiffieHellmanSecurityTransformsPublicKey(otherPartyPublicKey.ExportParameters()); } try { SafeSecKeyRefHandle otherPublic = secTransPubKey.KeyHandle; if (Interop.AppleCrypto.EccGetKeySizeInBits(otherPublic) != KeySize) { throw new ArgumentException( SR.Cryptography_ArgECDHKeySizeMismatch, nameof(otherPartyPublicKey)); } SafeSecKeyRefHandle?thisPrivate = GetKeys().PrivateKey; if (thisPrivate == null) { throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey); } // Since Apple only supports secp256r1, secp384r1, and secp521r1; and 521 fits in // 66 bytes ((521 + 7) / 8), the Span path will always succeed. Span <byte> secretSpan = stackalloc byte[66]; byte[]? secret = Interop.AppleCrypto.EcdhKeyAgree( thisPrivate, otherPublic, secretSpan, out int bytesWritten); // Either we wrote to the span or we returned an array, but not both, and not neither. // ("neither" would have thrown) Debug.Assert( (bytesWritten == 0) != (secret == null), $"bytesWritten={bytesWritten}, (secret==null)={secret == null}"); if (hasher == null) { return(secret ?? secretSpan.Slice(0, bytesWritten).ToArray()); } if (secret == null) { hasher.AppendData(secretSpan.Slice(0, bytesWritten)); } else { hasher.AppendData(secret); Array.Clear(secret, 0, secret.Length); } return(null); } finally { if (!ReferenceEquals(otherPartyPublicKey, secTransPubKey)) { secTransPubKey.Dispose(); } } }
private static partial int AppleCryptoNative_SecKeyExport( SafeSecKeyRefHandle?key, int exportPrivate, SafeCreateHandle cfExportPassphrase, out SafeCFDataHandle cfDataOut, out int pOSStatus);
private SecKeyPair(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle?privateKey) { PublicKey = publicKey; PrivateKey = privateKey; }
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); } }