e78a85f174e3e7e94f27443a37d802b7506f2389
[mono.git] / mcs / class / System / Mono.AppleTls / Certificate.cs
1 // 
2 // Certificate.cs: Implements the managed SecCertificate wrapper.
3 //
4 // Authors: 
5 //      Miguel de Icaza
6 //  Sebastien Pouliot  <sebastien@xamarin.com>
7 //
8 // Copyright 2010 Novell, Inc
9 // Copyright 2012-2013 Xamarin Inc.
10 //
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:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
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.
29 //
30
31 #if SECURITY_DEP && MONO_FEATURE_APPLETLS
32
33 using System;
34 using System.Runtime.InteropServices;
35 using System.Security.Cryptography.X509Certificates;
36 using Mono.Net;
37
38 using ObjCRuntime;
39
40 namespace Mono.AppleTls {
41
42         partial class SecCertificate : INativeObject, IDisposable {
43                 internal IntPtr handle;
44                 
45                 internal SecCertificate (IntPtr handle, bool owns = false)
46                 {
47                         if (handle == IntPtr.Zero)
48                                 throw new Exception ("Invalid handle");
49
50                         this.handle = handle;
51                         if (!owns)
52                                 CFObject.CFRetain (handle);
53                 }
54                 
55                 [DllImport (AppleTlsContext.SecurityLibrary, EntryPoint="SecCertificateGetTypeID")]
56                 public extern static IntPtr GetTypeID ();
57                         
58                 [DllImport (AppleTlsContext.SecurityLibrary)]
59                 extern static IntPtr SecCertificateCreateWithData (IntPtr allocator, IntPtr cfData);
60
61                 public SecCertificate (X509Certificate certificate)
62                 {
63                         if (certificate == null)
64                                 throw new ArgumentNullException ("certificate");
65
66                         handle = certificate.Impl.GetNativeAppleCertificate ();
67                         if (handle != IntPtr.Zero) {
68                                 CFObject.CFRetain (handle);
69                                 return;
70                         }
71
72                         using (CFData cert = CFData.FromData (certificate.GetRawCertData ())) {
73                                 Initialize (cert);
74                         }
75                 }
76
77                 internal SecCertificate (X509CertificateImpl impl)
78                 {
79                         handle = impl.GetNativeAppleCertificate ();
80                         if (handle != IntPtr.Zero) {
81                                 CFObject.CFRetain (handle);
82                                 return;
83                         }
84
85                         using (CFData cert = CFData.FromData (impl.GetRawCertData ())) {
86                                 Initialize (cert);
87                         }
88                 }
89
90                 void Initialize (CFData data)
91                 {
92                         handle = SecCertificateCreateWithData (IntPtr.Zero, data.Handle);
93                         if (handle == IntPtr.Zero)
94                                 throw new ArgumentException ("Not a valid DER-encoded X.509 certificate");
95                 }
96
97                 [DllImport (AppleTlsContext.SecurityLibrary)]
98                 extern static IntPtr SecCertificateCopySubjectSummary (IntPtr cert);
99
100                 public string SubjectSummary {
101                         get {
102                                 if (handle == IntPtr.Zero)
103                                         throw new ObjectDisposedException ("SecCertificate");
104                                 
105                                 IntPtr subjectSummaryHandle = IntPtr.Zero;
106                                 try {
107                                         subjectSummaryHandle = SecCertificateCopySubjectSummary (handle);
108                                         CFString subjectSummary = CFString.AsString (subjectSummaryHandle);
109                                         return subjectSummary;
110                                 }
111                                 finally {
112                                         if (subjectSummaryHandle != IntPtr.Zero)
113                                                 CFObject.CFRelease (subjectSummaryHandle);
114                                 }
115                         }
116                 }
117
118                 [DllImport (AppleTlsContext.SecurityLibrary)]
119                 extern static /* CFDataRef */ IntPtr SecCertificateCopyData (/* SecCertificateRef */ IntPtr cert);
120
121                 public CFData DerData {
122                         get {
123                                 if (handle == IntPtr.Zero)
124                                         throw new ObjectDisposedException ("SecCertificate");
125
126                                 IntPtr data = SecCertificateCopyData (handle);
127                                 if (data == IntPtr.Zero)
128                                         throw new ArgumentException ("Not a valid certificate");
129                                 return new CFData (data, true);
130                         }
131                 }
132
133                 public X509Certificate ToX509Certificate ()
134                 {
135                         if (handle == IntPtr.Zero)
136                                 throw new ObjectDisposedException ("SecCertificate");
137
138                         return new X509Certificate (handle);
139                 }
140
141                 internal static bool Equals (SecCertificate first, SecCertificate second)
142                 {
143                         /*
144                          * This is a little bit expensive, but unfortunately there is no better API to compare two
145                          * SecCertificateRef's for equality.
146                          */
147                         if (first == null)
148                                 throw new ArgumentNullException ("first");
149                         if (second == null)
150                                 throw new ArgumentNullException ("second");
151                         if (first.Handle == second.Handle)
152                                 return true;
153
154                         using (var firstData = first.DerData)
155                         using (var secondData = second.DerData) {
156                                 if (firstData.Handle == secondData.Handle)
157                                         return true;
158
159                                 if (firstData.Length != secondData.Length)
160                                         return false;
161                                 IntPtr length = (IntPtr)firstData.Length;
162                                 for (long i = 0; i < (long)length; i++) {
163                                         if (firstData [i] != secondData [i])
164                                                 return false;
165                                 }
166
167                                 return true;
168                         }
169                 }
170
171                 ~SecCertificate ()
172                 {
173                         Dispose (false);
174                 }
175
176                 public IntPtr Handle {
177                         get {
178                                 return handle;
179                         }
180                 }
181
182                 public void Dispose ()
183                 {
184                         Dispose (true);
185                         GC.SuppressFinalize (this);
186                 }
187
188                 protected virtual void Dispose (bool disposing)
189                 {
190                         if (handle != IntPtr.Zero){
191                                 CFObject.CFRelease (handle);
192                                 handle = IntPtr.Zero;
193                         }
194                 }
195         }
196
197         partial class SecIdentity : INativeObject, IDisposable {
198                  
199                 static readonly CFString ImportExportPassphase;
200                 static readonly CFString ImportItemIdentity;
201                 
202                 static SecIdentity ()
203                 {
204                         var handle = CFObject.dlopen (AppleTlsContext.SecurityLibrary, 0);
205                         if (handle == IntPtr.Zero)
206                                 return;
207
208                         try {           
209                                 ImportExportPassphase = CFObject.GetStringConstant (handle, "kSecImportExportPassphrase");
210                                 ImportItemIdentity = CFObject.GetStringConstant (handle, "kSecImportItemIdentity");
211                         } finally {
212                                 CFObject.dlclose (handle);
213                         }
214                 }
215
216                 internal IntPtr handle;
217                 
218                 internal SecIdentity (IntPtr handle, bool owns = false)
219                 {
220                         this.handle = handle;
221                         if (!owns)
222                                 CFObject.CFRetain (handle);
223                 }
224
225                 [DllImport (AppleTlsContext.SecurityLibrary, EntryPoint="SecIdentityGetTypeID")]
226                 public extern static IntPtr GetTypeID ();
227
228                 [DllImport (AppleTlsContext.SecurityLibrary)]
229                 extern static /* OSStatus */ SecStatusCode SecIdentityCopyCertificate (/* SecIdentityRef */ IntPtr identityRef,  /* SecCertificateRef* */ out IntPtr certificateRef);
230
231                 public SecCertificate Certificate {
232                         get {
233                                 if (handle == IntPtr.Zero)
234                                         throw new ObjectDisposedException ("SecIdentity");
235                                 IntPtr cert;
236                                 SecStatusCode result = SecIdentityCopyCertificate (handle, out cert);
237                                 if (result != SecStatusCode.Success)
238                                         throw new InvalidOperationException (result.ToString ());
239                                 return new SecCertificate (cert, true);
240                         }
241                 }
242
243                 public static SecIdentity Import (byte[] data, string password)
244                 {
245                         if (data == null)
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 ());
255
256                                 return new SecIdentity (array [0].GetValue (ImportItemIdentity.Handle));
257                         }
258                 }
259
260                 public static SecIdentity Import (X509Certificate2 certificate)
261                 {
262                         if (certificate == null)
263                                 throw new ArgumentNullException ("certificate");
264                         if (!certificate.HasPrivateKey)
265                                 throw new InvalidOperationException ("Need X509Certificate2 with a private key.");
266
267                         /*
268                          * SecPSK12Import does not allow any empty passwords, so let's generate
269                          * a semi-random one here.
270                          */
271                         var password = Guid.NewGuid ().ToString ();
272                         var pkcs12 = certificate.Export (X509ContentType.Pfx, password);
273                         return Import (pkcs12, password);
274                 }
275
276                 ~SecIdentity ()
277                 {
278                         Dispose (false);
279                 }
280
281                 public IntPtr Handle {
282                         get {
283                                 return handle;
284                         }
285                 }
286
287                 public void Dispose ()
288                 {
289                         Dispose (true);
290                         GC.SuppressFinalize (this);
291                 }
292
293                 protected virtual void Dispose (bool disposing)
294                 {
295                         if (handle != IntPtr.Zero){
296                                 CFObject.CFRelease (handle);
297                                 handle = IntPtr.Zero;
298                         }
299                 }
300         }
301
302         partial class SecKey : INativeObject, IDisposable {
303                 internal IntPtr handle;
304                 
305                 public SecKey (IntPtr handle, bool owns = false)
306                 {
307                         this.handle = handle;
308                         if (!owns)
309                                 CFObject.CFRetain (handle);
310                 }
311
312                 [DllImport (AppleTlsContext.SecurityLibrary, EntryPoint="SecKeyGetTypeID")]
313                 public extern static IntPtr GetTypeID ();
314                 
315                 ~SecKey ()
316                 {
317                         Dispose (false);
318                 }
319
320                 public IntPtr Handle {
321                         get {
322                                 return handle;
323                         }
324                 }
325
326                 public void Dispose ()
327                 {
328                         Dispose (true);
329                         GC.SuppressFinalize (this);
330                 }
331
332                 protected virtual void Dispose (bool disposing)
333                 {
334                         if (handle != IntPtr.Zero){
335                                 CFObject.CFRelease (handle);
336                                 handle = IntPtr.Zero;
337                         }
338                 }
339         }
340 }
341 #endif