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