2 // Certificate.cs: Implements the managed SecCertificate wrapper.
6 // Sebastien Pouliot <sebastien@xamarin.com>
8 // Copyright 2010 Novell, Inc
9 // Copyright 2012-2013 Xamarin Inc.
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 #if SECURITY_DEP && MONO_FEATURE_APPLETLS
34 using System.Collections.Generic;
35 using System.Runtime.InteropServices;
36 using System.Security.Cryptography.X509Certificates;
39 using ObjCRuntimeInternal;
41 namespace Mono.AppleTls {
43 partial class SecCertificate : INativeObject, IDisposable {
44 internal IntPtr handle;
46 internal SecCertificate (IntPtr handle, bool owns = false)
48 if (handle == IntPtr.Zero)
49 throw new Exception ("Invalid handle");
53 CFObject.CFRetain (handle);
56 [DllImport (AppleTlsContext.SecurityLibrary, EntryPoint="SecCertificateGetTypeID")]
57 public extern static IntPtr GetTypeID ();
59 [DllImport (AppleTlsContext.SecurityLibrary)]
60 extern static IntPtr SecCertificateCreateWithData (IntPtr allocator, IntPtr cfData);
62 public SecCertificate (X509Certificate certificate)
64 if (certificate == null)
65 throw new ArgumentNullException ("certificate");
67 handle = certificate.Impl.GetNativeAppleCertificate ();
68 if (handle != IntPtr.Zero) {
69 CFObject.CFRetain (handle);
73 using (CFData cert = CFData.FromData (certificate.GetRawCertData ())) {
78 internal SecCertificate (X509CertificateImpl impl)
80 handle = impl.GetNativeAppleCertificate ();
81 if (handle != IntPtr.Zero) {
82 CFObject.CFRetain (handle);
86 using (CFData cert = CFData.FromData (impl.GetRawCertData ())) {
91 void Initialize (CFData data)
93 handle = SecCertificateCreateWithData (IntPtr.Zero, data.Handle);
94 if (handle == IntPtr.Zero)
95 throw new ArgumentException ("Not a valid DER-encoded X.509 certificate");
98 [DllImport (AppleTlsContext.SecurityLibrary)]
99 extern static IntPtr SecCertificateCopySubjectSummary (IntPtr cert);
101 public string SubjectSummary {
103 if (handle == IntPtr.Zero)
104 throw new ObjectDisposedException ("SecCertificate");
106 IntPtr subjectSummaryHandle = IntPtr.Zero;
108 subjectSummaryHandle = SecCertificateCopySubjectSummary (handle);
109 CFString subjectSummary = CFString.AsString (subjectSummaryHandle);
110 return subjectSummary;
113 if (subjectSummaryHandle != IntPtr.Zero)
114 CFObject.CFRelease (subjectSummaryHandle);
119 [DllImport (AppleTlsContext.SecurityLibrary)]
120 extern static /* CFDataRef */ IntPtr SecCertificateCopyData (/* SecCertificateRef */ IntPtr cert);
122 public CFData DerData {
124 if (handle == IntPtr.Zero)
125 throw new ObjectDisposedException ("SecCertificate");
127 IntPtr data = SecCertificateCopyData (handle);
128 if (data == IntPtr.Zero)
129 throw new ArgumentException ("Not a valid certificate");
130 return new CFData (data, true);
134 public X509Certificate ToX509Certificate ()
136 if (handle == IntPtr.Zero)
137 throw new ObjectDisposedException ("SecCertificate");
139 return new X509Certificate (handle);
142 internal static bool Equals (SecCertificate first, SecCertificate second)
145 * This is a little bit expensive, but unfortunately there is no better API to compare two
146 * SecCertificateRef's for equality.
149 throw new ArgumentNullException ("first");
151 throw new ArgumentNullException ("second");
152 if (first.Handle == second.Handle)
155 using (var firstData = first.DerData)
156 using (var secondData = second.DerData) {
157 if (firstData.Handle == secondData.Handle)
160 if (firstData.Length != secondData.Length)
162 IntPtr length = (IntPtr)firstData.Length;
163 for (long i = 0; i < (long)length; i++) {
164 if (firstData [i] != secondData [i])
177 public IntPtr Handle {
183 public void Dispose ()
186 GC.SuppressFinalize (this);
189 protected virtual void Dispose (bool disposing)
191 if (handle != IntPtr.Zero){
192 CFObject.CFRelease (handle);
193 handle = IntPtr.Zero;
198 partial class SecIdentity : INativeObject, IDisposable {
200 static readonly CFString ImportExportPassphase;
201 static readonly CFString ImportItemIdentity;
202 static readonly CFString ImportExportAccess;
203 static readonly CFString ImportExportKeychain;
205 static SecIdentity ()
207 var handle = CFObject.dlopen (AppleTlsContext.SecurityLibrary, 0);
208 if (handle == IntPtr.Zero)
212 ImportExportPassphase = CFObject.GetStringConstant (handle, "kSecImportExportPassphrase");
213 ImportItemIdentity = CFObject.GetStringConstant (handle, "kSecImportItemIdentity");
214 ImportExportAccess = CFObject.GetStringConstant (handle, "kSecImportExportAccess");
215 ImportExportKeychain = CFObject.GetStringConstant (handle, "kSecImportExportKeychain");
217 CFObject.dlclose (handle);
221 internal IntPtr handle;
223 internal SecIdentity (IntPtr handle, bool owns = false)
225 this.handle = handle;
227 CFObject.CFRetain (handle);
230 [DllImport (AppleTlsContext.SecurityLibrary, EntryPoint="SecIdentityGetTypeID")]
231 public extern static IntPtr GetTypeID ();
233 [DllImport (AppleTlsContext.SecurityLibrary)]
234 extern static /* OSStatus */ SecStatusCode SecIdentityCopyCertificate (/* SecIdentityRef */ IntPtr identityRef, /* SecCertificateRef* */ out IntPtr certificateRef);
236 public SecCertificate Certificate {
238 if (handle == IntPtr.Zero)
239 throw new ObjectDisposedException ("SecIdentity");
241 SecStatusCode result = SecIdentityCopyCertificate (handle, out cert);
242 if (result != SecStatusCode.Success)
243 throw new InvalidOperationException (result.ToString ());
244 return new SecCertificate (cert, true);
248 internal class ImportOptions
251 public SecAccess Access {
254 public SecKeyChain KeyChain {
260 static CFDictionary CreateImportOptions (CFString password, ImportOptions options = null)
263 return CFDictionary.FromObjectAndKey (password.Handle, ImportExportPassphase.Handle);
265 var items = new List<Tuple<IntPtr, IntPtr>> ();
266 items.Add (new Tuple<IntPtr, IntPtr> (ImportExportPassphase.Handle, password.Handle));
269 if (options.KeyChain != null)
270 items.Add (new Tuple<IntPtr, IntPtr> (ImportExportKeychain.Handle, options.KeyChain.Handle));
271 if (options.Access != null)
272 items.Add (new Tuple<IntPtr, IntPtr> (ImportExportAccess.Handle, options.Access.Handle));
275 return CFDictionary.FromKeysAndObjects (items);
278 public static SecIdentity Import (byte[] data, string password, ImportOptions options = null)
281 throw new ArgumentNullException ("data");
282 if (string.IsNullOrEmpty (password)) // SecPKCS12Import() doesn't allow empty passwords.
283 throw new ArgumentException ("password");
284 using (var pwstring = CFString.Create (password))
285 using (var optionDict = CreateImportOptions (pwstring, options)) {
286 CFDictionary [] array;
287 SecStatusCode result = SecImportExport.ImportPkcs12 (data, optionDict, out array);
288 if (result != SecStatusCode.Success)
289 throw new InvalidOperationException (result.ToString ());
291 return new SecIdentity (array [0].GetValue (ImportItemIdentity.Handle));
295 public static SecIdentity Import (X509Certificate2 certificate, ImportOptions options = null)
297 if (certificate == null)
298 throw new ArgumentNullException ("certificate");
299 if (!certificate.HasPrivateKey)
300 throw new InvalidOperationException ("Need X509Certificate2 with a private key.");
303 * SecPSK12Import does not allow any empty passwords, so let's generate
304 * a semi-random one here.
306 var password = Guid.NewGuid ().ToString ();
307 var pkcs12 = certificate.Export (X509ContentType.Pfx, password);
308 return Import (pkcs12, password, options);
316 public IntPtr Handle {
322 public void Dispose ()
325 GC.SuppressFinalize (this);
328 protected virtual void Dispose (bool disposing)
330 if (handle != IntPtr.Zero){
331 CFObject.CFRelease (handle);
332 handle = IntPtr.Zero;
337 partial class SecKey : INativeObject, IDisposable {
338 internal IntPtr handle;
339 internal IntPtr owner;
341 public SecKey (IntPtr handle, bool owns = false)
343 this.handle = handle;
345 CFObject.CFRetain (handle);
349 * SecItemImport() returns a SecArrayRef. We need to free the array, not the items inside it.
352 internal SecKey (IntPtr handle, IntPtr owner)
354 this.handle = handle;
356 CFObject.CFRetain (owner);
359 [DllImport (AppleTlsContext.SecurityLibrary, EntryPoint="SecKeyGetTypeID")]
360 public extern static IntPtr GetTypeID ();
367 public IntPtr Handle {
373 public void Dispose ()
376 GC.SuppressFinalize (this);
379 protected virtual void Dispose (bool disposing)
381 if (owner != IntPtr.Zero) {
382 CFObject.CFRelease (owner);
383 owner = handle = IntPtr.Zero;
384 } else if (handle != IntPtr.Zero) {
385 CFObject.CFRelease (handle);
386 handle = IntPtr.Zero;
392 class SecAccess : INativeObject, IDisposable {
393 internal IntPtr handle;
395 public SecAccess (IntPtr handle, bool owns = false)
397 this.handle = handle;
399 CFObject.CFRetain (handle);
407 public IntPtr Handle {
413 [DllImport (AppleTlsContext.SecurityLibrary)]
414 extern static /* OSStatus */ SecStatusCode SecAccessCreate (/* CFStringRef */ IntPtr descriptor, /* CFArrayRef */ IntPtr trustedList, /* SecAccessRef _Nullable * */ out IntPtr accessRef);
416 public static SecAccess Create (string descriptor)
418 var descriptorHandle = CFString.Create (descriptor);
419 if (descriptorHandle == null)
420 throw new InvalidOperationException ();
424 var result = SecAccessCreate (descriptorHandle.Handle, IntPtr.Zero, out accessRef);
425 if (result != SecStatusCode.Success)
426 throw new InvalidOperationException (result.ToString ());
428 return new SecAccess (accessRef, true);
430 descriptorHandle.Dispose ();
434 public void Dispose ()
437 GC.SuppressFinalize (this);
440 protected virtual void Dispose (bool disposing)
442 if (handle != IntPtr.Zero) {
443 CFObject.CFRelease (handle);
444 handle = IntPtr.Zero;