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.X509;
38 using System.Runtime.Serialization;
39 using Mono.Security.Authenticode;
41 namespace System.Security.Cryptography.X509Certificates {
44 // a. Internet X.509 Public Key Infrastructure Certificate and CRL Profile
45 // http://www.ietf.org/rfc/rfc3280.txt
47 // LAMESPEC: the MSDN docs always talks about X509v3 certificates
48 // and/or Authenticode certs. However this class works with older
49 // X509v1 certificates and non-authenticode (code signing) certs.
52 public partial class X509Certificate {
54 public partial class X509Certificate : IDeserializationCallback, ISerializable {
56 // typedef struct _CERT_CONTEXT {
57 // DWORD dwCertEncodingType;
58 // BYTE *pbCertEncoded;
59 // DWORD cbCertEncoded;
60 // PCERT_INFO pCertInfo;
61 // HCERTSTORE hCertStore;
62 // } CERT_CONTEXT, *PCERT_CONTEXT;
63 // typedef const CERT_CONTEXT *PCCERT_CONTEXT;
64 [StructLayout (LayoutKind.Sequential)]
65 internal struct CertificateContext {
66 public UInt32 dwCertEncodingType;
67 public IntPtr pbCertEncoded;
68 public UInt32 cbCertEncoded;
69 public IntPtr pCertInfo;
70 public IntPtr hCertStore;
72 // NOTE: We only define the CryptoAPI structure (from WINCRYPT.H)
73 // so we don't create any dependencies on Windows DLL in corlib
75 private Mono.Security.X509.X509Certificate x509;
76 private bool hideDates;
77 private byte[] cachedCertificateHash;
79 // almost every byte[] returning function has a string equivalent
80 // sadly the BitConverter insert dash between bytes :-(
81 private string tostr (byte[] data)
84 StringBuilder sb = new StringBuilder ();
85 for (int i = 0; i < data.Length; i++)
86 sb.Append (data[i].ToString ("X2"));
87 return sb.ToString ();
95 public static X509Certificate CreateFromCertFile (string filename)
97 byte[] data = File.ReadAllBytes (filename);
98 return new X509Certificate (data);
101 [MonoTODO ("Incomplete - minimal validation in this version")]
102 public static X509Certificate CreateFromSignedFile (string filename)
105 AuthenticodeDeformatter a = new AuthenticodeDeformatter (filename);
106 if (a.SigningCertificate != null) {
107 return new X509Certificate (a.SigningCertificate.RawData);
110 catch (SecurityException) {
111 // don't wrap SecurityException into a COMException
114 catch (Exception e) {
115 string msg = Locale.GetText ("Couldn't extract digital signature from {0}.", filename);
116 throw new COMException (msg, e);
118 throw new CryptographicException (Locale.GetText ("{0} isn't signed.", filename));
123 // special constructor for Publisher (and related classes).
124 // Dates strings are null
125 internal X509Certificate (byte[] data, bool dates)
128 Import (data, (string)null, X509KeyStorageFlags.DefaultKeySet);
133 public X509Certificate (byte[] data) : this (data, true)
137 public X509Certificate (IntPtr handle)
139 if (handle == IntPtr.Zero)
140 throw new ArgumentException ("Invalid handle.");
142 // this works on Windows-only so it's of no use for Moonlight
143 // even more since this ctor is [SecurityCritical]
144 throw new NotSupportedException ();
146 InitFromHandle (handle);
150 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
151 private void InitFromHandle (IntPtr handle)
153 if (handle != IntPtr.Zero) {
154 // both Marshal.PtrToStructure and Marshal.Copy use LinkDemand (so they will always success from here)
155 CertificateContext cc = (CertificateContext) Marshal.PtrToStructure (handle, typeof (CertificateContext));
156 byte[] data = new byte [cc.cbCertEncoded];
157 Marshal.Copy (cc.pbCertEncoded, data, 0, (int)cc.cbCertEncoded);
158 x509 = new Mono.Security.X509.X509Certificate (data);
160 // for 1.x IntPtr.Zero results in an "empty" certificate instance
163 public X509Certificate (System.Security.Cryptography.X509Certificates.X509Certificate cert)
166 throw new ArgumentNullException ("cert");
169 byte[] data = cert.GetRawCertData ();
171 x509 = new Mono.Security.X509.X509Certificate (data);
179 public virtual bool Equals (System.Security.Cryptography.X509Certificates.X509Certificate other)
184 if (other.x509 == null) {
187 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
190 byte[] raw = other.x509.RawData;
194 if (x509.RawData == null)
196 if (raw.Length == x509.RawData.Length) {
197 for (int i = 0; i < raw.Length; i++) {
198 if (raw[i] != x509.RawData [i])
201 // well no choice must be equals!
208 return ((x509 == null) || (x509.RawData == null));
211 // LAMESPEC: This is the equivalent of the "thumbprint" that can be seen
212 // in the certificate viewer of Windows. This is ALWAYS the SHA1 hash of
213 // the certificate (i.e. it has nothing to do with the actual hash
214 // algorithm used to sign the certificate).
215 public virtual byte[] GetCertHash ()
218 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
219 // we'll hash the cert only once and only if required
220 if ((cachedCertificateHash == null) && (x509 != null)) {
221 SHA1 sha = SHA1.Create ();
222 cachedCertificateHash = sha.ComputeHash (x509.RawData);
224 return cachedCertificateHash;
227 public virtual string GetCertHashString ()
229 // must call GetCertHash (not variable) or optimization wont work
230 return tostr (GetCertHash ());
233 // strangly there are no DateTime returning function
234 public virtual string GetEffectiveDateString ()
239 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
241 return x509.ValidFrom.ToLocalTime ().ToString ();
244 // strangly there are no DateTime returning function
245 public virtual string GetExpirationDateString ()
250 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
252 return x509.ValidUntil.ToLocalTime ().ToString ();
255 // well maybe someday there'll be support for PGP or SPKI ?
256 public virtual string GetFormat ()
258 return "X509"; // DO NOT TRANSLATE
261 public override int GetHashCode ()
265 // the cert hash may not be (yet) calculated
266 if (cachedCertificateHash == null)
269 // return the integer of the first 4 bytes of the cert hash
270 if ((cachedCertificateHash != null) && (cachedCertificateHash.Length >= 4))
271 return ((cachedCertificateHash[0] << 24) |(cachedCertificateHash[1] << 16) |
272 (cachedCertificateHash[2] << 8) | cachedCertificateHash[3]);
277 [Obsolete ("Use the Issuer property.")]
278 public virtual string GetIssuerName ()
281 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
282 return x509.IssuerName;
285 public virtual string GetKeyAlgorithm ()
288 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
289 return x509.KeyAlgorithm;
292 public virtual byte[] GetKeyAlgorithmParameters ()
295 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
297 byte[] kap = x509.KeyAlgorithmParameters;
299 throw new CryptographicException (Locale.GetText ("Parameters not part of the certificate"));
304 public virtual string GetKeyAlgorithmParametersString ()
306 return tostr (GetKeyAlgorithmParameters ());
309 [Obsolete ("Use the Subject property.")]
310 public virtual string GetName ()
313 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
314 return x509.SubjectName;
317 public virtual byte[] GetPublicKey ()
320 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
321 return x509.PublicKey;
324 public virtual string GetPublicKeyString ()
326 return tostr (GetPublicKey ());
329 public virtual byte[] GetRawCertData ()
332 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
336 public virtual string GetRawCertDataString ()
339 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
340 return tostr (x509.RawData);
343 public virtual byte[] GetSerialNumber ()
346 throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
347 return x509.SerialNumber;
350 public virtual string GetSerialNumberString ()
352 byte[] sn = GetSerialNumber ();
357 // to please corcompare ;-)
358 public override string ToString ()
360 return base.ToString ();
363 public virtual string ToString (bool fVerbose)
365 if (!fVerbose || (x509 == null))
366 return base.ToString ();
368 string nl = Environment.NewLine;
369 StringBuilder sb = new StringBuilder ();
370 sb.AppendFormat ("[Subject]{0} {1}{0}{0}", nl, Subject);
371 sb.AppendFormat ("[Issuer]{0} {1}{0}{0}", nl, Issuer);
372 sb.AppendFormat ("[Not Before]{0} {1}{0}{0}", nl, GetEffectiveDateString ());
373 sb.AppendFormat ("[Not After]{0} {1}{0}{0}", nl, GetExpirationDateString ());
374 sb.AppendFormat ("[Thumbprint]{0} {1}{0}", nl, GetCertHashString ());
376 return sb.ToString ();
380 protected static string FormatDate (DateTime date)
382 throw new NotImplementedException ();