Merge pull request #3715 from kumpera/fix-44707
[mono.git] / mcs / class / System / Mono.Btls / MonoBtlsX509.cs
1 //
2 // MonoBtlsX509.cs
3 //
4 // Author:
5 //       Martin Baulig <martin.baulig@xamarin.com>
6 //
7 // Copyright (c) 2016 Xamarin Inc. (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26 #if SECURITY_DEP
27 using System;
28 using System.IO;
29 using System.Text;
30 using System.Threading;
31 using System.Runtime.CompilerServices;
32 using System.Runtime.InteropServices;
33 using System.Security.Cryptography.X509Certificates;
34 using System.Security.Cryptography;
35
36 namespace Mono.Btls
37 {
38         class MonoBtlsX509 : MonoBtlsObject
39         {
40                 internal class BoringX509Handle : MonoBtlsHandle
41                 {
42                         public BoringX509Handle (IntPtr handle)
43                                 : base (handle, true)
44                         {
45                         }
46
47                         protected override bool ReleaseHandle ()
48                         {
49                                 if (handle != IntPtr.Zero)
50                                         mono_btls_x509_free (handle);
51                                 return true;
52                         }
53
54                         public IntPtr StealHandle ()
55                         {
56                                 var retval = Interlocked.Exchange (ref handle, IntPtr.Zero);
57                                 return retval;
58                         }
59                 }
60
61                 new internal BoringX509Handle Handle {
62                         get { return (BoringX509Handle)base.Handle; }
63                 }
64
65                 internal MonoBtlsX509 (BoringX509Handle handle) 
66                         : base (handle)
67                 {
68                 }
69
70                 [MethodImpl (MethodImplOptions.InternalCall)]
71                 extern static IntPtr mono_btls_x509_up_ref (IntPtr handle);
72
73                 [MethodImpl (MethodImplOptions.InternalCall)]
74                 extern static IntPtr mono_btls_x509_from_data (IntPtr data, int len, MonoBtlsX509Format format);
75
76                 [MethodImpl (MethodImplOptions.InternalCall)]
77                 extern static IntPtr mono_btls_x509_get_subject_name (IntPtr handle);
78
79                 [MethodImpl (MethodImplOptions.InternalCall)]
80                 extern static IntPtr mono_btls_x509_get_issuer_name (IntPtr handle);
81
82                 [MethodImpl (MethodImplOptions.InternalCall)]
83                 extern static int mono_btls_x509_get_subject_name_string (IntPtr handle, IntPtr buffer, int size);
84
85                 [MethodImpl (MethodImplOptions.InternalCall)]
86                 extern static int mono_btls_x509_get_issuer_name_string (IntPtr handle, IntPtr buffer, int size);
87
88                 [MethodImpl (MethodImplOptions.InternalCall)]
89                 extern static int mono_btls_x509_get_raw_data (IntPtr handle, IntPtr bio, MonoBtlsX509Format format);
90
91                 [MethodImpl (MethodImplOptions.InternalCall)]
92                 extern static int mono_btls_x509_cmp (IntPtr a, IntPtr b);
93
94                 [MethodImpl (MethodImplOptions.InternalCall)]
95                 extern static int mono_btls_x509_get_hash (IntPtr handle, out IntPtr data);
96
97                 [MethodImpl (MethodImplOptions.InternalCall)]
98                 extern static long mono_btls_x509_get_not_before (IntPtr handle);
99
100                 [MethodImpl (MethodImplOptions.InternalCall)]
101                 extern static long mono_btls_x509_get_not_after (IntPtr handle);
102
103                 [MethodImpl (MethodImplOptions.InternalCall)]
104                 extern static int mono_btls_x509_get_public_key (IntPtr handle, IntPtr bio);
105
106                 [MethodImpl (MethodImplOptions.InternalCall)]
107                 extern static int mono_btls_x509_get_serial_number (IntPtr handle, IntPtr data, int size, int mono_style);
108
109                 [MethodImpl (MethodImplOptions.InternalCall)]
110                 extern static int mono_btls_x509_get_version (IntPtr handle);
111
112                 [MethodImpl (MethodImplOptions.InternalCall)]
113                 extern static int mono_btls_x509_get_signature_algorithm (IntPtr handle, IntPtr buffer, int size);
114
115                 [MethodImpl (MethodImplOptions.InternalCall)]
116                 extern static int mono_btls_x509_get_public_key_asn1 (IntPtr handle, IntPtr oid, int oid_size, out IntPtr data, out int size);
117
118                 [MethodImpl (MethodImplOptions.InternalCall)]
119                 extern static int mono_btls_x509_get_public_key_parameters (IntPtr handle, IntPtr oid, int oid_size, out IntPtr data, out int size);
120
121                 [MethodImpl (MethodImplOptions.InternalCall)]
122                 extern static IntPtr mono_btls_x509_get_pubkey (IntPtr handle);
123
124                 [MethodImpl (MethodImplOptions.InternalCall)]
125                 extern static int mono_btls_x509_get_subject_key_identifier (IntPtr handle, out IntPtr data, out int size);
126
127                 [MethodImpl (MethodImplOptions.InternalCall)]
128                 extern static int mono_btls_x509_print (IntPtr handle, IntPtr bio);
129
130                 [MethodImpl (MethodImplOptions.InternalCall)]
131                 extern static void mono_btls_x509_free (IntPtr handle);
132
133                 [MethodImpl (MethodImplOptions.InternalCall)]
134                 extern static IntPtr mono_btls_x509_dup (IntPtr handle);
135
136                 [MethodImpl (MethodImplOptions.InternalCall)]
137                 extern static int mono_btls_x509_add_trust_object (IntPtr handle, MonoBtlsX509Purpose purpose);
138
139                 [MethodImpl (MethodImplOptions.InternalCall)]
140                 extern static int mono_btls_x509_add_reject_object (IntPtr handle, MonoBtlsX509Purpose purpose);
141
142                 [MethodImpl (MethodImplOptions.InternalCall)]
143                 extern static int mono_btls_x509_add_explicit_trust (IntPtr handle, MonoBtlsX509TrustKind kind);
144
145                 internal MonoBtlsX509 Copy ()
146                 {
147                         var copy = mono_btls_x509_up_ref (Handle.DangerousGetHandle ());
148                         CheckError (copy != IntPtr.Zero);
149                         return new MonoBtlsX509 (new BoringX509Handle (copy));
150                 }
151
152                 // This will actually duplicate the underlying 'X509 *' object instead of
153                 // simply increasing the reference count.
154                 internal MonoBtlsX509 Duplicate ()
155                 {
156                         var copy = mono_btls_x509_dup (Handle.DangerousGetHandle ());
157                         CheckError (copy != IntPtr.Zero);
158                         return new MonoBtlsX509 (new BoringX509Handle (copy));
159                 }
160
161                 public static MonoBtlsX509 LoadFromData (byte[] buffer, MonoBtlsX509Format format)
162                 {
163                         var data = Marshal.AllocHGlobal (buffer.Length);
164                         if (data == IntPtr.Zero)
165                                 throw new OutOfMemoryException ();
166
167                         try {
168                                 Marshal.Copy (buffer, 0, data, buffer.Length);
169                                 var x509 = mono_btls_x509_from_data (data, buffer.Length, format);
170                                 if (x509 == IntPtr.Zero)
171                                         throw new MonoBtlsException ("Failed to read certificate from data.");
172
173                                 return new MonoBtlsX509 (new BoringX509Handle (x509));
174                         } finally {
175                                 Marshal.FreeHGlobal (data);
176                         }
177                 }
178
179                 public MonoBtlsX509Name GetSubjectName ()
180                 {
181                         var handle = mono_btls_x509_get_subject_name (Handle.DangerousGetHandle ());
182                         CheckError (handle != IntPtr.Zero);
183                         return new MonoBtlsX509Name (new MonoBtlsX509Name.BoringX509NameHandle (handle, false));
184                 }
185
186                 public string GetSubjectNameString ()
187                 {
188                         const int size = 4096;
189                         var data = Marshal.AllocHGlobal (size);
190                         try {
191                                 var ret = mono_btls_x509_get_subject_name_string (
192                                         Handle.DangerousGetHandle (), data, size);
193                                 CheckError (ret);
194                                 return Marshal.PtrToStringAnsi (data);
195                         } finally {
196                                 Marshal.FreeHGlobal (data);
197                         }
198                 }
199
200                 public long GetSubjectNameHash ()
201                 {
202                         CheckThrow ();
203                         using (var subject = GetSubjectName ())
204                                 return subject.GetHash ();
205                 }
206
207                 public MonoBtlsX509Name GetIssuerName ()
208                 {
209                         var handle = mono_btls_x509_get_issuer_name (Handle.DangerousGetHandle ());
210                         CheckError (handle != IntPtr.Zero);
211                         return new MonoBtlsX509Name (new MonoBtlsX509Name.BoringX509NameHandle (handle, false));
212                 }
213
214                 public string GetIssuerNameString ()
215                 {
216                         const int size = 4096;
217                         var data = Marshal.AllocHGlobal (size);
218                         try {
219                                 var ret = mono_btls_x509_get_issuer_name_string (
220                                         Handle.DangerousGetHandle (), data, size);
221                                 CheckError (ret);
222                                 return Marshal.PtrToStringAnsi (data);
223                         } finally {
224                                 Marshal.FreeHGlobal (data);
225                         }
226                 }
227
228                 public byte[] GetRawData (MonoBtlsX509Format format)
229                 {
230                         using (var bio = new MonoBtlsBioMemory ()) {
231                                 var ret = mono_btls_x509_get_raw_data (
232                                         Handle.DangerousGetHandle (),
233                                         bio.Handle.DangerousGetHandle (),
234                                         format);
235                                 CheckError (ret);
236                                 return bio.GetData ();
237                         }
238                 }
239
240                 public void GetRawData (MonoBtlsBio bio, MonoBtlsX509Format format)
241                 {
242                         CheckThrow ();
243                         var ret = mono_btls_x509_get_raw_data (
244                                 Handle.DangerousGetHandle (),
245                                 bio.Handle.DangerousGetHandle (),
246                                 format);
247                         CheckError (ret);
248                 }
249
250                 public static int Compare (MonoBtlsX509 a, MonoBtlsX509 b)
251                 {
252                         return mono_btls_x509_cmp (
253                                 a.Handle.DangerousGetHandle (),
254                                 b.Handle.DangerousGetHandle ());
255                 }
256
257                 public byte[] GetCertHash ()
258                 {
259                         IntPtr data;
260                         var ret = mono_btls_x509_get_hash (Handle.DangerousGetHandle (), out data);
261                         CheckError (ret > 0);
262                         var buffer = new byte [ret];
263                         Marshal.Copy (data, buffer, 0, ret);
264                         return buffer;
265                 }
266
267                 public DateTime GetNotBefore ()
268                 {
269                         var ticks = mono_btls_x509_get_not_before (Handle.DangerousGetHandle ());
270                         return new DateTime (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds (ticks);
271                 }
272
273                 public DateTime GetNotAfter ()
274                 {
275                         var ticks = mono_btls_x509_get_not_after (Handle.DangerousGetHandle ());
276                         return new DateTime (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds (ticks);
277                 }
278
279                 public byte[] GetPublicKeyData ()
280                 {
281                         using (var bio = new MonoBtlsBioMemory ()) {
282                                 var ret = mono_btls_x509_get_public_key (
283                                         Handle.DangerousGetHandle (),
284                                         bio.Handle.DangerousGetHandle ());
285                                 CheckError (ret > 0);
286                                 return bio.GetData ();
287                         }
288                 }
289
290                 public byte[] GetSerialNumber (bool mono_style)
291                 {
292                         int size = 256;
293                         IntPtr data = Marshal.AllocHGlobal (size);
294                         try {
295                                 var ret = mono_btls_x509_get_serial_number (
296                                         Handle.DangerousGetHandle (), data,
297                                         size, mono_style ? 1 : 0);
298                                 CheckError (ret > 0);
299                                 var buffer = new byte [ret];
300                                 Marshal.Copy (data, buffer, 0, ret);
301                                 return buffer;
302                         } finally {
303                                 if (data != IntPtr.Zero)
304                                         Marshal.FreeHGlobal (data);
305                         }
306                 }
307
308                 public int GetVersion ()
309                 {
310                         return mono_btls_x509_get_version (Handle.DangerousGetHandle ());
311                 }
312
313                 public Oid GetSignatureAlgorithm ()
314                 {
315                         int size = 256;
316                         IntPtr data = Marshal.AllocHGlobal (size);
317                         try {
318                                 var ret = mono_btls_x509_get_signature_algorithm (
319                                         Handle.DangerousGetHandle (), data, size);
320                                 CheckError (ret > 0);
321                                 return new Oid (Marshal.PtrToStringAnsi (data));
322                         } finally {
323                                 Marshal.FreeHGlobal (data);
324                         }
325                 }
326
327                 public AsnEncodedData GetPublicKeyAsn1 ()
328                 {
329                         int size;
330                         IntPtr data;
331
332                         int oidSize = 256;
333                         var oidData = Marshal.AllocHGlobal (256);
334                         string oid;
335
336                         try {
337                                 var ret = mono_btls_x509_get_public_key_asn1 (
338                                         Handle.DangerousGetHandle (), oidData, oidSize,
339                                         out data, out size);
340                                 CheckError (ret);
341                                 oid = Marshal.PtrToStringAnsi (oidData);
342                         } finally {
343                                 Marshal.FreeHGlobal (oidData);
344                         }
345
346                         try {
347                                 var buffer = new byte[size];
348                                 Marshal.Copy (data, buffer, 0, size);
349                                 return new AsnEncodedData (oid.ToString (), buffer);
350                         } finally {
351                                 if (data != IntPtr.Zero)
352                                         FreeDataPtr (data);
353                         }
354                 }
355
356                 public AsnEncodedData GetPublicKeyParameters ()
357                 {
358                         int size;
359                         IntPtr data;
360
361                         int oidSize = 256;
362                         var oidData = Marshal.AllocHGlobal (256);
363                         string oid;
364
365                         try {
366                                 var ret = mono_btls_x509_get_public_key_parameters (
367                                         Handle.DangerousGetHandle (), oidData, oidSize,
368                                         out data, out size);
369                                 CheckError (ret);
370                                 oid = Marshal.PtrToStringAnsi (oidData);
371                         } finally {
372                                 Marshal.FreeHGlobal (oidData);
373                         }
374
375                         try {
376                                 var buffer = new byte[size];
377                                 Marshal.Copy (data, buffer, 0, size);
378                                 return new AsnEncodedData (oid.ToString (), buffer);
379                         } finally {
380                                 if (data != IntPtr.Zero)
381                                         FreeDataPtr (data);
382                         }
383                 }
384
385                 public byte[] GetSubjectKeyIdentifier ()
386                 {
387                         int size;
388                         IntPtr data = IntPtr.Zero;
389
390                         try {
391                                 var ret = mono_btls_x509_get_subject_key_identifier (
392                                         Handle.DangerousGetHandle (), out data, out size);
393                                 CheckError (ret);
394                                 var buffer = new byte[size];
395                                 Marshal.Copy (data, buffer, 0, size);
396                                 return buffer;
397                         } finally {
398                                 if (data != IntPtr.Zero)
399                                         FreeDataPtr (data);
400                         }
401                 }
402
403                 public MonoBtlsKey GetPublicKey ()
404                 {
405                         var handle = mono_btls_x509_get_pubkey (Handle.DangerousGetHandle ());
406                         CheckError (handle != IntPtr.Zero);
407                         return new MonoBtlsKey (new MonoBtlsKey.BoringKeyHandle (handle));
408                 }
409
410                 public void Print (MonoBtlsBio bio)
411                 {
412                         var ret = mono_btls_x509_print (
413                                 Handle.DangerousGetHandle (),
414                                 bio.Handle.DangerousGetHandle ());
415                         CheckError (ret);
416                 }
417
418                 public void ExportAsPEM (MonoBtlsBio bio, bool includeHumanReadableForm)
419                 {
420                         GetRawData (bio, MonoBtlsX509Format.PEM);
421
422                         if (!includeHumanReadableForm)
423                                 return;
424
425                         Print (bio);
426
427                         var hash = GetCertHash ();
428                         var output = new StringBuilder ();
429                         output.Append ("SHA1 Fingerprint=");
430                         for (int i = 0; i < hash.Length; i++) {
431                                 if (i > 0)
432                                         output.Append (":");
433                                 output.AppendFormat ("{0:X2}", hash [i]);
434                         }
435                         output.AppendLine ();
436                         var outputData = Encoding.ASCII.GetBytes (output.ToString ());
437                         bio.Write (outputData, 0, outputData.Length);
438                 }
439
440                 public void AddTrustObject (MonoBtlsX509Purpose purpose)
441                 {
442                         CheckThrow ();
443                         var ret = mono_btls_x509_add_trust_object (
444                                 Handle.DangerousGetHandle (), purpose);
445                         CheckError (ret);
446                 }
447
448                 public void AddRejectObject (MonoBtlsX509Purpose purpose)
449                 {
450                         CheckThrow ();
451                         var ret = mono_btls_x509_add_reject_object (
452                                 Handle.DangerousGetHandle (), purpose);
453                         CheckError (ret);
454                 }
455
456                 public void AddExplicitTrust (MonoBtlsX509TrustKind kind)
457                 {
458                         CheckThrow ();
459                         var ret = mono_btls_x509_add_explicit_trust (
460                                 Handle.DangerousGetHandle (), kind);
461                         CheckError (ret);
462                 }
463         }
464 }
465 #endif