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 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;
35 using Mono.Security.Authenticode;
36 using Mono.Security.X509;
39 using System.Runtime.Serialization;
42 namespace System.Security.Cryptography.X509Certificates {
45 // a. Internet X.509 Public Key Infrastructure Certificate and CRL Profile
46 // http://www.ietf.org/rfc/rfc3280.txt
48 // LAMESPEC: the MSDN docs always talks about X509v3 certificates
49 // and/or Authenticode certs. However this class works with older
50 // X509v1 certificates and non-authenticode (code signing) certs.
53 public class X509Certificate : IDeserializationCallback, ISerializable {
55 public class X509Certificate {
57 // typedef struct _CERT_CONTEXT {
58 // DWORD dwCertEncodingType;
59 // BYTE *pbCertEncoded;
60 // DWORD cbCertEncoded;
61 // PCERT_INFO pCertInfo;
62 // HCERTSTORE hCertStore;
63 // } CERT_CONTEXT, *PCERT_CONTEXT;
64 // typedef const CERT_CONTEXT *PCCERT_CONTEXT;
65 [StructLayout (LayoutKind.Sequential)]
66 internal struct CertificateContext {
67 public UInt32 dwCertEncodingType;
68 public IntPtr pbCertEncoded;
69 public UInt32 cbCertEncoded;
70 public IntPtr pCertInfo;
71 public IntPtr hCertStore;
73 // NOTE: We only define the CryptoAPI structure (from WINCRYPT.H)
74 // so we don't create any dependencies on Windows DLL in corlib
76 private Mono.Security.X509.X509Certificate x509;
77 private bool hideDates;
78 private byte[] cachedCertificateHash;
80 // almost every byte[] returning function has a string equivalent
81 // sadly the BitConverter insert dash between bytes :-(
82 private string tostr (byte[] data)
85 StringBuilder sb = new StringBuilder ();
86 for (int i = 0; i < data.Length; i++)
87 sb.Append (data[i].ToString ("X2"));
88 return sb.ToString ();
96 public static X509Certificate CreateFromCertFile (string filename)
99 using (FileStream fs = new FileStream (filename, FileMode.Open)) {
100 data = new byte [fs.Length];
101 fs.Read (data, 0, data.Length);
104 return new X509Certificate (data);
107 [MonoTODO ("Incomplete - minimal validation in this version")]
108 public static X509Certificate CreateFromSignedFile (string filename)
111 AuthenticodeDeformatter a = new AuthenticodeDeformatter (filename);
112 if (a.SigningCertificate != null) {
114 string msg = String.Format (Locale.GetText (
115 "Invalid digital signature on {0}, reason #{1}."),
117 throw new COMException (msg);
119 return new X509Certificate (a.SigningCertificate.RawData);
122 // if no signature is present return an empty certificate
123 byte[] cert = null; // must not confuse compiler about null ;)
124 return new X509Certificate (cert);
126 catch (Exception e) {
127 string msg = String.Format (Locale.GetText ("Couldn't extract digital signature from {0}."), filename);
128 throw new COMException (msg, e);
134 // special constructor for Publisher (and related classes).
135 // Dates strings are null
136 internal X509Certificate (byte[] data, bool dates)
139 x509 = new Mono.Security.X509.X509Certificate (data);
144 public X509Certificate (byte[] data) : this (data, true)
148 public X509Certificate (IntPtr handle)
150 CertificateContext cc = (CertificateContext) Marshal.PtrToStructure (handle, typeof (CertificateContext));
151 byte[] data = new byte [cc.cbCertEncoded];
152 Marshal.Copy (cc.pbCertEncoded, data, 0, (int)cc.cbCertEncoded);
153 x509 = new Mono.Security.X509.X509Certificate (data);
156 public X509Certificate (System.Security.Cryptography.X509Certificates.X509Certificate cert)
159 byte[] data = cert.GetRawCertData ();
161 x509 = new Mono.Security.X509.X509Certificate (data);
168 public X509Certificate ()
173 public X509Certificate (byte[] rawData, string password)
175 Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
179 public X509Certificate (byte[] rawData, SecureString password)
181 Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
185 public X509Certificate (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
187 Import (rawData, password, keyStorageFlags);
191 public X509Certificate (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
193 Import (rawData, password, keyStorageFlags);
197 public X509Certificate (string fileName)
199 Import (fileName, (byte[])null, X509KeyStorageFlags.DefaultKeySet);
203 public X509Certificate (string fileName, string password)
205 Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
209 public X509Certificate (string fileName, SecureString password)
211 Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
215 public X509Certificate (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
217 Import (fileName, password, keyStorageFlags);
221 public X509Certificate (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
223 Import (fileName, password, keyStorageFlags);
227 public X509Certificate (SerializationInfo info, StreamingContext context)
234 public virtual bool Equals (System.Security.Cryptography.X509Certificates.X509Certificate cert)
237 byte[] raw = cert.GetRawCertData ();
241 if (x509.RawData == null)
243 if (raw.Length == x509.RawData.Length) {
244 for (int i = 0; i < raw.Length; i++) {
245 if (raw[i] != x509.RawData [i])
248 // well no choice must be equals!
255 return (x509.RawData == null);
258 // LAMESPEC: This is the equivalent of the "thumbprint" that can be seen
259 // in the certificate viewer of Windows. This is ALWAYS the SHA1 hash of
260 // the certificate (i.e. it has nothing to do with the actual hash
261 // algorithm used to sign the certificate).
262 public virtual byte[] GetCertHash ()
264 // we'll hash the cert only once and only if required
265 if ((cachedCertificateHash == null) && (x509 != null)) {
266 SHA1 sha = SHA1.Create ();
267 cachedCertificateHash = sha.ComputeHash (x509.RawData);
269 return cachedCertificateHash;
272 public virtual string GetCertHashString ()
274 // must call GetCertHash (not variable) or optimization wont work
275 return tostr (GetCertHash ());
278 // strangly there are no DateTime returning function
279 // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
280 // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
281 public virtual string GetEffectiveDateString ()
285 DateTime dt = x509.ValidFrom.ToUniversalTime().AddHours (-8);
286 return dt.ToString (); //"yyyy-MM-dd HH:mm:ss");
289 // strangly there are no DateTime returning function
290 // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
291 // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
292 public virtual string GetExpirationDateString ()
296 DateTime dt = x509.ValidUntil.ToUniversalTime().AddHours (-8);
297 return dt.ToString (); //"yyyy-MM-dd HH:mm:ss");
300 // well maybe someday there'll be support for PGP or SPKI ?
301 public virtual string GetFormat ()
303 return "X509"; // DO NOT TRANSLATE
306 public override int GetHashCode ()
308 // the cert hash may not be (yet) calculated
309 if (cachedCertificateHash == null)
312 // return the integer of the first 4 bytes of the cert hash
313 if ((cachedCertificateHash != null) && (cachedCertificateHash.Length >= 4))
314 return ((cachedCertificateHash[0] << 24) |(cachedCertificateHash[1] << 16) |
315 (cachedCertificateHash[2] << 8) | cachedCertificateHash[3]);
320 public virtual string GetIssuerName ()
322 return x509.IssuerName;
325 public virtual string GetKeyAlgorithm ()
327 return x509.KeyAlgorithm;
330 public virtual byte[] GetKeyAlgorithmParameters ()
332 return x509.KeyAlgorithmParameters;
335 public virtual string GetKeyAlgorithmParametersString ()
337 return tostr (x509.KeyAlgorithmParameters);
340 public virtual string GetName ()
342 return x509.SubjectName;
345 public virtual byte[] GetPublicKey ()
347 return x509.PublicKey;
350 public virtual string GetPublicKeyString ()
352 return tostr (x509.PublicKey);
355 public virtual byte[] GetRawCertData ()
357 return ((x509 != null) ? x509.RawData : null);
360 public virtual string GetRawCertDataString ()
362 return ((x509 != null) ? tostr (x509.RawData) : null);
365 public virtual byte[] GetSerialNumber ()
367 return x509.SerialNumber;
370 public virtual string GetSerialNumberString ()
372 return tostr (x509.SerialNumber);
375 // to please corcompare ;-)
376 public override string ToString ()
378 return base.ToString ();
381 public virtual string ToString (bool details)
384 string nl = Environment.NewLine;
385 StringBuilder sb = new StringBuilder ();
386 sb.Append ("CERTIFICATE:");
388 sb.Append ("\tFormat: ");
389 sb.Append (GetFormat ());
390 if (x509.SubjectName != null) {
392 sb.Append ("\tName: ");
393 sb.Append (GetName ());
395 if (x509.IssuerName != null) {
397 sb.Append ("\tIssuing CA: ");
398 sb.Append (GetIssuerName ());
400 if (x509.SignatureAlgorithm != null) {
402 sb.Append ("\tKey Algorithm: ");
403 sb.Append (GetKeyAlgorithm ());
405 if (x509.SerialNumber != null) {
407 sb.Append ("\tSerial Number: ");
408 sb.Append (GetSerialNumberString ());
410 // Note: Algorithm is not spelled right as the actual
411 // MS implementation (we do exactly the same for the
412 // comparison in the unit tests)
413 if (x509.KeyAlgorithmParameters != null) {
415 sb.Append ("\tKey Alogrithm Parameters: ");
416 sb.Append (GetKeyAlgorithmParametersString ());
418 if (x509.PublicKey != null) {
420 sb.Append ("\tPublic Key: ");
421 sb.Append (GetPublicKeyString ());
425 return sb.ToString ();
428 return base.ToString ();
433 public override bool Equals (object obj)
435 X509Certificate x = (obj as X509Certificate);
437 return this.Equals (x);
441 [MonoTODO ("incomplete")]
443 public virtual byte[] Export (X509ContentType contentType)
445 return Export (contentType, (byte[])null);
448 [MonoTODO ("incomplete")]
450 public virtual byte[] Export (X509ContentType contentType, string password)
452 return Export (contentType, Encoding.UTF8.GetBytes (password));
455 [MonoTODO ("incomplete")]
457 public virtual byte[] Export (X509ContentType contentType, SecureString password)
459 return Export (contentType, password.GetBuffer ());
462 [MonoTODO ("export!")]
463 internal byte[] Export (X509ContentType contentType, byte[] password)
466 switch (contentType) {
467 case X509ContentType.Cert:
470 throw new NotSupportedException ();
475 if (password != null)
476 Array.Clear (password, 0, password.Length);
481 void IDeserializationCallback.OnDeserialization (object sender)
485 [MonoTODO ("incomplete")]
487 public virtual void Import (byte[] rawData)
489 Import (rawData, (byte[])null, X509KeyStorageFlags.DefaultKeySet);
492 [MonoTODO ("incomplete")]
494 public virtual void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
496 Import (rawData, Encoding.UTF8.GetBytes (password), keyStorageFlags);
499 [MonoTODO ("incomplete")]
501 public virtual void Import (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
503 Import (rawData, password.GetBuffer (), keyStorageFlags);
506 [MonoTODO ("import!")]
507 internal void Import (byte[] rawData, byte[] password, X509KeyStorageFlags keyStorageFlags)
510 if (password == null) {
511 x509 = new Mono.Security.X509.X509Certificate (rawData);
515 throw new NotSupportedException ();
520 if (password != null)
521 Array.Clear (password, 0, password.Length);
525 [MonoTODO ("incomplete")]
527 public virtual void Import (string fileName)
529 Import (fileName, (byte[])null, X509KeyStorageFlags.DefaultKeySet);
532 [MonoTODO ("incomplete - is the password UTF8, ASCII, Unicode ?")]
534 public virtual void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
536 Import (fileName, Encoding.UTF8.GetBytes (password), keyStorageFlags);
539 [MonoTODO ("incomplete")]
541 public virtual void Import (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
543 Import (fileName, password.GetBuffer (), keyStorageFlags);
546 internal void Import (string fileName, byte[] password, X509KeyStorageFlags keyStorageFlags)
549 using (FileStream fs = new FileStream (fileName, FileMode.Open)) {
550 byte[] data = new byte [fs.Length];
551 fs.Read (data, 0, data.Length);
553 Import (data, password, keyStorageFlags);
558 if (password != null)
559 Array.Clear (password, 0, password.Length);
564 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
570 public virtual void Reset ()
577 public IntPtr Handle {
578 get { return (IntPtr) 0; }