2 // X509Certificate.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.
54 public partial class X509Certificate : IDeserializationCallback, ISerializable {
56 public class X509Certificate {
58 // typedef struct _CERT_CONTEXT {
59 // DWORD dwCertEncodingType;
60 // BYTE *pbCertEncoded;
61 // DWORD cbCertEncoded;
62 // PCERT_INFO pCertInfo;
63 // HCERTSTORE hCertStore;
64 // } CERT_CONTEXT, *PCERT_CONTEXT;
65 // typedef const CERT_CONTEXT *PCCERT_CONTEXT;
66 [StructLayout (LayoutKind.Sequential)]
67 internal struct CertificateContext {
68 public UInt32 dwCertEncodingType;
69 public IntPtr pbCertEncoded;
70 public UInt32 cbCertEncoded;
71 public IntPtr pCertInfo;
72 public IntPtr hCertStore;
74 // NOTE: We only define the CryptoAPI structure (from WINCRYPT.H)
75 // so we don't create any dependencies on Windows DLL in corlib
77 private Mono.Security.X509.X509Certificate x509;
78 private bool hideDates;
79 private byte[] cachedCertificateHash;
81 // almost every byte[] returning function has a string equivalent
82 // sadly the BitConverter insert dash between bytes :-(
83 private string tostr (byte[] data)
86 StringBuilder sb = new StringBuilder ();
87 for (int i = 0; i < data.Length; i++)
88 sb.Append (data[i].ToString ("X2"));
89 return sb.ToString ();
97 public static X509Certificate CreateFromCertFile (string filename)
99 byte[] data = Load (filename);
100 return new X509Certificate (data);
103 [MonoTODO ("Incomplete - minimal validation in this version")]
104 public static X509Certificate CreateFromSignedFile (string filename)
107 AuthenticodeDeformatter a = new AuthenticodeDeformatter (filename);
108 if (a.SigningCertificate != null) {
110 // before 2.0 the signing certificate is returned only if the signature is valid
112 string msg = String.Format (Locale.GetText (
113 "Invalid digital signature on {0}, reason #{1}."),
115 throw new COMException (msg);
118 return new X509Certificate (a.SigningCertificate.RawData);
121 catch (SecurityException) {
122 // don't wrap SecurityException into a COMException
126 catch (COMException) {
127 // don't wrap COMException into a COMException
131 catch (Exception e) {
132 string msg = Locale.GetText ("Couldn't extract digital signature from {0}.", filename);
133 throw new COMException (msg, e);
136 throw new CryptographicException (Locale.GetText ("{0} isn't signed.", filename));
138 // if no signature is present return an empty certificate
139 byte[] cert = null; // must not confuse compiler about null ;)
140 return new X509Certificate (cert);
146 // special constructor for Publisher (and related classes).
147 // Dates strings are null
148 internal X509Certificate (byte[] data, bool dates)
152 Import (data, (string)null, X509KeyStorageFlags.DefaultKeySet);
154 x509 = new Mono.Security.X509.X509Certificate (data);
160 public X509Certificate (byte[] data) : this (data, true)
164 public X509Certificate (IntPtr handle)
167 if (handle == IntPtr.Zero)
168 throw new ArgumentException ("Invalid handle.");
170 InitFromHandle (handle);
173 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
174 private void InitFromHandle (IntPtr handle)
176 if (handle != IntPtr.Zero) {
177 // both Marshal.PtrToStructure and Marshal.Copy use LinkDemand (so they will always success from here)
178 CertificateContext cc = (CertificateContext) Marshal.PtrToStructure (handle, typeof (CertificateContext));
179 byte[] data = new byte [cc.cbCertEncoded];
180 Marshal.Copy (cc.pbCertEncoded, data, 0, (int)cc.cbCertEncoded);
181 x509 = new Mono.Security.X509.X509Certificate (data);
183 // for 1.x IntPtr.Zero results in an "empty" certificate instance
186 public X509Certificate (System.Security.Cryptography.X509Certificates.X509Certificate cert)
190 throw new ArgumentNullException ("cert");
194 byte[] data = cert.GetRawCertData ();
196 x509 = new Mono.Security.X509.X509Certificate (data);
204 public virtual bool Equals (System.Security.Cryptography.X509Certificates.X509Certificate other)
209 if (other.x509 == null) {
213 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
215 return (x509 == null);
219 byte[] raw = other.x509.RawData;
223 if (x509.RawData == null)
225 if (raw.Length == x509.RawData.Length) {
226 for (int i = 0; i < raw.Length; i++) {
227 if (raw[i] != x509.RawData [i])
230 // well no choice must be equals!
237 return ((x509 == null) || (x509.RawData == null));
240 // LAMESPEC: This is the equivalent of the "thumbprint" that can be seen
241 // in the certificate viewer of Windows. This is ALWAYS the SHA1 hash of
242 // the certificate (i.e. it has nothing to do with the actual hash
243 // algorithm used to sign the certificate).
244 public virtual byte[] GetCertHash ()
248 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
250 // we'll hash the cert only once and only if required
251 if ((cachedCertificateHash == null) && (x509 != null)) {
252 SHA1 sha = SHA1.Create ();
253 cachedCertificateHash = sha.ComputeHash (x509.RawData);
255 return cachedCertificateHash;
258 public virtual string GetCertHashString ()
260 // must call GetCertHash (not variable) or optimization wont work
261 return tostr (GetCertHash ());
264 // strangly there are no DateTime returning function
265 public virtual string GetEffectiveDateString ()
271 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
273 return x509.ValidFrom.ToLocalTime ().ToString ();
275 // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
276 // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
277 return x509.ValidFrom.ToUniversalTime ().AddHours (-8).ToString ();
281 // strangly there are no DateTime returning function
282 public virtual string GetExpirationDateString ()
288 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
290 return x509.ValidUntil.ToLocalTime ().ToString ();
292 // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
293 // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
294 return x509.ValidUntil.ToUniversalTime ().AddHours (-8).ToString ();
298 // well maybe someday there'll be support for PGP or SPKI ?
299 public virtual string GetFormat ()
301 return "X509"; // DO NOT TRANSLATE
304 public override int GetHashCode ()
310 // the cert hash may not be (yet) calculated
311 if (cachedCertificateHash == null)
314 // return the integer of the first 4 bytes of the cert hash
315 if ((cachedCertificateHash != null) && (cachedCertificateHash.Length >= 4))
316 return ((cachedCertificateHash[0] << 24) |(cachedCertificateHash[1] << 16) |
317 (cachedCertificateHash[2] << 8) | cachedCertificateHash[3]);
323 [Obsolete ("Use the Issuer property.")]
325 public virtual string GetIssuerName ()
329 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
331 return x509.IssuerName;
334 public virtual string GetKeyAlgorithm ()
338 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
340 return x509.KeyAlgorithm;
343 public virtual byte[] GetKeyAlgorithmParameters ()
347 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
349 byte[] kap = x509.KeyAlgorithmParameters;
351 throw new CryptographicException (Locale.GetText ("Parameters not part of the certificate"));
355 return x509.KeyAlgorithmParameters;
359 public virtual string GetKeyAlgorithmParametersString ()
361 return tostr (GetKeyAlgorithmParameters ());
365 [Obsolete ("Use the Subject property.")]
367 public virtual string GetName ()
371 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
373 return x509.SubjectName;
376 public virtual byte[] GetPublicKey ()
380 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
382 return x509.PublicKey;
385 public virtual string GetPublicKeyString ()
387 return tostr (GetPublicKey ());
390 public virtual byte[] GetRawCertData ()
394 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
397 return ((x509 != null) ? x509.RawData : null);
401 public virtual string GetRawCertDataString ()
405 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
406 return tostr (x509.RawData);
408 return ((x509 != null) ? tostr (x509.RawData) : null);
412 public virtual byte[] GetSerialNumber ()
416 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
418 return x509.SerialNumber;
421 public virtual string GetSerialNumberString ()
423 byte[] sn = GetSerialNumber ();
430 // to please corcompare ;-)
431 public override string ToString ()
433 return base.ToString ();
436 public virtual string ToString (bool fVerbose)
438 if (!fVerbose || (x509 == null))
439 return base.ToString ();
441 string nl = Environment.NewLine;
442 StringBuilder sb = new StringBuilder ();
444 sb.AppendFormat ("[Subject]{0} {1}{0}{0}", nl, Subject);
445 sb.AppendFormat ("[Issuer]{0} {1}{0}{0}", nl, Issuer);
446 sb.AppendFormat ("[Not Before]{0} {1}{0}{0}", nl, GetEffectiveDateString ());
447 sb.AppendFormat ("[Not After]{0} {1}{0}{0}", nl, GetExpirationDateString ());
448 sb.AppendFormat ("[Thumbprint]{0} {1}{0}", nl, GetCertHashString ());
450 sb.Append ("CERTIFICATE:");
452 sb.Append ("\tFormat: ");
453 sb.Append (GetFormat ());
454 if (x509.SubjectName != null) {
456 sb.Append ("\tName: ");
457 sb.Append (GetName ());
459 if (x509.IssuerName != null) {
461 sb.Append ("\tIssuing CA: ");
462 sb.Append (GetIssuerName ());
464 if (x509.SignatureAlgorithm != null) {
466 sb.Append ("\tKey Algorithm: ");
467 sb.Append (GetKeyAlgorithm ());
469 if (x509.SerialNumber != null) {
471 sb.Append ("\tSerial Number: ");
472 sb.Append (GetSerialNumberString ());
474 // Note: Algorithm is not spelled right as the actual
475 // MS implementation (we do exactly the same for the
476 // comparison in the unit tests)
477 if (x509.KeyAlgorithmParameters != null) {
479 sb.Append ("\tKey Alogrithm Parameters: ");
480 sb.Append (GetKeyAlgorithmParametersString ());
482 if (x509.PublicKey != null) {
484 sb.Append ("\tPublic Key: ");
485 sb.Append (GetPublicKeyString ());
490 return sb.ToString ();
493 private static byte[] Load (string fileName)
496 using (FileStream fs = File.OpenRead (fileName)) {
497 data = new byte [fs.Length];
498 fs.Read (data, 0, data.Length);