New test.
[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-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 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 = File.OpenRead (filename)) {
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 #if NET_2_0
164                         else
165                                 throw new ArgumentException ("Invalid handle.");
166 #endif
167                         // IntPtr.Zero results in an "empty" certificate instance
168                 }
169         
170                 public X509Certificate (System.Security.Cryptography.X509Certificates.X509Certificate cert) 
171                 {
172 #if NET_2_0
173                         if (cert == null)
174                                 throw new ArgumentNullException ();
175 #endif
176
177                         if (cert != null) {
178                                 byte[] data = cert.GetRawCertData ();
179                                 if (data != null)
180                                         x509 = new Mono.Security.X509.X509Certificate (data);
181                                 hideDates = false;
182                         }
183                 }
184
185 #if NET_2_0
186                 [MonoTODO]
187                 public X509Certificate ()
188                 {
189                 }
190
191                 [MonoTODO]
192                 public X509Certificate (byte[] rawData, string password)
193                 {
194                         Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
195                 }
196
197                 [MonoTODO]
198                 public X509Certificate (byte[] rawData, SecureString password)
199                 {
200                         Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
201                 }
202
203                 [MonoTODO]
204                 public X509Certificate (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
205                 {
206                         Import (rawData, password, keyStorageFlags);
207                 }
208
209                 [MonoTODO]
210                 public X509Certificate (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
211                 {
212                         Import (rawData, password, keyStorageFlags);
213                 }
214
215                 [MonoTODO]
216                 public X509Certificate (string fileName)
217                 {
218                         Import (fileName, (string)null, X509KeyStorageFlags.DefaultKeySet);
219                 }
220
221                 [MonoTODO]
222                 public X509Certificate (string fileName, string password)
223                 {
224                         Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
225                 }
226
227                 [MonoTODO]
228                 public X509Certificate (string fileName, SecureString password)
229                 {
230                         Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
231                 }
232
233                 [MonoTODO]
234                 public X509Certificate (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
235                 {
236                         Import (fileName, password, keyStorageFlags);
237                 }
238
239                 [MonoTODO]
240                 public X509Certificate (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
241                 {
242                         Import (fileName, password, keyStorageFlags);
243                 }
244
245                 [MonoTODO]
246                 public X509Certificate (SerializationInfo info, StreamingContext context)
247                 {
248                 }
249 #endif
250
251                 // public methods
252         
253                 public virtual bool Equals (System.Security.Cryptography.X509Certificates.X509Certificate cert)
254                 {
255                         if (cert != null) {
256                                 byte[] raw = cert.GetRawCertData ();
257                                 if (raw != null) {
258                                         if (x509 == null)
259                                                 return false;
260                                         if (x509.RawData == null)
261                                                 return false;
262                                         if (raw.Length == x509.RawData.Length) {
263                                                 for (int i = 0; i < raw.Length; i++) {
264                                                         if (raw[i] != x509.RawData [i])
265                                                                 return false;
266                                                 }
267                                                 // well no choice must be equals!
268                                                 return true;
269                                         }
270                                         else
271                                                 return false;
272                                 }
273                         }
274                         else
275                                 return false;
276                         return x509 == null || (x509.RawData == null);
277                 }
278         
279                 // LAMESPEC: This is the equivalent of the "thumbprint" that can be seen
280                 // in the certificate viewer of Windows. This is ALWAYS the SHA1 hash of
281                 // the certificate (i.e. it has nothing to do with the actual hash 
282                 // algorithm used to sign the certificate).
283                 public virtual byte[] GetCertHash () 
284                 {
285                         // we'll hash the cert only once and only if required
286                         if ((cachedCertificateHash == null) && (x509 != null)) {
287                                 SHA1 sha = SHA1.Create ();
288                                 cachedCertificateHash = sha.ComputeHash (x509.RawData);
289                         }
290                         return cachedCertificateHash;
291                 }
292         
293                 public virtual string GetCertHashString () 
294                 {
295                         // must call GetCertHash (not variable) or optimization wont work
296                         return tostr (GetCertHash ());
297                 }
298         
299                 // strangly there are no DateTime returning function
300                 public virtual string GetEffectiveDateString ()
301                 {
302                         if (hideDates)
303                                 return null;
304 #if NET_2_0
305                         return x509.ValidFrom.ToString ();
306 #else
307                         // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
308                         // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
309                         return x509.ValidFrom.ToUniversalTime ().AddHours (-8).ToString ();
310 #endif
311                 }
312         
313                 // strangly there are no DateTime returning function
314                 public virtual string GetExpirationDateString () 
315                 {
316                         if (hideDates)
317                                 return null;
318 #if NET_2_0
319                         return x509.ValidUntil.ToString ();
320 #else
321                         // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
322                         // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
323                         return x509.ValidUntil.ToUniversalTime ().AddHours (-8).ToString ();
324 #endif
325                 }
326         
327                 // well maybe someday there'll be support for PGP or SPKI ?
328                 public virtual string GetFormat () 
329                 {
330                         return "X509";  // DO NOT TRANSLATE
331                 }
332         
333                 public override int GetHashCode ()
334                 {
335                         // the cert hash may not be (yet) calculated
336                         if (cachedCertificateHash == null)
337                                 GetCertHash();
338                 
339                         // return the integer of the first 4 bytes of the cert hash
340                         if ((cachedCertificateHash != null) && (cachedCertificateHash.Length >= 4))
341                                 return ((cachedCertificateHash[0] << 24) |(cachedCertificateHash[1] << 16) |
342                                         (cachedCertificateHash[2] << 8) | cachedCertificateHash[3]);
343                         else
344                                 return 0;
345                 }
346
347 #if NET_2_0
348                 [Obsolete ("Use the Issuer property.")]
349 #endif
350                 public virtual string GetIssuerName () 
351                 {
352                         return x509.IssuerName;
353                 }
354         
355                 public virtual string GetKeyAlgorithm () 
356                 {
357                         return x509.KeyAlgorithm;
358                 }
359         
360                 public virtual byte[] GetKeyAlgorithmParameters () 
361                 {
362                         return x509.KeyAlgorithmParameters;
363                 }
364         
365                 public virtual string GetKeyAlgorithmParametersString () 
366                 {
367                         return tostr (x509.KeyAlgorithmParameters);
368                 }
369         
370 #if NET_2_0
371                 [Obsolete ("Use the Subject property.")]
372 #endif
373                 public virtual string GetName ()
374                 {
375                         return x509.SubjectName;
376                 }
377         
378                 public virtual byte[] GetPublicKey () 
379                 {
380                         return x509.PublicKey;
381                 }
382         
383                 public virtual string GetPublicKeyString () 
384                 {
385                         return tostr (x509.PublicKey);
386                 }
387         
388                 public virtual byte[] GetRawCertData () 
389                 {
390                         return ((x509 != null) ? x509.RawData : null);
391                 }
392         
393                 public virtual string GetRawCertDataString () 
394                 {
395                         return ((x509 != null) ? tostr (x509.RawData) : null);
396                 }
397         
398                 public virtual byte[] GetSerialNumber () 
399                 {
400                         return x509.SerialNumber;
401                 }
402         
403                 public virtual string GetSerialNumberString () 
404                 {
405                         return tostr (x509.SerialNumber);
406                 }
407         
408                 // to please corcompare ;-)
409                 public override string ToString () 
410                 {
411                         return base.ToString ();
412                 }
413         
414                 public virtual string ToString (bool details) 
415                 {
416                         if (details) {
417                                 string nl = Environment.NewLine;
418                                 StringBuilder sb = new StringBuilder ();
419                                 sb.Append ("CERTIFICATE:");
420                                 sb.Append (nl);
421                                 sb.Append ("\tFormat:  ");
422                                 sb.Append (GetFormat ());
423                                 if (x509.SubjectName != null) {
424                                         sb.Append (nl);
425                                         sb.Append ("\tName:  ");
426                                         sb.Append (GetName ());
427                                 }
428                                 if (x509.IssuerName != null) {
429                                         sb.Append (nl);
430                                         sb.Append ("\tIssuing CA:  ");
431                                         sb.Append (GetIssuerName ());
432                                 }
433                                 if (x509.SignatureAlgorithm != null) {
434                                         sb.Append (nl);
435                                         sb.Append ("\tKey Algorithm:  ");
436                                         sb.Append (GetKeyAlgorithm ());
437                                 }
438                                 if (x509.SerialNumber != null) {
439                                         sb.Append (nl);
440                                         sb.Append ("\tSerial Number:  ");
441                                         sb.Append (GetSerialNumberString ());
442                                 }
443                                 // Note: Algorithm is not spelled right as the actual 
444                                 // MS implementation (we do exactly the same for the
445                                 // comparison in the unit tests)
446                                 if (x509.KeyAlgorithmParameters != null) {
447                                         sb.Append (nl);
448                                         sb.Append ("\tKey Alogrithm Parameters:  ");
449                                         sb.Append (GetKeyAlgorithmParametersString ());
450                                 }
451                                 if (x509.PublicKey != null) {
452                                         sb.Append (nl);
453                                         sb.Append ("\tPublic Key:  ");
454                                         sb.Append (GetPublicKeyString ());
455                                 }
456                                 sb.Append (nl);
457                                 sb.Append (nl);
458                                 return sb.ToString ();
459                         }
460                         else
461                                 return base.ToString ();
462                 }
463
464 #if NET_2_0
465                 public string Issuer {
466                         get { return x509.IssuerName; }
467                 }
468
469                 public string Subject {
470                         get { return x509.SubjectName; }
471                 }
472
473                 [ComVisible (false)]
474                 public override bool Equals (object obj) 
475                 {
476                         X509Certificate x = (obj as X509Certificate);
477                         if (x != null)
478                                 return this.Equals (x);
479                         return false;
480                 }
481
482                 [MonoTODO ("incomplete")]
483                 [ComVisible (false)]
484                 public virtual byte[] Export (X509ContentType contentType)
485                 {
486                         return Export (contentType, (byte[])null);
487                 }
488
489                 [MonoTODO ("incomplete")]
490                 [ComVisible (false)]
491                 public virtual byte[] Export (X509ContentType contentType, string password)
492                 {
493                         return Export (contentType, Encoding.UTF8.GetBytes (password));
494                 }
495
496                 [MonoTODO ("incomplete")]
497                 public virtual byte[] Export (X509ContentType contentType, SecureString password)
498                 {
499                         return Export (contentType, password.GetBuffer ());
500                 }
501
502                 [MonoTODO ("export!")]
503                 internal byte[] Export (X509ContentType contentType, byte[] password)
504                 {
505                         try {
506                                 switch (contentType) {
507                                 case X509ContentType.Cert:
508                                         return x509.RawData;
509                                 default:
510                                         throw new NotSupportedException ();
511                                 }
512                         }
513                         finally {
514                                 // protect password
515                                 if (password != null)
516                                         Array.Clear (password, 0, password.Length);
517                         }
518                 }
519
520                 [MonoTODO]
521                 void IDeserializationCallback.OnDeserialization (object sender)
522                 {
523                 }
524
525                 [ComVisible (false)]
526                 public virtual void Import (byte[] rawData)
527                 {
528                         Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
529                 }
530
531                 [MonoTODO ("missing KeyStorageFlags support")]
532                 [ComVisible (false)]
533                 public virtual void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
534                 {
535                         if (password == null) {
536                                 x509 = new Mono.Security.X509.X509Certificate (rawData);
537                                 // TODO - PKCS12 without password
538                         } else {
539                                 // try PKCS#12
540                                 try {
541                                         PKCS12 pfx = new PKCS12 (rawData, password);
542                                         if (pfx.Certificates.Count > 0) {
543                                                 x509 = pfx.Certificates [0];
544                                         } else {
545                                                 x509 = null;
546                                         }
547                                 }
548                                 catch {
549                                         // it's possible to supply a (unrequired/unusued) password
550                                         // fix bug #79028
551                                         x509 = new Mono.Security.X509.X509Certificate (rawData);
552                                 }
553                         }
554                 }
555
556                 [MonoTODO ("SecureString is incomplete")]
557                 public virtual void Import (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
558                 {
559                         Import (rawData, (string)null, keyStorageFlags);
560                 }
561
562                 [ComVisible (false)]
563                 public virtual void Import (string fileName)
564                 {
565                         byte[] rawData = Load (fileName);
566                         Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
567                 }
568
569                 [MonoTODO ("missing KeyStorageFlags support")]
570                 [ComVisible (false)]
571                 public virtual void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
572                 {
573                         byte[] rawData = Load (fileName);
574                         Import (rawData, password, keyStorageFlags);
575                 }
576
577                 [MonoTODO ("SecureString is incomplete")]
578                 public virtual void Import (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
579                 {
580                         byte[] rawData = Load (fileName);
581                         Import (rawData, (string)null, keyStorageFlags);
582                 }
583
584                 private byte[] Load (string fileName)
585                 {
586                         byte[] data = null;
587                         using (FileStream fs = new FileStream (fileName, FileMode.Open)) {
588                                 data = new byte [fs.Length];
589                                 fs.Read (data, 0, data.Length);
590                                 fs.Close ();
591                         }
592                         return data;
593                 }
594
595                 [MonoTODO]
596                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
597                 {
598                 }
599
600                 [MonoTODO]
601                 [ComVisible (false)]
602                 public virtual void Reset ()
603                 {
604                 }
605
606                 // properties
607
608                 [ComVisible (false)]
609                 public IntPtr Handle {
610                         get { return (IntPtr) 0; }
611                 }
612 #endif
613         }
614 }