Merge pull request #2698 from esdrubal/iosxmlarray
[mono.git] / mcs / class / System / System.Security.Cryptography.X509Certificates / X509Certificate2ImplMono.cs
1 //
2 // X509Certificate2ImplMono
3 //
4 // Authors:
5 //      Sebastien Pouliot  <sebastien@xamarin.com>
6 //      Martin Baulig  <martin.baulig@xamarin.com>
7 //
8 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
9 // Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com)
10 // Copyright (C) 2015-2016 Xamarin, Inc. (http://www.xamarin.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 #if SECURITY_DEP
33
34 #if MONO_SECURITY_ALIAS
35 extern alias MonoSecurity;
36 using MonoSecurity::Mono.Security;
37 using MonoSecurity::Mono.Security.Cryptography;
38 using MX = MonoSecurity::Mono.Security.X509;
39 #else
40 using Mono.Security;
41 using Mono.Security.Cryptography;
42 using MX = Mono.Security.X509;
43 #endif
44
45 using System.IO;
46 using System.Text;
47 using System.Collections;
48
49 namespace System.Security.Cryptography.X509Certificates
50 {
51         internal class X509Certificate2ImplMono : X509Certificate2Impl
52         {
53                 bool _archived;
54                 X509ExtensionCollection _extensions;
55                 string _serial;
56                 PublicKey _publicKey;
57                 X500DistinguishedName issuer_name;
58                 X500DistinguishedName subject_name;
59                 Oid signature_algorithm;
60
61                 MX.X509Certificate _cert;
62
63                 static string empty_error = Locale.GetText ("Certificate instance is empty.");
64
65                 public override bool IsValid {
66                         get {
67                                 return _cert != null;
68                         }
69                 }
70
71                 public override IntPtr Handle {
72                         get { return IntPtr.Zero; }
73                 }
74
75                 internal X509Certificate2ImplMono (MX.X509Certificate cert)
76                 {
77                         this._cert = cert;
78                 }
79
80                 public override X509CertificateImpl Clone ()
81                 {
82                         ThrowIfContextInvalid ();
83                         return new X509Certificate2ImplMono (_cert);
84                 }
85
86                 #region Implemented X509CertificateImpl members
87
88                 public override string GetIssuerName (bool legacyV1Mode)
89                 {
90                         ThrowIfContextInvalid ();
91                         if (legacyV1Mode)
92                                 return _cert.IssuerName;
93                         else
94                                 return MX.X501.ToString (_cert.GetIssuerName (), true, ", ", true);
95                 }
96
97                 public override string GetSubjectName (bool legacyV1Mode)
98                 {
99                         ThrowIfContextInvalid ();
100                         if (legacyV1Mode)
101                                 return _cert.SubjectName;
102                         else
103                                 return MX.X501.ToString (_cert.GetSubjectName (), true, ", ", true);
104                 }
105
106                 public override byte[] GetRawCertData ()
107                 {
108                         ThrowIfContextInvalid ();
109                         return _cert.RawData;
110                 }
111
112                 protected override byte[] GetCertHash (bool lazy)
113                 {
114                         ThrowIfContextInvalid ();
115                         SHA1 sha = SHA1.Create ();
116                         return sha.ComputeHash (_cert.RawData);
117                 }
118
119                 public override DateTime GetValidFrom ()
120                 {
121                         ThrowIfContextInvalid ();
122                         return _cert.ValidFrom;
123                 }
124
125                 public override DateTime GetValidUntil ()
126                 {
127                         ThrowIfContextInvalid ();
128                         return _cert.ValidUntil;
129                 }
130
131                 public override bool Equals (X509CertificateImpl other, out bool result)
132                 {
133                         // Use default implementation
134                         result = false;
135                         return false;
136                 }
137
138                 public override string GetKeyAlgorithm () 
139                 {
140                         ThrowIfContextInvalid ();
141                         return _cert.KeyAlgorithm;
142                 }
143
144                 public override byte[] GetKeyAlgorithmParameters () 
145                 {
146                         ThrowIfContextInvalid ();
147                         return _cert.KeyAlgorithmParameters;
148                 }
149
150                 public override byte[] GetPublicKey ()
151                 {
152                         ThrowIfContextInvalid ();
153                         return _cert.PublicKey;
154                 }
155
156                 public override byte[] GetSerialNumber ()
157                 {
158                         ThrowIfContextInvalid ();
159                         return _cert.SerialNumber;
160                 }
161
162                 public override byte[] Export (X509ContentType contentType, byte[] password)
163                 {
164                         ThrowIfContextInvalid ();
165
166                         switch (contentType) {
167                         case X509ContentType.Cert:
168                                 return GetRawCertData ();
169                         case X509ContentType.Pfx: // this includes Pkcs12
170                                 // TODO
171                                 throw new NotSupportedException ();
172                         case X509ContentType.SerializedCert:
173                                 // TODO
174                                 throw new NotSupportedException ();
175                         default:
176                                 string msg = Locale.GetText ("This certificate format '{0}' cannot be exported.", contentType);
177                                 throw new CryptographicException (msg);
178                         }
179                 }
180
181                 #endregion
182
183                 // constructors
184
185                 public X509Certificate2ImplMono ()
186                 {
187                         _cert = null;
188                 }
189
190                 // properties
191
192                 public override bool Archived {
193                         get {
194                                 if (_cert == null)
195                                         throw new CryptographicException (empty_error);
196                                 return _archived;
197                         }
198                         set {
199                                 if (_cert == null)
200                                         throw new CryptographicException (empty_error);
201                                 _archived = value;
202                         }
203                 }
204
205                 public override X509ExtensionCollection Extensions {
206                         get {
207                                 if (_cert == null)
208                                         throw new CryptographicException (empty_error);
209                                 if (_extensions == null)
210                                         _extensions = new X509ExtensionCollection (_cert);
211                                 return _extensions;
212                         }
213                 }
214
215                 // FIXME - Could be more efficient
216                 public override bool HasPrivateKey {
217                         get { return PrivateKey != null; }
218                 }
219
220                 public override X500DistinguishedName IssuerName {
221                         get {
222                                 if (_cert == null)
223                                         throw new CryptographicException (empty_error);
224                                 if (issuer_name == null)
225                                         issuer_name = new X500DistinguishedName (_cert.GetIssuerName ().GetBytes ());
226                                 return issuer_name;
227                         }
228                 } 
229
230                 public override AsymmetricAlgorithm PrivateKey {
231                         get {
232                                 if (_cert == null)
233                                         throw new CryptographicException (empty_error);
234                                 try {
235                                         if (_cert.RSA != null) {
236                                                 RSACryptoServiceProvider rcsp = _cert.RSA as RSACryptoServiceProvider;
237                                                 if (rcsp != null)
238                                                         return rcsp.PublicOnly ? null : rcsp;
239
240                                                 RSAManaged rsam = _cert.RSA as RSAManaged;
241                                                 if (rsam != null)
242                                                         return rsam.PublicOnly ? null : rsam;
243
244                                                 _cert.RSA.ExportParameters (true);
245                                                 return _cert.RSA;
246                                         } else if (_cert.DSA != null) {
247                                                 DSACryptoServiceProvider dcsp = _cert.DSA as DSACryptoServiceProvider;
248                                                 if (dcsp != null)
249                                                         return dcsp.PublicOnly ? null : dcsp;
250
251                                                 _cert.DSA.ExportParameters (true);
252                                                 return _cert.DSA;
253                                         }
254                                 }
255                                 catch {
256                                 }
257                                 return null;
258                         }
259                         set {
260                                 if (_cert == null)
261                                         throw new CryptographicException (empty_error);
262
263                                 // allow NULL so we can "forget" the key associated to the certificate
264                                 // e.g. in case we want to export it in another format (see bug #396620)
265                                 if (value == null) {
266                                         _cert.RSA = null;
267                                         _cert.DSA = null;
268                                 } else  if (value is RSA)
269                                         _cert.RSA = (RSA) value;
270                                 else if (value is DSA)
271                                         _cert.DSA = (DSA) value;
272                                 else
273                                         throw new NotSupportedException ();
274                         }
275                 } 
276
277                 public override PublicKey PublicKey {
278                         get { 
279                                 if (_cert == null)
280                                         throw new CryptographicException (empty_error);
281
282                                 if (_publicKey == null) {
283                                         try {
284                                                 _publicKey = new PublicKey (_cert);
285                                         }
286                                         catch (Exception e) {
287                                                 string msg = Locale.GetText ("Unable to decode public key.");
288                                                 throw new CryptographicException (msg, e);
289                                         }
290                                 }
291                                 return _publicKey;
292                         }
293                 }
294
295                 public override Oid SignatureAlgorithm {
296                         get {
297                                 if (_cert == null)
298                                         throw new CryptographicException (empty_error);
299
300                                 if (signature_algorithm == null)
301                                         signature_algorithm = new Oid (_cert.SignatureAlgorithm);
302                                 return signature_algorithm;
303                         }
304                 } 
305
306                 public override X500DistinguishedName SubjectName {
307                         get {
308                                 if (_cert == null)
309                                         throw new CryptographicException (empty_error);
310
311                                 if (subject_name == null)
312                                         subject_name = new X500DistinguishedName (_cert.GetSubjectName ().GetBytes ());
313                                 return subject_name;
314                         }
315                 } 
316
317                 public override int Version {
318                         get {
319                                 if (_cert == null)
320                                         throw new CryptographicException (empty_error);
321                                 return _cert.Version;
322                         }
323                 }
324
325                 // methods
326
327                 [MonoTODO ("always return String.Empty for UpnName, DnsFromAlternativeName and UrlName")]
328                 public override string GetNameInfo (X509NameType nameType, bool forIssuer) 
329                 {
330                         switch (nameType) {
331                         case X509NameType.SimpleName:
332                                 if (_cert == null)
333                                         throw new CryptographicException (empty_error);
334                                 // return CN= or, if missing, the first part of the DN
335                                 ASN1 sn = forIssuer ? _cert.GetIssuerName () : _cert.GetSubjectName ();
336                                 ASN1 dn = Find (commonName, sn);
337                                 if (dn != null)
338                                         return GetValueAsString (dn);
339                                 if (sn.Count == 0)
340                                         return String.Empty;
341                                 ASN1 last_entry = sn [sn.Count - 1];
342                                 if (last_entry.Count == 0)
343                                         return String.Empty;
344                                 return GetValueAsString (last_entry [0]);
345                         case X509NameType.EmailName:
346                                 // return the E= part of the DN (if present)
347                                 ASN1 e = Find (email, forIssuer ? _cert.GetIssuerName () : _cert.GetSubjectName ());
348                                 if (e != null)
349                                         return GetValueAsString (e);
350                                 return String.Empty;
351                         case X509NameType.UpnName:
352                                 // FIXME - must find/create test case
353                                 return String.Empty;
354                         case X509NameType.DnsName:
355                                 // return the CN= part of the DN (if present)
356                                 ASN1 cn = Find (commonName, forIssuer ? _cert.GetIssuerName () : _cert.GetSubjectName ());
357                                 if (cn != null)
358                                         return GetValueAsString (cn);
359                                 return String.Empty;
360                         case X509NameType.DnsFromAlternativeName:
361                                 // FIXME - must find/create test case
362                                 return String.Empty;
363                         case X509NameType.UrlName:
364                                 // FIXME - must find/create test case
365                                 return String.Empty;
366                         default:
367                                 throw new ArgumentException ("nameType");
368                         }
369                 }
370
371                 static byte[] commonName = { 0x55, 0x04, 0x03 };
372                 static byte[] email = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 };
373
374                 private ASN1 Find (byte[] oid, ASN1 dn)
375                 {
376                         if (dn.Count == 0)
377                                 return null;
378
379                         // process SET
380                         for (int i = 0; i < dn.Count; i++) {
381                                 ASN1 set = dn [i];
382                                 for (int j = 0; j < set.Count; j++) {
383                                         ASN1 pair = set [j];
384                                         if (pair.Count != 2)
385                                                 continue;
386
387                                         ASN1 poid = pair [0];
388                                         if (poid == null)
389                                                 continue;
390
391                                         if (poid.CompareValue (oid))
392                                                 return pair;
393                                 }
394                         }
395                         return null;
396                 }
397
398                 private string GetValueAsString (ASN1 pair)
399                 {
400                         if (pair.Count != 2)
401                                 return String.Empty;
402
403                         ASN1 value = pair [1];
404                         if ((value.Value == null) || (value.Length == 0))
405                                 return String.Empty;
406
407                         if (value.Tag == 0x1E) {
408                                 // BMPSTRING
409                                 StringBuilder sb = new StringBuilder ();
410                                 for (int j = 1; j < value.Value.Length; j += 2)
411                                         sb.Append ((char)value.Value [j]);
412                                 return sb.ToString ();
413                         } else {
414                                 return Encoding.UTF8.GetString (value.Value);
415                         }
416                 }
417
418                 private MX.X509Certificate ImportPkcs12 (byte[] rawData, string password)
419                 {
420                         MX.PKCS12 pfx = null;
421                         if (string.IsNullOrEmpty (password)) {
422                                 try {
423                                         // Support both unencrypted PKCS#12..
424                                         pfx = new MX.PKCS12 (rawData, (string)null);
425                                 } catch {
426                                         // ..and PKCS#12 encrypted with an empty password
427                                         pfx = new MX.PKCS12 (rawData, string.Empty);
428                                 }
429                         } else {
430                                 pfx = new MX.PKCS12 (rawData, password);
431                         }
432
433                         if (pfx.Certificates.Count == 0) {
434                                 // no certificate was found
435                                 return null;
436                         } else if (pfx.Keys.Count == 0) {
437                                 // no key were found - pick the first certificate
438                                 return pfx.Certificates [0];
439                         } else {
440                                 // find the certificate that match the first key
441                                 MX.X509Certificate cert = null;
442                                 var keypair = (pfx.Keys [0] as AsymmetricAlgorithm);
443                                 string pubkey = keypair.ToXmlString (false);
444                                 foreach (var c in pfx.Certificates) {
445                                         if (((c.RSA != null) && (pubkey == c.RSA.ToXmlString (false))) ||
446                                                 ((c.DSA != null) && (pubkey == c.DSA.ToXmlString (false)))) {
447                                                 cert = c;
448                                                 break;
449                                         }
450                                 }
451                                 if (cert == null) {
452                                         cert = pfx.Certificates [0]; // no match, pick first certificate without keys
453                                 } else {
454                                         cert.RSA = (keypair as RSA);
455                                         cert.DSA = (keypair as DSA);
456                                 }
457                                 return cert;
458                         }
459                 }
460
461                 [MonoTODO ("missing KeyStorageFlags support")]
462                 public override void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
463                 {
464                         MX.X509Certificate cert = null;
465                         if (password == null) {
466                                 try {
467                                         cert = new MX.X509Certificate (rawData);
468                                 }
469                                 catch (Exception e) {
470                                         try {
471                                                 cert = ImportPkcs12 (rawData, null);
472                                         }
473                                         catch {
474                                                 string msg = Locale.GetText ("Unable to decode certificate.");
475                                                 // inner exception is the original (not second) exception
476                                                 throw new CryptographicException (msg, e);
477                                         }
478                                 }
479                         } else {
480                                 // try PKCS#12
481                                 try {
482                                         cert = ImportPkcs12 (rawData, password);
483                                 }
484                                 catch {
485                                         // it's possible to supply a (unrequired/unusued) password
486                                         // fix bug #79028
487                                         cert = new MX.X509Certificate (rawData);
488                                 }
489                         }
490                         _cert = cert;
491                 }
492
493                 [MonoTODO ("X509ContentType.SerializedCert is not supported")]
494                 public override byte[] Export (X509ContentType contentType, string password)
495                 {
496                         if (_cert == null)
497                                 throw new CryptographicException (empty_error);
498
499                         switch (contentType) {
500                         case X509ContentType.Cert:
501                                 return _cert.RawData;
502                         case X509ContentType.Pfx: // this includes Pkcs12
503                                 return ExportPkcs12 (password);
504                         case X509ContentType.SerializedCert:
505                                 // TODO
506                                 throw new NotSupportedException ();
507                         default:
508                                 string msg = Locale.GetText ("This certificate format '{0}' cannot be exported.", contentType);
509                                 throw new CryptographicException (msg);
510                         }
511                 }
512
513                 byte[] ExportPkcs12 (string password)
514                 {
515                         var pfx = new MX.PKCS12 ();
516                         try {
517                                 var attrs = new Hashtable ();
518                                 var localKeyId = new ArrayList ();
519                                 localKeyId.Add (new byte[] { 1, 0, 0, 0 });
520                                 attrs.Add (MX.PKCS9.localKeyId, localKeyId);
521
522                                 if (password != null)
523                                         pfx.Password = password;
524                                 pfx.AddCertificate (_cert, attrs);
525                                 var privateKey = PrivateKey;
526                                 if (privateKey != null)
527                                         pfx.AddPkcs8ShroudedKeyBag (privateKey, attrs);
528                                 return pfx.GetBytes ();
529                         } finally {
530                                 pfx.Password = null;
531                         }
532                 }
533
534                 public override void Reset () 
535                 {
536                         _cert = null;
537                         _archived = false;
538                         _extensions = null;
539                         _serial = null;
540                         _publicKey = null;
541                         issuer_name = null;
542                         subject_name = null;
543                         signature_algorithm = null;
544                 }
545
546                 public override string ToString ()
547                 {
548                         if (_cert == null)
549                                 return "System.Security.Cryptography.X509Certificates.X509Certificate2";
550
551                         return ToString (true);
552                 }
553
554                 public override string ToString (bool verbose)
555                 {
556                         if (_cert == null)
557                                 return "System.Security.Cryptography.X509Certificates.X509Certificate2";
558
559                         string nl = Environment.NewLine;
560                         StringBuilder sb = new StringBuilder ();
561
562                         // the non-verbose X509Certificate2 == verbose X509Certificate
563                         if (!verbose) {
564                                 sb.AppendFormat ("[Subject]{0}  {1}{0}{0}", nl, GetSubjectName (false));
565                                 sb.AppendFormat ("[Issuer]{0}  {1}{0}{0}", nl, GetIssuerName (false));
566                                 sb.AppendFormat ("[Not Before]{0}  {1}{0}{0}", nl, GetValidFrom ().ToLocalTime ());
567                                 sb.AppendFormat ("[Not After]{0}  {1}{0}{0}", nl, GetValidUntil ().ToLocalTime ());
568                                 sb.AppendFormat ("[Thumbprint]{0}  {1}{0}", nl, X509Helper.ToHexString (GetCertHash ()));
569                                 sb.Append (nl);
570                                 return sb.ToString ();
571                         }
572
573                         sb.AppendFormat ("[Version]{0}  V{1}{0}{0}", nl, Version);
574                         sb.AppendFormat ("[Subject]{0}  {1}{0}{0}", nl, GetSubjectName (false));
575                         sb.AppendFormat ("[Issuer]{0}  {1}{0}{0}", nl, GetIssuerName (false));
576                         sb.AppendFormat ("[Serial Number]{0}  {1}{0}{0}", nl, GetSerialNumber ());
577                         sb.AppendFormat ("[Not Before]{0}  {1}{0}{0}", nl, GetValidFrom ().ToLocalTime ());
578                         sb.AppendFormat ("[Not After]{0}  {1}{0}{0}", nl, GetValidUntil ().ToLocalTime ());
579                         sb.AppendFormat ("[Thumbprint]{0}  {1}{0}", nl, X509Helper.ToHexString (GetCertHash ()));
580                         sb.AppendFormat ("[Signature Algorithm]{0}  {1}({2}){0}{0}", nl, SignatureAlgorithm.FriendlyName, 
581                                 SignatureAlgorithm.Value);
582
583                         AsymmetricAlgorithm key = PublicKey.Key;
584                         sb.AppendFormat ("[Public Key]{0}  Algorithm: ", nl);
585                         if (key is RSA)
586                                 sb.Append ("RSA");
587                         else if (key is DSA)
588                                 sb.Append ("DSA");
589                         else
590                                 sb.Append (key.ToString ());
591                         sb.AppendFormat ("{0}  Length: {1}{0}  Key Blob: ", nl, key.KeySize);
592                         AppendBuffer (sb, PublicKey.EncodedKeyValue.RawData);
593                         sb.AppendFormat ("{0}  Parameters: ", nl);
594                         AppendBuffer (sb, PublicKey.EncodedParameters.RawData);
595                         sb.Append (nl);
596
597                         return sb.ToString ();
598                 }
599
600                 private static void AppendBuffer (StringBuilder sb, byte[] buffer)
601                 {
602                         if (buffer == null)
603                                 return;
604                         for (int i=0; i < buffer.Length; i++) {
605                                 sb.Append (buffer [i].ToString ("x2"));
606                                 if (i < buffer.Length - 1)
607                                         sb.Append (" ");
608                         }
609                 }
610
611                 [MonoTODO ("by default this depends on the incomplete X509Chain")]
612                 public override bool Verify (X509Certificate2 thisCertificate)
613                 {
614                         if (_cert == null)
615                                 throw new CryptographicException (empty_error);
616
617                         X509Chain chain = X509Chain.Create ();
618                         if (!chain.Build (thisCertificate))
619                                 return false;
620                         // TODO - check chain and other stuff ???
621                         return true;
622                 }
623
624                 // static methods
625
626                 private static byte[] signedData = new byte[] { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 };
627
628                 [MonoTODO ("Detection limited to Cert, Pfx, Pkcs12, Pkcs7 and Unknown")]
629                 public static X509ContentType GetCertContentType (byte[] rawData)
630                 {
631                         if ((rawData == null) || (rawData.Length == 0))
632                                 throw new ArgumentException ("rawData");
633
634                         X509ContentType type = X509ContentType.Unknown;
635                         try {
636                                 ASN1 data = new ASN1 (rawData);
637                                 if (data.Tag != 0x30) {
638                                         string msg = Locale.GetText ("Unable to decode certificate.");
639                                         throw new CryptographicException (msg);
640                                 }
641
642                                 if (data.Count == 0)
643                                         return type;
644
645                                 if (data.Count == 3) {
646                                         switch (data [0].Tag) {
647                                         case 0x30:
648                                                 // SEQUENCE / SEQUENCE / BITSTRING
649                                                 if ((data [1].Tag == 0x30) && (data [2].Tag == 0x03))
650                                                         type = X509ContentType.Cert;
651                                                 break;
652                                         case 0x02:
653                                                 // INTEGER / SEQUENCE / SEQUENCE
654                                                 if ((data [1].Tag == 0x30) && (data [2].Tag == 0x30))
655                                                         type = X509ContentType.Pkcs12;
656                                                 // note: Pfx == Pkcs12
657                                                 break;
658                                         }
659                                 }
660                                 // check for PKCS#7 (count unknown but greater than 0)
661                                 // SEQUENCE / OID (signedData)
662                                 if ((data [0].Tag == 0x06) && data [0].CompareValue (signedData))
663                                         type = X509ContentType.Pkcs7;
664                         }
665                         catch (Exception e) {
666                                 string msg = Locale.GetText ("Unable to decode certificate.");
667                                 throw new CryptographicException (msg, e);
668                         }
669
670                         return type;
671                 }
672
673                 [MonoTODO ("Detection limited to Cert, Pfx, Pkcs12 and Unknown")]
674                 public static X509ContentType GetCertContentType (string fileName)
675                 {
676                         if (fileName == null)
677                                 throw new ArgumentNullException ("fileName");
678                         if (fileName.Length == 0)
679                                 throw new ArgumentException ("fileName");
680
681                         byte[] data = File.ReadAllBytes (fileName);
682                         return GetCertContentType (data);
683                 }
684
685                 // internal stuff because X509Certificate2 isn't complete enough
686                 // (maybe X509Certificate3 will be better?)
687
688                 internal MX.X509Certificate MonoCertificate {
689                         get { return _cert; }
690                 }
691         }
692 }
693
694 #endif