// // MonoCertificatePal.cs // // Authors: // Miguel de Icaza // Sebastien Pouliot // Martin Baulig // // Copyright 2010 Novell, Inc // Copyright 2011-2014 Xamarin Inc. // Copyright (c) 2018 Xamarin Inc. (http://www.xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Threading; using System.Runtime.InteropServices; using System.Security.Cryptography.Apple; using System.Security.Cryptography.X509Certificates; using Microsoft.Win32.SafeHandles; using Mono.Net; namespace Mono.AppleTls { static partial class MonoCertificatePal { [DllImport (AppleTlsContext.SecurityLibrary)] extern static IntPtr SecCertificateCreateWithData (IntPtr allocator, IntPtr cfData); public static SafeSecCertificateHandle FromOtherCertificate (X509Certificate certificate) { if (certificate == null) throw new ArgumentNullException (nameof (certificate)); return FromOtherCertificate (certificate.Impl); } public static SafeSecCertificateHandle FromOtherCertificate (X509CertificateImpl impl) { X509Helper.ThrowIfContextInvalid (impl); var handle = impl.GetNativeAppleCertificate (); if (handle != IntPtr.Zero) return new SafeSecCertificateHandle (handle, false); using (var data = CFData.FromData (impl.RawData)) { handle = SecCertificateCreateWithData (IntPtr.Zero, data.Handle); if (handle == IntPtr.Zero) throw new ArgumentException ("Not a valid DER-encoded X.509 certificate"); return new SafeSecCertificateHandle (handle, true); } } [DllImport (AppleTlsContext.SecurityLibrary)] extern static IntPtr SecIdentityGetTypeID (); public static bool IsSecIdentity (IntPtr ptr) { if (ptr == IntPtr.Zero) return false; return CFType.GetTypeID (ptr) == SecIdentityGetTypeID (); } [DllImport (AppleTlsContext.SecurityLibrary)] public extern static IntPtr SecKeyGetTypeID (); public static bool IsSecKey (IntPtr ptr) { if (ptr == IntPtr.Zero) return false; return CFType.GetTypeID (ptr) == SecKeyGetTypeID (); } [DllImport (AppleTlsContext.SecurityLibrary)] extern static /* OSStatus */ SecStatusCode SecIdentityCopyCertificate (/* SecIdentityRef */ IntPtr identityRef, /* SecCertificateRef* */ out IntPtr certificateRef); public static SafeSecCertificateHandle GetCertificate (SafeSecIdentityHandle identity) { if (identity == null || identity.IsInvalid) throw new ArgumentNullException (nameof (identity)); var result = SecIdentityCopyCertificate (identity.DangerousGetHandle (), out var cert); if (result != SecStatusCode.Success) throw new InvalidOperationException (result.ToString ()); return new SafeSecCertificateHandle (cert, true); } [DllImport (AppleTlsContext.SecurityLibrary)] extern static IntPtr SecCertificateCopySubjectSummary (IntPtr cert); public static string GetSubjectSummary (SafeSecCertificateHandle certificate) { if (certificate == null || certificate.IsInvalid) throw new ArgumentNullException (nameof (certificate)); var subjectSummaryHandle = IntPtr.Zero; try { subjectSummaryHandle = SecCertificateCopySubjectSummary (certificate.DangerousGetHandle ()); return CFString.AsString (subjectSummaryHandle); } finally { if (subjectSummaryHandle != IntPtr.Zero) CFObject.CFRelease (subjectSummaryHandle); } } [DllImport (AppleTlsContext.SecurityLibrary)] extern static /* CFDataRef */ IntPtr SecCertificateCopyData (/* SecCertificateRef */ IntPtr cert); public static byte[] GetRawData (SafeSecCertificateHandle certificate) { if (certificate == null || certificate.IsInvalid) throw new ArgumentNullException (nameof (certificate)); var dataPtr = SecCertificateCopyData (certificate.DangerousGetHandle ()); if (dataPtr == IntPtr.Zero) throw new ArgumentException ("Not a valid certificate"); using (var data = new CFData (dataPtr, true)) { var buffer = new byte[(int)data.Length]; Marshal.Copy (data.Bytes, buffer, 0, buffer.Length); return buffer; } } public static bool Equals (SafeSecCertificateHandle first, SafeSecCertificateHandle second) { /* * This is a little bit expensive, but unfortunately there is no better API to compare two * SecCertificateRef's for equality. */ if (first == null || first.IsInvalid) throw new ArgumentNullException (nameof (first)); if (second == null || second.IsInvalid) throw new ArgumentNullException (nameof (second)); if (first.DangerousGetHandle () == second.DangerousGetHandle ()) return true; var firstDataPtr = SecCertificateCopyData (first.DangerousGetHandle ()); var secondDataPtr = SecCertificateCopyData (first.DangerousGetHandle ()); try { if (firstDataPtr == IntPtr.Zero || secondDataPtr == IntPtr.Zero) throw new ArgumentException ("Not a valid certificate."); if (firstDataPtr == secondDataPtr) return true; var firstLength = (int)CFData.CFDataGetLength (firstDataPtr); var secondLength = (int)CFData.CFDataGetLength (secondDataPtr); if (firstLength != secondLength) return false; var firstBytePtr = CFData.CFDataGetBytePtr (firstDataPtr); var secondBytePtr = CFData.CFDataGetBytePtr (secondDataPtr); if (firstBytePtr == secondBytePtr) return true; var firstBuffer = new byte[firstLength]; var secondBuffer = new byte[secondLength]; Marshal.Copy (firstBytePtr, firstBuffer, 0, firstBuffer.Length); Marshal.Copy (secondBytePtr, secondBuffer, 0, secondBuffer.Length); for (int i = 0; i < firstBuffer.Length; i++) if (firstBuffer[i] != secondBuffer[i]) return false; return true; } finally { if (firstDataPtr != IntPtr.Zero) CFObject.CFRelease (firstDataPtr); if (secondDataPtr != IntPtr.Zero) CFObject.CFRelease (secondDataPtr); } } } }