2004-12-03 Lluis Sanchez Gual <lluis@novell.com>
[mono.git] / mcs / class / corlib / System.Security.Cryptography.X509Certificates / X509Certificate.cs
1 //
2 // X509Certificates.cs: Handles X.509 certificates.
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004 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 using System.IO;
31 using System.Runtime.InteropServices;
32 using System.Text;
33
34 using Mono.Security;
35 using Mono.Security.Authenticode;
36 using Mono.Security.X509;
37
38 #if NET_2_0
39 using System.Runtime.Serialization;
40 #endif
41
42 namespace System.Security.Cryptography.X509Certificates {
43
44         // References:
45         // a.   Internet X.509 Public Key Infrastructure Certificate and CRL Profile
46         //      http://www.ietf.org/rfc/rfc3280.txt
47         
48         // LAMESPEC: the MSDN docs always talks about X509v3 certificates
49         // and/or Authenticode certs. However this class works with older
50         // X509v1 certificates and non-authenticode (code signing) certs.
51         [Serializable]
52 #if NET_2_0
53         public class X509Certificate : IDeserializationCallback, ISerializable {
54 #else
55         public class X509Certificate {
56 #endif
57                 // typedef struct _CERT_CONTEXT {
58                 //      DWORD                   dwCertEncodingType;
59                 //      BYTE                    *pbCertEncoded;
60                 //      DWORD                   cbCertEncoded;
61                 //      PCERT_INFO              pCertInfo;
62                 //      HCERTSTORE              hCertStore;
63                 // } CERT_CONTEXT, *PCERT_CONTEXT;
64                 // typedef const CERT_CONTEXT *PCCERT_CONTEXT;
65                 [StructLayout (LayoutKind.Sequential)]
66                 internal struct CertificateContext {
67                         public UInt32 dwCertEncodingType;
68                         public IntPtr pbCertEncoded;
69                         public UInt32 cbCertEncoded;
70                         public IntPtr pCertInfo;
71                         public IntPtr hCertStore;
72                 }
73                 // NOTE: We only define the CryptoAPI structure (from WINCRYPT.H)
74                 // so we don't create any dependencies on Windows DLL in corlib
75
76                 private Mono.Security.X509.X509Certificate x509;
77                 private bool hideDates;
78                 private byte[] cachedCertificateHash;
79         
80                 // almost every byte[] returning function has a string equivalent
81                 // sadly the BitConverter insert dash between bytes :-(
82                 private string tostr (byte[] data) 
83                 {
84                         if (data != null) {
85                                 StringBuilder sb = new StringBuilder ();
86                                 for (int i = 0; i < data.Length; i++)
87                                         sb.Append (data[i].ToString ("X2"));
88                                 return sb.ToString ();
89                         }
90                         else
91                                 return null;
92                 }
93         
94                 // static methods
95         
96                 public static X509Certificate CreateFromCertFile (string filename) 
97                 {
98                         byte[] data = null;
99                         using (FileStream fs = new FileStream (filename, FileMode.Open)) {
100                                 data = new byte [fs.Length];
101                                 fs.Read (data, 0, data.Length);
102                                 fs.Close ();
103                         }
104                         return new X509Certificate (data);
105                 }
106         
107                 [MonoTODO ("Incomplete - minimal validation in this version")]
108                 public static X509Certificate CreateFromSignedFile (string filename)
109                 {
110                         try {
111                                 AuthenticodeDeformatter a = new AuthenticodeDeformatter (filename);
112                                 if (a.SigningCertificate != null) {
113                                         if (a.Reason != 0) {
114                                                 string msg = String.Format (Locale.GetText (
115                                                         "Invalid digital signature on {0}, reason #{1}."),
116                                                         filename, a.Reason);
117                                                 throw new COMException (msg);
118                                         }
119                                         return new X509Certificate (a.SigningCertificate.RawData);
120                                 }
121
122                                 // if no signature is present return an empty certificate
123                                 byte[] cert = null; // must not confuse compiler about null ;)
124                                 return new X509Certificate (cert);
125                         }
126                         catch (Exception e) {
127                                 string msg = String.Format (Locale.GetText ("Couldn't extract digital signature from {0}."), filename);
128                                 throw new COMException (msg, e);
129                         }
130                 }
131         
132                 // constructors
133         
134                 // special constructor for Publisher (and related classes).
135                 // Dates strings are null
136                 internal X509Certificate (byte[] data, bool dates) 
137                 {
138                         if (data != null) {
139                                 x509 = new Mono.Security.X509.X509Certificate (data);
140                                 hideDates = !dates;
141                         }
142                 }
143         
144                 public X509Certificate (byte[] data) : this (data, true)
145                 {
146                 }
147         
148                 public X509Certificate (IntPtr handle) 
149                 {
150                         CertificateContext cc = (CertificateContext) Marshal.PtrToStructure (handle, typeof (CertificateContext));
151                         byte[] data = new byte [cc.cbCertEncoded];
152                         Marshal.Copy (cc.pbCertEncoded, data, 0, (int)cc.cbCertEncoded);
153                         x509 = new Mono.Security.X509.X509Certificate (data);
154                 }
155         
156                 public X509Certificate (System.Security.Cryptography.X509Certificates.X509Certificate cert) 
157                 {
158                         if (cert != null) {
159                                 byte[] data = cert.GetRawCertData ();
160                                 if (data != null)
161                                         x509 = new Mono.Security.X509.X509Certificate (data);
162                                 hideDates = false;
163                         }
164                 }
165
166 #if NET_2_0
167                 [MonoTODO]
168                 public X509Certificate ()
169                 {
170                 }
171
172                 [MonoTODO]
173                 public X509Certificate (byte[] rawData, string password)
174                 {
175                         Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
176                 }
177
178                 [MonoTODO]
179                 public X509Certificate (byte[] rawData, SecureString password)
180                 {
181                         Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
182                 }
183
184                 [MonoTODO]
185                 public X509Certificate (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
186                 {
187                         Import (rawData, password, keyStorageFlags);
188                 }
189
190                 [MonoTODO]
191                 public X509Certificate (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
192                 {
193                         Import (rawData, password, keyStorageFlags);
194                 }
195
196                 [MonoTODO]
197                 public X509Certificate (string fileName)
198                 {
199                         Import (fileName, (byte[])null, X509KeyStorageFlags.DefaultKeySet);
200                 }
201
202                 [MonoTODO]
203                 public X509Certificate (string fileName, string password)
204                 {
205                         Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
206                 }
207
208                 [MonoTODO]
209                 public X509Certificate (string fileName, SecureString password)
210                 {
211                         Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
212                 }
213
214                 [MonoTODO]
215                 public X509Certificate (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
216                 {
217                         Import (fileName, password, keyStorageFlags);
218                 }
219
220                 [MonoTODO]
221                 public X509Certificate (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
222                 {
223                         Import (fileName, password, keyStorageFlags);
224                 }
225
226                 [MonoTODO]
227                 public X509Certificate (SerializationInfo info, StreamingContext context)
228                 {
229                 }
230 #endif
231
232                 // public methods
233         
234                 public virtual bool Equals (System.Security.Cryptography.X509Certificates.X509Certificate cert)
235                 {
236                         if (cert != null) {
237                                 byte[] raw = cert.GetRawCertData ();
238                                 if (raw != null) {
239                                         if (x509 == null)
240                                                 return false;
241                                         if (x509.RawData == null)
242                                                 return false;
243                                         if (raw.Length == x509.RawData.Length) {
244                                                 for (int i = 0; i < raw.Length; i++) {
245                                                         if (raw[i] != x509.RawData [i])
246                                                                 return false;
247                                                 }
248                                                 // well no choice must be equals!
249                                                 return true;
250                                         }
251                                         else
252                                                 return false;
253                                 }
254                         }
255                         return (x509.RawData == null);
256                 }
257         
258                 // LAMESPEC: This is the equivalent of the "thumbprint" that can be seen
259                 // in the certificate viewer of Windows. This is ALWAYS the SHA1 hash of
260                 // the certificate (i.e. it has nothing to do with the actual hash 
261                 // algorithm used to sign the certificate).
262                 public virtual byte[] GetCertHash () 
263                 {
264                         // we'll hash the cert only once and only if required
265                         if ((cachedCertificateHash == null) && (x509 != null)) {
266                                 SHA1 sha = SHA1.Create ();
267                                 cachedCertificateHash = sha.ComputeHash (x509.RawData);
268                         }
269                         return cachedCertificateHash;
270                 }
271         
272                 public virtual string GetCertHashString () 
273                 {
274                         // must call GetCertHash (not variable) or optimization wont work
275                         return tostr (GetCertHash ());
276                 }
277         
278                 // strangly there are no DateTime returning function
279                 // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
280                 // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
281                 public virtual string GetEffectiveDateString ()
282                 {
283                         if (hideDates)
284                                 return null;
285                         DateTime dt = x509.ValidFrom.ToUniversalTime().AddHours (-8);
286                         return dt.ToString (); //"yyyy-MM-dd HH:mm:ss");
287                 }
288         
289                 // strangly there are no DateTime returning function
290                 // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
291                 // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
292                 public virtual string GetExpirationDateString () 
293                 {
294                         if (hideDates)
295                                 return null;
296                         DateTime dt = x509.ValidUntil.ToUniversalTime().AddHours (-8);
297                         return dt.ToString (); //"yyyy-MM-dd HH:mm:ss");
298                 }
299         
300                 // well maybe someday there'll be support for PGP or SPKI ?
301                 public virtual string GetFormat () 
302                 {
303                         return "X509";  // DO NOT TRANSLATE
304                 }
305         
306                 public override int GetHashCode ()
307                 {
308                         // the cert hash may not be (yet) calculated
309                         if (cachedCertificateHash == null)
310                                 GetCertHash();
311                 
312                         // return the integer of the first 4 bytes of the cert hash
313                         if ((cachedCertificateHash != null) && (cachedCertificateHash.Length >= 4))
314                                 return ((cachedCertificateHash[0] << 24) |(cachedCertificateHash[1] << 16) |
315                                         (cachedCertificateHash[2] << 8) | cachedCertificateHash[3]);
316                         else
317                                 return 0;
318                 }
319         
320                 public virtual string GetIssuerName () 
321                 {
322                         return x509.IssuerName;
323                 }
324         
325                 public virtual string GetKeyAlgorithm () 
326                 {
327                         return x509.KeyAlgorithm;
328                 }
329         
330                 public virtual byte[] GetKeyAlgorithmParameters () 
331                 {
332                         return x509.KeyAlgorithmParameters;
333                 }
334         
335                 public virtual string GetKeyAlgorithmParametersString () 
336                 {
337                         return tostr (x509.KeyAlgorithmParameters);
338                 }
339         
340                 public virtual string GetName ()
341                 {
342                         return x509.SubjectName;
343                 }
344         
345                 public virtual byte[] GetPublicKey () 
346                 {
347                         return x509.PublicKey;
348                 }
349         
350                 public virtual string GetPublicKeyString () 
351                 {
352                         return tostr (x509.PublicKey);
353                 }
354         
355                 public virtual byte[] GetRawCertData () 
356                 {
357                         return ((x509 != null) ? x509.RawData : null);
358                 }
359         
360                 public virtual string GetRawCertDataString () 
361                 {
362                         return ((x509 != null) ? tostr (x509.RawData) : null);
363                 }
364         
365                 public virtual byte[] GetSerialNumber () 
366                 {
367                         return x509.SerialNumber;
368                 }
369         
370                 public virtual string GetSerialNumberString () 
371                 {
372                         return tostr (x509.SerialNumber);
373                 }
374         
375                 // to please corcompare ;-)
376                 public override string ToString () 
377                 {
378                         return base.ToString ();
379                 }
380         
381                 public virtual string ToString (bool details) 
382                 {
383                         if (details) {
384                                 string nl = Environment.NewLine;
385                                 StringBuilder sb = new StringBuilder ();
386                                 sb.Append ("CERTIFICATE:");
387                                 sb.Append (nl);
388                                 sb.Append ("\tFormat:  ");
389                                 sb.Append (GetFormat ());
390                                 if (x509.SubjectName != null) {
391                                         sb.Append (nl);
392                                         sb.Append ("\tName:  ");
393                                         sb.Append (GetName ());
394                                 }
395                                 if (x509.IssuerName != null) {
396                                         sb.Append (nl);
397                                         sb.Append ("\tIssuing CA:  ");
398                                         sb.Append (GetIssuerName ());
399                                 }
400                                 if (x509.SignatureAlgorithm != null) {
401                                         sb.Append (nl);
402                                         sb.Append ("\tKey Algorithm:  ");
403                                         sb.Append (GetKeyAlgorithm ());
404                                 }
405                                 if (x509.SerialNumber != null) {
406                                         sb.Append (nl);
407                                         sb.Append ("\tSerial Number:  ");
408                                         sb.Append (GetSerialNumberString ());
409                                 }
410                                 // Note: Algorithm is not spelled right as the actual 
411                                 // MS implementation (we do exactly the same for the
412                                 // comparison in the unit tests)
413                                 if (x509.KeyAlgorithmParameters != null) {
414                                         sb.Append (nl);
415                                         sb.Append ("\tKey Alogrithm Parameters:  ");
416                                         sb.Append (GetKeyAlgorithmParametersString ());
417                                 }
418                                 if (x509.PublicKey != null) {
419                                         sb.Append (nl);
420                                         sb.Append ("\tPublic Key:  ");
421                                         sb.Append (GetPublicKeyString ());
422                                 }
423                                 sb.Append (nl);
424                                 sb.Append (nl);
425                                 return sb.ToString ();
426                         }
427                         else
428                                 return base.ToString ();
429                 }
430
431 #if NET_2_0
432                 [ComVisible (false)]
433                 public override bool Equals (object obj) 
434                 {
435                         X509Certificate x = (obj as X509Certificate);
436                         if (x != null)
437                                 return this.Equals (x);
438                         return false;
439                 }
440
441                 [MonoTODO ("incomplete")]
442                 [ComVisible (false)]
443                 public virtual byte[] Export (X509ContentType contentType)
444                 {
445                         return Export (contentType, (byte[])null);
446                 }
447
448                 [MonoTODO ("incomplete")]
449                 [ComVisible (false)]
450                 public virtual byte[] Export (X509ContentType contentType, string password)
451                 {
452                         return Export (contentType, Encoding.UTF8.GetBytes (password));
453                 }
454
455                 [MonoTODO ("incomplete")]
456                 [ComVisible (false)]
457                 public virtual byte[] Export (X509ContentType contentType, SecureString password)
458                 {
459                         return Export (contentType, password.GetBuffer ());
460                 }
461
462                 [MonoTODO ("export!")]
463                 internal byte[] Export (X509ContentType contentType, byte[] password)
464                 {
465                         try {
466                                 switch (contentType) {
467                                 case X509ContentType.Cert:
468                                         return x509.RawData;
469                                 default:
470                                         throw new NotSupportedException ();
471                                 }
472                         }
473                         finally {
474                                 // protect password
475                                 if (password != null)
476                                         Array.Clear (password, 0, password.Length);
477                         }
478                 }
479
480                 [MonoTODO]
481                 void IDeserializationCallback.OnDeserialization (object sender)
482                 {
483                 }
484
485                 [MonoTODO ("incomplete")]
486                 [ComVisible (false)]
487                 public virtual void Import (byte[] rawData)
488                 {
489                         Import (rawData, (byte[])null, X509KeyStorageFlags.DefaultKeySet);
490                 }
491
492                 [MonoTODO ("incomplete")]
493                 [ComVisible (false)]
494                 public virtual void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
495                 {
496                         Import (rawData, Encoding.UTF8.GetBytes (password), keyStorageFlags);
497                 }
498
499                 [MonoTODO ("incomplete")]
500                 [ComVisible (false)]
501                 public virtual void Import (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
502                 {
503                         Import (rawData, password.GetBuffer (), keyStorageFlags);
504                 }
505
506                 [MonoTODO ("import!")]
507                 internal void Import (byte[] rawData, byte[] password, X509KeyStorageFlags keyStorageFlags)
508                 {
509                         try {
510                                 if (password == null) {
511                                         x509 = new Mono.Security.X509.X509Certificate (rawData);
512                                 }
513                                 else {
514                                         // TODO
515                                         throw new NotSupportedException ();
516                                 }
517                         }
518                         finally {
519                                 // protect password
520                                 if (password != null)
521                                         Array.Clear (password, 0, password.Length);
522                         }
523                 }
524
525                 [MonoTODO ("incomplete")]
526                 [ComVisible (false)]
527                 public virtual void Import (string fileName)
528                 {
529                         Import (fileName, (byte[])null, X509KeyStorageFlags.DefaultKeySet);
530                 }
531
532                 [MonoTODO ("incomplete - is the password UTF8, ASCII, Unicode ?")]
533                 [ComVisible (false)]
534                 public virtual void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
535                 {
536                         Import (fileName, Encoding.UTF8.GetBytes (password), keyStorageFlags);
537                 }
538
539                 [MonoTODO ("incomplete")]
540                 [ComVisible (false)]
541                 public virtual void Import (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
542                 {
543                         Import (fileName, password.GetBuffer (), keyStorageFlags);
544                 }
545
546                 internal void Import (string fileName, byte[] password, X509KeyStorageFlags keyStorageFlags)
547                 {
548                         try {
549                                 using (FileStream fs = new FileStream (fileName, FileMode.Open)) {
550                                         byte[] data = new byte [fs.Length];
551                                         fs.Read (data, 0, data.Length);
552                                         fs.Close ();
553                                         Import (data, password, keyStorageFlags);
554                                 }
555                         }
556                         finally {
557                                 // protect password
558                                 if (password != null)
559                                         Array.Clear (password, 0, password.Length);
560                         }
561                 }
562
563                 [MonoTODO]
564                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
565                 {
566                 }
567
568                 [MonoTODO]
569                 [ComVisible (false)]
570                 public virtual void Reset ()
571                 {
572                 }
573
574                 // properties
575
576                 [ComVisible (false)]
577                 public IntPtr Handle {
578                         get { return (IntPtr) 0; }
579                 }
580 #endif
581         }
582 }