using CPF.Mac.CoreFoundation; using CPF.Mac.Foundation; using CPF.Mac.ObjCRuntime; using System; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; namespace CPF.Mac.Security { public class SecTrust : INativeObject, IDisposable { private IntPtr handle; public int Count { get { if (handle == IntPtr.Zero) { return 0; } return (int)SecTrustGetCertificateCount(handle); } } public SecCertificate this[int index] { get { if (handle == IntPtr.Zero) { throw new ObjectDisposedException("SecTrust"); } if (index < 0 || index >= Count) { throw new ArgumentOutOfRangeException("index"); } return new SecCertificate(SecTrustGetCertificateAtIndex(handle, index)); } } public IntPtr Handle => handle; internal SecTrust(IntPtr handle) : this(handle, owns: false) { } [Preserve(Conditional = true)] internal SecTrust(IntPtr handle, bool owns) { if (handle == IntPtr.Zero) { throw new Exception("Invalid handle"); } this.handle = handle; if (!owns) { CFObject.CFRetain(handle); } } [DllImport("/System/Library/Frameworks/Security.framework/Security", EntryPoint = "SecTrustGetTypeID")] public static extern int GetTypeID(); [DllImport("/System/Library/Frameworks/Security.framework/Security")] private static extern SecStatusCode SecTrustCreateWithCertificates(IntPtr certOrCertArray, IntPtr policies, out IntPtr sectrustref); public SecTrust(X509Certificate certificate, SecPolicy policy) { if (certificate == null) { throw new ArgumentNullException("certificate"); } using (SecCertificate secCertificate = new SecCertificate(certificate)) { Initialize(secCertificate.Handle, policy); } } public SecTrust(X509Certificate2 certificate, SecPolicy policy) { if (certificate == null) { throw new ArgumentNullException("certificate"); } using (SecCertificate secCertificate = new SecCertificate(certificate)) { Initialize(secCertificate.Handle, policy); } } public SecTrust(X509CertificateCollection certificates, SecPolicy policy) { if (certificates == null) { throw new ArgumentNullException("certificates"); } SecCertificate[] array = new SecCertificate[certificates.Count]; int num = 0; foreach (X509Certificate certificate in certificates) { array[num++] = new SecCertificate(certificate); } Initialize(array, policy); } public SecTrust(X509Certificate2Collection certificates, SecPolicy policy) { if (certificates == null) { throw new ArgumentNullException("certificates"); } SecCertificate[] array = new SecCertificate[certificates.Count]; int num = 0; X509Certificate2Enumerator enumerator = certificates.GetEnumerator(); while (enumerator.MoveNext()) { X509Certificate2 current = enumerator.Current; array[num++] = new SecCertificate(current); } Initialize(array, policy); } private void Initialize(SecCertificate[] array, SecPolicy policy) { using (CFArray cFArray = CFArray.FromNativeObjects(array)) { Initialize(cFArray.Handle, policy); } } private void Initialize(IntPtr certHandle, SecPolicy policy) { if (policy == null) { throw new ArgumentNullException("policy"); } SecStatusCode secStatusCode = SecTrustCreateWithCertificates(certHandle, policy.Handle, out handle); if (secStatusCode != 0) { throw new ArgumentException(secStatusCode.ToString()); } } [DllImport("/System/Library/Frameworks/Security.framework/Security")] private static extern SecStatusCode SecTrustEvaluate(IntPtr trust, out SecTrustResult result); public SecTrustResult Evaluate() { if (handle == IntPtr.Zero) { throw new ObjectDisposedException("SecTrust"); } SecTrustResult result; SecStatusCode secStatusCode = SecTrustEvaluate(handle, out result); if (secStatusCode != 0) { throw new InvalidOperationException(secStatusCode.ToString()); } return result; } [DllImport("/System/Library/Frameworks/Security.framework/Security")] private static extern long SecTrustGetCertificateCount(IntPtr trust); [DllImport("/System/Library/Frameworks/Security.framework/Security")] private static extern IntPtr SecTrustGetCertificateAtIndex(IntPtr trust, long ix); [DllImport("/System/Library/Frameworks/Security.framework/Security")] private static extern IntPtr SecTrustCopyPublicKey(IntPtr trust); public SecKey GetPublicKey() { if (handle == IntPtr.Zero) { throw new ObjectDisposedException("SecTrust"); } return new SecKey(SecTrustCopyPublicKey(handle), owns: true); } [DllImport("/System/Library/Frameworks/Security.framework/Security")] private static extern IntPtr SecTrustCopyExceptions(IntPtr trust); public NSData GetExceptions() { if (handle == IntPtr.Zero) { throw new ObjectDisposedException("SecTrust"); } return new NSData(SecTrustCopyExceptions(handle), owns: false); } [DllImport("/System/Library/Frameworks/Security.framework/Security")] private static extern bool SecTrustSetExceptions(IntPtr trust, IntPtr exceptions); public bool SetExceptions(NSData data) { if (handle == IntPtr.Zero) { throw new ObjectDisposedException("SecTrust"); } IntPtr exceptions = data?.Handle ?? IntPtr.Zero; return SecTrustSetExceptions(handle, exceptions); } [DllImport("/System/Library/Frameworks/Security.framework/Security")] private static extern double SecTrustGetVerifyTime(IntPtr trust); public double GetVerifyTime() { if (handle == IntPtr.Zero) { throw new ObjectDisposedException("SecTrust"); } return SecTrustGetVerifyTime(handle); } [DllImport("/System/Library/Frameworks/Security.framework/Security")] private static extern SecStatusCode SecTrustSetVerifyDate(IntPtr trust, IntPtr verifyDate); public SecStatusCode SetVerifyDate(DateTime date) { if (handle == IntPtr.Zero) { throw new ObjectDisposedException("SecTrust"); } using (NSDate nSDate = (NSDate)date) { return SecTrustSetVerifyDate(handle, nSDate.Handle); } } [DllImport("/System/Library/Frameworks/Security.framework/Security")] private static extern SecStatusCode SecTrustSetAnchorCertificates(IntPtr trust, IntPtr anchorCertificates); public SecStatusCode SetAnchorCertificates(X509CertificateCollection certificates) { if (handle == IntPtr.Zero) { throw new ObjectDisposedException("SecTrust"); } if (certificates == null) { return SecTrustSetAnchorCertificates(handle, IntPtr.Zero); } SecCertificate[] array = new SecCertificate[certificates.Count]; int num = 0; foreach (X509Certificate certificate in certificates) { array[num++] = new SecCertificate(certificate); } return SetAnchorCertificates(array); } public SecStatusCode SetAnchorCertificates(X509Certificate2Collection certificates) { if (handle == IntPtr.Zero) { throw new ObjectDisposedException("SecTrust"); } if (certificates == null) { return SecTrustSetAnchorCertificates(handle, IntPtr.Zero); } SecCertificate[] array = new SecCertificate[certificates.Count]; int num = 0; X509Certificate2Enumerator enumerator = certificates.GetEnumerator(); while (enumerator.MoveNext()) { X509Certificate2 current = enumerator.Current; array[num++] = new SecCertificate(current); } return SetAnchorCertificates(array); } private SecStatusCode SetAnchorCertificates(SecCertificate[] array) { using (CFArray cFArray = CFArray.FromNativeObjects(array)) { return SecTrustSetAnchorCertificates(handle, cFArray.Handle); } } [DllImport("/System/Library/Frameworks/Security.framework/Security")] private static extern SecStatusCode SecTrustSetAnchorCertificatesOnly(IntPtr trust, bool anchorCertificatesOnly); public SecStatusCode SetAnchorCertificatesOnly(bool anchorCertificatesOnly) { if (handle == IntPtr.Zero) { throw new ObjectDisposedException("SecTrust"); } return SecTrustSetAnchorCertificatesOnly(handle, anchorCertificatesOnly); } ~SecTrust() { Dispose(disposing: false); } protected virtual void Dispose(bool disposing) { if (handle != IntPtr.Zero) { CFObject.CFRelease(handle); handle = IntPtr.Zero; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } }