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