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