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-2005 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 = new FileStream (filename, FileMode.Open)) {
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);
163 // IntPtr.Zero results in an "empty" certificate instance
166 public X509Certificate (System.Security.Cryptography.X509Certificates.X509Certificate cert)
169 byte[] data = cert.GetRawCertData ();
171 x509 = new Mono.Security.X509.X509Certificate (data);
178 public X509Certificate ()
183 public X509Certificate (byte[] rawData, string password)
185 Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
189 public X509Certificate (byte[] rawData, SecureString password)
191 Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
195 public X509Certificate (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
197 Import (rawData, password, keyStorageFlags);
201 public X509Certificate (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
203 Import (rawData, password, keyStorageFlags);
207 public X509Certificate (string fileName)
209 Import (fileName, (string)null, X509KeyStorageFlags.DefaultKeySet);
213 public X509Certificate (string fileName, string password)
215 Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
219 public X509Certificate (string fileName, SecureString password)
221 Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
225 public X509Certificate (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
227 Import (fileName, password, keyStorageFlags);
231 public X509Certificate (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
233 Import (fileName, password, keyStorageFlags);
237 public X509Certificate (SerializationInfo info, StreamingContext context)
244 public virtual bool Equals (System.Security.Cryptography.X509Certificates.X509Certificate cert)
247 byte[] raw = cert.GetRawCertData ();
251 if (x509.RawData == null)
253 if (raw.Length == x509.RawData.Length) {
254 for (int i = 0; i < raw.Length; i++) {
255 if (raw[i] != x509.RawData [i])
258 // well no choice must be equals!
265 return (x509.RawData == null);
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 ()
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);
279 return cachedCertificateHash;
282 public virtual string GetCertHashString ()
284 // must call GetCertHash (not variable) or optimization wont work
285 return tostr (GetCertHash ());
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 ()
295 DateTime dt = x509.ValidFrom.ToUniversalTime().AddHours (-8);
296 return dt.ToString (); //"yyyy-MM-dd HH:mm:ss");
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 ()
306 DateTime dt = x509.ValidUntil.ToUniversalTime().AddHours (-8);
307 return dt.ToString (); //"yyyy-MM-dd HH:mm:ss");
310 // well maybe someday there'll be support for PGP or SPKI ?
311 public virtual string GetFormat ()
313 return "X509"; // DO NOT TRANSLATE
316 public override int GetHashCode ()
318 // the cert hash may not be (yet) calculated
319 if (cachedCertificateHash == null)
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]);
331 [Obsolete ("Use the Issuer property.")]
333 public virtual string GetIssuerName ()
335 return x509.IssuerName;
338 public virtual string GetKeyAlgorithm ()
340 return x509.KeyAlgorithm;
343 public virtual byte[] GetKeyAlgorithmParameters ()
345 return x509.KeyAlgorithmParameters;
348 public virtual string GetKeyAlgorithmParametersString ()
350 return tostr (x509.KeyAlgorithmParameters);
354 [Obsolete ("Use the Subject property.")]
356 public virtual string GetName ()
358 return x509.SubjectName;
361 public virtual byte[] GetPublicKey ()
363 return x509.PublicKey;
366 public virtual string GetPublicKeyString ()
368 return tostr (x509.PublicKey);
371 public virtual byte[] GetRawCertData ()
373 return ((x509 != null) ? x509.RawData : null);
376 public virtual string GetRawCertDataString ()
378 return ((x509 != null) ? tostr (x509.RawData) : null);
381 public virtual byte[] GetSerialNumber ()
383 return x509.SerialNumber;
386 public virtual string GetSerialNumberString ()
388 return tostr (x509.SerialNumber);
391 // to please corcompare ;-)
392 public override string ToString ()
394 return base.ToString ();
397 public virtual string ToString (bool details)
400 string nl = Environment.NewLine;
401 StringBuilder sb = new StringBuilder ();
402 sb.Append ("CERTIFICATE:");
404 sb.Append ("\tFormat: ");
405 sb.Append (GetFormat ());
406 if (x509.SubjectName != null) {
408 sb.Append ("\tName: ");
409 sb.Append (GetName ());
411 if (x509.IssuerName != null) {
413 sb.Append ("\tIssuing CA: ");
414 sb.Append (GetIssuerName ());
416 if (x509.SignatureAlgorithm != null) {
418 sb.Append ("\tKey Algorithm: ");
419 sb.Append (GetKeyAlgorithm ());
421 if (x509.SerialNumber != null) {
423 sb.Append ("\tSerial Number: ");
424 sb.Append (GetSerialNumberString ());
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) {
431 sb.Append ("\tKey Alogrithm Parameters: ");
432 sb.Append (GetKeyAlgorithmParametersString ());
434 if (x509.PublicKey != null) {
436 sb.Append ("\tPublic Key: ");
437 sb.Append (GetPublicKeyString ());
441 return sb.ToString ();
444 return base.ToString ();
448 public string Issuer {
449 get { return x509.IssuerName; }
452 public string Subject {
453 get { return x509.SubjectName; }
457 public override bool Equals (object obj)
459 X509Certificate x = (obj as X509Certificate);
461 return this.Equals (x);
465 [MonoTODO ("incomplete")]
467 public virtual byte[] Export (X509ContentType contentType)
469 return Export (contentType, (byte[])null);
472 [MonoTODO ("incomplete")]
474 public virtual byte[] Export (X509ContentType contentType, string password)
476 return Export (contentType, Encoding.UTF8.GetBytes (password));
479 [MonoTODO ("incomplete")]
480 public virtual byte[] Export (X509ContentType contentType, SecureString password)
482 return Export (contentType, password.GetBuffer ());
485 [MonoTODO ("export!")]
486 internal byte[] Export (X509ContentType contentType, byte[] password)
489 switch (contentType) {
490 case X509ContentType.Cert:
493 throw new NotSupportedException ();
498 if (password != null)
499 Array.Clear (password, 0, password.Length);
504 void IDeserializationCallback.OnDeserialization (object sender)
509 public virtual void Import (byte[] rawData)
511 Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
514 [MonoTODO ("missing KeyStorageFlags support")]
516 public virtual void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
518 if (password == null) {
519 x509 = new Mono.Security.X509.X509Certificate (rawData);
520 // TODO - PKCS12 without password
523 PKCS12 pfx = new PKCS12 (rawData, password);
524 if (pfx.Certificates.Count > 0) {
525 x509 = pfx.Certificates [0];
532 [MonoTODO ("SecureString is incomplete")]
533 public virtual void Import (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
535 Import (rawData, (string)null, keyStorageFlags);
539 public virtual void Import (string fileName)
541 byte[] rawData = Load (fileName);
542 Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
545 [MonoTODO ("missing KeyStorageFlags support")]
547 public virtual void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
549 byte[] rawData = Load (fileName);
550 Import (rawData, password, keyStorageFlags);
553 [MonoTODO ("SecureString is incomplete")]
554 public virtual void Import (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
556 byte[] rawData = Load (fileName);
557 Import (rawData, (string)null, keyStorageFlags);
560 private byte[] Load (string fileName)
563 using (FileStream fs = new FileStream (fileName, FileMode.Open)) {
564 data = new byte [fs.Length];
565 fs.Read (data, 0, data.Length);
572 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
578 public virtual void Reset ()
585 public IntPtr Handle {
586 get { return (IntPtr) 0; }