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