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); }