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