public unsafe bool Contains(X509Certificate certificate) { if (certificate == null) throw new ArgumentNullException ("certificate"); if (disposed) throw new ObjectDisposedException ("SecKeychain"); // Note: we don't have to use an alias attribute, it's just that it might be faster to use it (fewer certificates we have to compare raw data for) byte[] alias = Encoding.UTF8.GetBytes (certificate.GetCommonName ()); IntPtr searchRef, itemRef; bool found = false; byte[] certData; OSStatus status; fixed (byte* aliasPtr = alias) { SecKeychainAttribute* attrs = stackalloc SecKeychainAttribute [1]; int n = 0; if (alias != null) attrs[n++] = new SecKeychainAttribute (SecItemAttr.Alias, (uint) alias.Length, (IntPtr) aliasPtr); SecKeychainAttributeList attrList = new SecKeychainAttributeList (n, (IntPtr) attrs); status = SecKeychainSearchCreateFromAttributes (Handle, SecItemClass.Certificate, &attrList, out searchRef); if (status != OSStatus.Ok) throw new Exception ("Could not enumerate certificates from the keychain. Error:\n" + GetError (status)); certData = certificate.GetEncoded (); while (!found && SecKeychainSearchCopyNext (searchRef, out itemRef) == OSStatus.Ok) { SecItemClass itemClass = 0; IntPtr data = IntPtr.Zero; uint length = 0; status = SecKeychainItemCopyContent (itemRef, ref itemClass, IntPtr.Zero, ref length, ref data); if (status == OSStatus.Ok) { if (certData.Length == (int) length) { byte[] rawData = new byte[(int) length]; Marshal.Copy (data, rawData, 0, (int) length); found = true; for (int i = 0; i < rawData.Length; i++) { if (rawData[i] != certData[i]) { found = false; break; } } } SecKeychainItemFreeContent (IntPtr.Zero, data); } CFRelease (itemRef); } CFRelease (searchRef); } return found; }