2 // X509Certificates.cs: Handles X.509 certificates.
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
31 using System.Runtime.InteropServices;
32 using System.Security.Permissions;
36 using Mono.Security.Authenticode;
37 using Mono.Security.X509;
40 using System.Runtime.Serialization;
43 namespace System.Security.Cryptography.X509Certificates {
46 // a. Internet X.509 Public Key Infrastructure Certificate and CRL Profile
47 // http://www.ietf.org/rfc/rfc3280.txt
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.
55 public class X509Certificate : IDeserializationCallback, ISerializable {
57 public class X509Certificate {
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;
75 // NOTE: We only define the CryptoAPI structure (from WINCRYPT.H)
76 // so we don't create any dependencies on Windows DLL in corlib
78 private Mono.Security.X509.X509Certificate x509;
79 private bool hideDates;
80 private byte[] cachedCertificateHash;
82 // almost every byte[] returning function has a string equivalent
83 // sadly the BitConverter insert dash between bytes :-(
84 private string tostr (byte[] data)
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 ();
98 public static X509Certificate CreateFromCertFile (string filename)
101 using (FileStream fs = File.OpenRead (filename)) {
102 data = new byte [fs.Length];
103 fs.Read (data, 0, data.Length);
106 return new X509Certificate (data);
109 [MonoTODO ("Incomplete - minimal validation in this version")]
110 public static X509Certificate CreateFromSignedFile (string filename)
113 AuthenticodeDeformatter a = new AuthenticodeDeformatter (filename);
114 if (a.SigningCertificate != null) {
116 string msg = String.Format (Locale.GetText (
117 "Invalid digital signature on {0}, reason #{1}."),
119 throw new COMException (msg);
121 return new X509Certificate (a.SigningCertificate.RawData);
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);
128 catch (SecurityException) {
129 // don't wrap SecurityException into a COMException
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);
140 // special constructor for Publisher (and related classes).
141 // Dates strings are null
142 internal X509Certificate (byte[] data, bool dates)
145 x509 = new Mono.Security.X509.X509Certificate (data);
150 public X509Certificate (byte[] data) : this (data, true)
154 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
155 public X509Certificate (IntPtr handle)
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);
165 throw new ArgumentException ("Invalid handle.");
167 // IntPtr.Zero results in an "empty" certificate instance
170 public X509Certificate (System.Security.Cryptography.X509Certificates.X509Certificate cert)
174 throw new ArgumentNullException ();
178 byte[] data = cert.GetRawCertData ();
180 x509 = new Mono.Security.X509.X509Certificate (data);
187 public X509Certificate ()
192 public X509Certificate (byte[] rawData, string password)
194 Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
198 public X509Certificate (byte[] rawData, SecureString password)
200 Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
204 public X509Certificate (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
206 Import (rawData, password, keyStorageFlags);
210 public X509Certificate (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
212 Import (rawData, password, keyStorageFlags);
216 public X509Certificate (string fileName)
218 Import (fileName, (string)null, X509KeyStorageFlags.DefaultKeySet);
222 public X509Certificate (string fileName, string password)
224 Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
228 public X509Certificate (string fileName, SecureString password)
230 Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
234 public X509Certificate (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
236 Import (fileName, password, keyStorageFlags);
240 public X509Certificate (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
242 Import (fileName, password, keyStorageFlags);
246 public X509Certificate (SerializationInfo info, StreamingContext context)
253 public virtual bool Equals (System.Security.Cryptography.X509Certificates.X509Certificate cert)
256 byte[] raw = cert.GetRawCertData ();
260 if (x509.RawData == null)
262 if (raw.Length == x509.RawData.Length) {
263 for (int i = 0; i < raw.Length; i++) {
264 if (raw[i] != x509.RawData [i])
267 // well no choice must be equals!
276 return x509 == null || (x509.RawData == null);
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 ()
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);
290 return cachedCertificateHash;
293 public virtual string GetCertHashString ()
295 // must call GetCertHash (not variable) or optimization wont work
296 return tostr (GetCertHash ());
299 // strangly there are no DateTime returning function
300 public virtual string GetEffectiveDateString ()
305 return x509.ValidFrom.ToString ();
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 ();
313 // strangly there are no DateTime returning function
314 public virtual string GetExpirationDateString ()
319 return x509.ValidUntil.ToString ();
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 ();
327 // well maybe someday there'll be support for PGP or SPKI ?
328 public virtual string GetFormat ()
330 return "X509"; // DO NOT TRANSLATE
333 public override int GetHashCode ()
335 // the cert hash may not be (yet) calculated
336 if (cachedCertificateHash == null)
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]);
348 [Obsolete ("Use the Issuer property.")]
350 public virtual string GetIssuerName ()
352 return x509.IssuerName;
355 public virtual string GetKeyAlgorithm ()
357 return x509.KeyAlgorithm;
360 public virtual byte[] GetKeyAlgorithmParameters ()
362 return x509.KeyAlgorithmParameters;
365 public virtual string GetKeyAlgorithmParametersString ()
367 return tostr (x509.KeyAlgorithmParameters);
371 [Obsolete ("Use the Subject property.")]
373 public virtual string GetName ()
375 return x509.SubjectName;
378 public virtual byte[] GetPublicKey ()
380 return x509.PublicKey;
383 public virtual string GetPublicKeyString ()
385 return tostr (x509.PublicKey);
388 public virtual byte[] GetRawCertData ()
390 return ((x509 != null) ? x509.RawData : null);
393 public virtual string GetRawCertDataString ()
395 return ((x509 != null) ? tostr (x509.RawData) : null);
398 public virtual byte[] GetSerialNumber ()
400 return x509.SerialNumber;
403 public virtual string GetSerialNumberString ()
405 return tostr (x509.SerialNumber);
408 // to please corcompare ;-)
409 public override string ToString ()
411 return base.ToString ();
414 public virtual string ToString (bool details)
417 string nl = Environment.NewLine;
418 StringBuilder sb = new StringBuilder ();
419 sb.Append ("CERTIFICATE:");
421 sb.Append ("\tFormat: ");
422 sb.Append (GetFormat ());
423 if (x509.SubjectName != null) {
425 sb.Append ("\tName: ");
426 sb.Append (GetName ());
428 if (x509.IssuerName != null) {
430 sb.Append ("\tIssuing CA: ");
431 sb.Append (GetIssuerName ());
433 if (x509.SignatureAlgorithm != null) {
435 sb.Append ("\tKey Algorithm: ");
436 sb.Append (GetKeyAlgorithm ());
438 if (x509.SerialNumber != null) {
440 sb.Append ("\tSerial Number: ");
441 sb.Append (GetSerialNumberString ());
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) {
448 sb.Append ("\tKey Alogrithm Parameters: ");
449 sb.Append (GetKeyAlgorithmParametersString ());
451 if (x509.PublicKey != null) {
453 sb.Append ("\tPublic Key: ");
454 sb.Append (GetPublicKeyString ());
458 return sb.ToString ();
461 return base.ToString ();
465 public string Issuer {
466 get { return x509.IssuerName; }
469 public string Subject {
470 get { return x509.SubjectName; }
474 public override bool Equals (object obj)
476 X509Certificate x = (obj as X509Certificate);
478 return this.Equals (x);
482 [MonoTODO ("incomplete")]
484 public virtual byte[] Export (X509ContentType contentType)
486 return Export (contentType, (byte[])null);
489 [MonoTODO ("incomplete")]
491 public virtual byte[] Export (X509ContentType contentType, string password)
493 return Export (contentType, Encoding.UTF8.GetBytes (password));
496 [MonoTODO ("incomplete")]
497 public virtual byte[] Export (X509ContentType contentType, SecureString password)
499 return Export (contentType, password.GetBuffer ());
502 [MonoTODO ("export!")]
503 internal byte[] Export (X509ContentType contentType, byte[] password)
506 switch (contentType) {
507 case X509ContentType.Cert:
510 throw new NotSupportedException ();
515 if (password != null)
516 Array.Clear (password, 0, password.Length);
521 void IDeserializationCallback.OnDeserialization (object sender)
526 public virtual void Import (byte[] rawData)
528 Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
531 [MonoTODO ("missing KeyStorageFlags support")]
533 public virtual void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
535 if (password == null) {
536 x509 = new Mono.Security.X509.X509Certificate (rawData);
537 // TODO - PKCS12 without password
541 PKCS12 pfx = new PKCS12 (rawData, password);
542 if (pfx.Certificates.Count > 0) {
543 x509 = pfx.Certificates [0];
549 // it's possible to supply a (unrequired/unusued) password
551 x509 = new Mono.Security.X509.X509Certificate (rawData);
556 [MonoTODO ("SecureString is incomplete")]
557 public virtual void Import (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
559 Import (rawData, (string)null, keyStorageFlags);
563 public virtual void Import (string fileName)
565 byte[] rawData = Load (fileName);
566 Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
569 [MonoTODO ("missing KeyStorageFlags support")]
571 public virtual void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
573 byte[] rawData = Load (fileName);
574 Import (rawData, password, keyStorageFlags);
577 [MonoTODO ("SecureString is incomplete")]
578 public virtual void Import (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
580 byte[] rawData = Load (fileName);
581 Import (rawData, (string)null, keyStorageFlags);
584 private byte[] Load (string fileName)
587 using (FileStream fs = new FileStream (fileName, FileMode.Open)) {
588 data = new byte [fs.Length];
589 fs.Read (data, 0, data.Length);
596 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
602 public virtual void Reset ()
609 public IntPtr Handle {
610 get { return (IntPtr) 0; }