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.Runtime.InteropServices;
35 using System.Security.Cryptography.X509Certificates;
40 namespace Mono.AppleTls {
42 partial class SecCertificate : INativeObject, IDisposable {
43 internal IntPtr handle;
45 internal SecCertificate (IntPtr handle, bool owns = false)
47 if (handle == IntPtr.Zero)
48 throw new Exception ("Invalid handle");
52 CFObject.CFRetain (handle);
55 [DllImport (AppleTlsContext.SecurityLibrary, EntryPoint="SecCertificateGetTypeID")]
56 public extern static IntPtr GetTypeID ();
58 [DllImport (AppleTlsContext.SecurityLibrary)]
59 extern static IntPtr SecCertificateCreateWithData (IntPtr allocator, IntPtr cfData);
61 public SecCertificate (X509Certificate certificate)
63 if (certificate == null)
64 throw new ArgumentNullException ("certificate");
66 handle = certificate.Impl.GetNativeAppleCertificate ();
67 if (handle != IntPtr.Zero) {
68 CFObject.CFRetain (handle);
72 using (CFData cert = CFData.FromData (certificate.GetRawCertData ())) {
77 internal SecCertificate (X509CertificateImpl impl)
79 handle = impl.GetNativeAppleCertificate ();
80 if (handle != IntPtr.Zero) {
81 CFObject.CFRetain (handle);
85 using (CFData cert = CFData.FromData (impl.GetRawCertData ())) {
90 void Initialize (CFData data)
92 handle = SecCertificateCreateWithData (IntPtr.Zero, data.Handle);
93 if (handle == IntPtr.Zero)
94 throw new ArgumentException ("Not a valid DER-encoded X.509 certificate");
97 [DllImport (AppleTlsContext.SecurityLibrary)]
98 extern static IntPtr SecCertificateCopySubjectSummary (IntPtr cert);
100 public string SubjectSummary {
102 if (handle == IntPtr.Zero)
103 throw new ObjectDisposedException ("SecCertificate");
105 IntPtr subjectSummaryHandle = IntPtr.Zero;
107 subjectSummaryHandle = SecCertificateCopySubjectSummary (handle);
108 CFString subjectSummary = CFString.AsString (subjectSummaryHandle);
109 return subjectSummary;
112 if (subjectSummaryHandle != IntPtr.Zero)
113 CFObject.CFRelease (subjectSummaryHandle);
118 [DllImport (AppleTlsContext.SecurityLibrary)]
119 extern static /* CFDataRef */ IntPtr SecCertificateCopyData (/* SecCertificateRef */ IntPtr cert);
121 public CFData DerData {
123 if (handle == IntPtr.Zero)
124 throw new ObjectDisposedException ("SecCertificate");
126 IntPtr data = SecCertificateCopyData (handle);
127 if (data == IntPtr.Zero)
128 throw new ArgumentException ("Not a valid certificate");
129 return new CFData (data, true);
133 public X509Certificate ToX509Certificate ()
135 if (handle == IntPtr.Zero)
136 throw new ObjectDisposedException ("SecCertificate");
138 return new X509Certificate (handle);
141 internal static bool Equals (SecCertificate first, SecCertificate second)
144 * This is a little bit expensive, but unfortunately there is no better API to compare two
145 * SecCertificateRef's for equality.
148 throw new ArgumentNullException ("first");
150 throw new ArgumentNullException ("second");
151 if (first.Handle == second.Handle)
154 using (var firstData = first.DerData)
155 using (var secondData = second.DerData) {
156 if (firstData.Handle == secondData.Handle)
159 if (firstData.Length != secondData.Length)
161 IntPtr length = (IntPtr)firstData.Length;
162 for (long i = 0; i < (long)length; i++) {
163 if (firstData [i] != secondData [i])
176 public IntPtr Handle {
182 public void Dispose ()
185 GC.SuppressFinalize (this);
188 protected virtual void Dispose (bool disposing)
190 if (handle != IntPtr.Zero){
191 CFObject.CFRelease (handle);
192 handle = IntPtr.Zero;
197 partial class SecIdentity : INativeObject, IDisposable {
199 static readonly CFString ImportExportPassphase;
200 static readonly CFString ImportItemIdentity;
202 static SecIdentity ()
204 var handle = CFObject.dlopen (AppleTlsContext.SecurityLibrary, 0);
205 if (handle == IntPtr.Zero)
209 ImportExportPassphase = CFObject.GetStringConstant (handle, "kSecImportExportPassphrase");
210 ImportItemIdentity = CFObject.GetStringConstant (handle, "kSecImportItemIdentity");
212 CFObject.dlclose (handle);
216 internal IntPtr handle;
218 internal SecIdentity (IntPtr handle, bool owns = false)
220 this.handle = handle;
222 CFObject.CFRetain (handle);
225 [DllImport (AppleTlsContext.SecurityLibrary, EntryPoint="SecIdentityGetTypeID")]
226 public extern static IntPtr GetTypeID ();
228 [DllImport (AppleTlsContext.SecurityLibrary)]
229 extern static /* OSStatus */ SecStatusCode SecIdentityCopyCertificate (/* SecIdentityRef */ IntPtr identityRef, /* SecCertificateRef* */ out IntPtr certificateRef);
231 public SecCertificate Certificate {
233 if (handle == IntPtr.Zero)
234 throw new ObjectDisposedException ("SecIdentity");
236 SecStatusCode result = SecIdentityCopyCertificate (handle, out cert);
237 if (result != SecStatusCode.Success)
238 throw new InvalidOperationException (result.ToString ());
239 return new SecCertificate (cert, true);
243 public static SecIdentity Import (byte[] data, string password)
246 throw new ArgumentNullException ("data");
247 if (string.IsNullOrEmpty (password)) // SecPKCS12Import() doesn't allow empty passwords.
248 throw new ArgumentException ("password");
249 using (var pwstring = CFString.Create (password))
250 using (var options = CFDictionary.FromObjectAndKey (pwstring.Handle, ImportExportPassphase.Handle)) {
251 CFDictionary [] array;
252 SecStatusCode result = SecImportExport.ImportPkcs12 (data, options, out array);
253 if (result != SecStatusCode.Success)
254 throw new InvalidOperationException (result.ToString ());
256 return new SecIdentity (array [0].GetValue (ImportItemIdentity.Handle));
260 public static SecIdentity Import (X509Certificate2 certificate)
262 if (certificate == null)
263 throw new ArgumentNullException ("certificate");
264 if (!certificate.HasPrivateKey)
265 throw new InvalidOperationException ("Need X509Certificate2 with a private key.");
268 * SecPSK12Import does not allow any empty passwords, so let's generate
269 * a semi-random one here.
271 var password = Guid.NewGuid ().ToString ();
272 var pkcs12 = certificate.Export (X509ContentType.Pfx, password);
273 return Import (pkcs12, password);
281 public IntPtr Handle {
287 public void Dispose ()
290 GC.SuppressFinalize (this);
293 protected virtual void Dispose (bool disposing)
295 if (handle != IntPtr.Zero){
296 CFObject.CFRelease (handle);
297 handle = IntPtr.Zero;
302 partial class SecKey : INativeObject, IDisposable {
303 internal IntPtr handle;
305 public SecKey (IntPtr handle, bool owns = false)
307 this.handle = handle;
309 CFObject.CFRetain (handle);
312 [DllImport (AppleTlsContext.SecurityLibrary, EntryPoint="SecKeyGetTypeID")]
313 public extern static IntPtr GetTypeID ();
320 public IntPtr Handle {
326 public void Dispose ()
329 GC.SuppressFinalize (this);
332 protected virtual void Dispose (bool disposing)
334 if (handle != IntPtr.Zero){
335 CFObject.CFRelease (handle);
336 handle = IntPtr.Zero;