// X509Certificates.cs: Handles X.509 certificates.
//
// Author:
-// Sebastien Pouliot (spouliot@motus.com)
+// Sebastien Pouliot <sebastien@ximian.com>
//
// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
+// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-using System;
using System.IO;
using System.Runtime.InteropServices;
-using System.Security.Cryptography;
+using System.Security.Permissions;
using System.Text;
using Mono.Security;
using Mono.Security.Authenticode;
using Mono.Security.X509;
+#if NET_2_0
+using System.Runtime.Serialization;
+#endif
+
namespace System.Security.Cryptography.X509Certificates {
// References:
// and/or Authenticode certs. However this class works with older
// X509v1 certificates and non-authenticode (code signing) certs.
[Serializable]
+#if NET_2_0
+ [ComVisible (true)]
+ public class X509Certificate : IDeserializationCallback, ISerializable {
+#else
public class X509Certificate {
-
+#endif
// typedef struct _CERT_CONTEXT {
// DWORD dwCertEncodingType;
// BYTE *pbCertEncoded;
public static X509Certificate CreateFromCertFile (string filename)
{
byte[] data = null;
- FileStream fs = new FileStream (filename, FileMode.Open);
- try {
+ using (FileStream fs = File.OpenRead (filename)) {
data = new byte [fs.Length];
fs.Read (data, 0, data.Length);
- }
- finally {
fs.Close ();
}
-
return new X509Certificate (data);
}
- /*static private int ReadWord (Stream s)
- {
- int word = s.ReadByte ();
- word = (s.ReadByte () << 8) + word;
- return word;
- }
-
- static private int ReadDWord (Stream s)
- {
- int b1 = s.ReadByte ();
- int b2 = s.ReadByte ();
- int b3 = s.ReadByte ();
- int b4 = s.ReadByte ();
- return (b4 << 24) + (b3 << 16) + (b2 << 8) + b1;
- }
-
- // http://www.mycgiserver.com/~ultraschall/files/pefile.htm
- static private byte[] GetAuthenticodeSignature (string fileName)
+ [MonoTODO ("Incomplete - minimal validation in this version")]
+ public static X509Certificate CreateFromSignedFile (string filename)
{
- FileStream fs = new FileStream (fileName, FileMode.Open, FileAccess.Read);
try {
- // MZ - DOS header
- if (ReadWord (fs) != 0x5a4d)
- return null;
- // find offset of PE header
- fs.Seek (60, SeekOrigin.Begin);
- int peOffset = ReadDWord (fs);
-
- // PE - NT header
- fs.Seek (peOffset, SeekOrigin.Begin);
- if (ReadWord (fs) != 0x4550)
- return null;
-
- fs.Seek (150, SeekOrigin.Current);
-
- // IMAGE_DIRECTORY_ENTRY_SECURITY
- int secOffset = ReadDWord (fs);
- if (secOffset == 0)
- return null;
- int secSize = ReadDWord (fs);
- if (secSize == 0)
- return null;
-
- // Authenticode signature
- fs.Seek (secOffset, SeekOrigin.Begin);
- if (ReadDWord (fs) != secSize)
- return null;
- if (ReadDWord (fs) != 0x00020200)
- return null;
-
- byte[] signature = new byte [secSize - 8];
- fs.Read (signature, 0, signature.Length);
- fs.Close ();
- return signature;
- }
- catch {
- fs.Close ();
- return null;
- }
- }*/
+ AuthenticodeDeformatter a = new AuthenticodeDeformatter (filename);
+ if (a.SigningCertificate != null) {
+ if (a.Reason != 0) {
+ string msg = String.Format (Locale.GetText (
+ "Invalid digital signature on {0}, reason #{1}."),
+ filename, a.Reason);
+ throw new COMException (msg);
+ }
+ return new X509Certificate (a.SigningCertificate.RawData);
+ }
- // LAMESPEC: How does it differ from CreateFromCertFile ?
- // It seems to get the certificate inside a PE file (maybe a CAB too ?)
- [MonoTODO ("Incomplete - no validation in this version")]
- public static X509Certificate CreateFromSignedFile (string filename)
- {
- AuthenticodeDeformatter a = new AuthenticodeDeformatter (filename);
- if ((a.Certificates != null) && (a.Certificates.Count > 0)) {
- return new X509Certificate (a.Certificates [0].RawData);
- }
- else {
// if no signature is present return an empty certificate
byte[] cert = null; // must not confuse compiler about null ;)
return new X509Certificate (cert);
}
-/*
- byte[] signature = GetAuthenticodeSignature (filename);
- if (signature == null)
- throw new COMException ("File doesn't have a signature", -2146762496);
-
- // this is a big bad ASN.1 structure
- // Reference: http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
- // next we must find the last certificate inside the structure
- try {
- ASN1 sign = new ASN1 (signature);
- // we don't have to understand much of it to get the certificate
- ASN1 certs = sign [1][0][3];
- byte[] lastCert = certs [certs.Count - 1].GetBytes();
- return new X509Certificate (lastCert);
+ catch (SecurityException) {
+ // don't wrap SecurityException into a COMException
+ throw;
+ }
+ catch (Exception e) {
+ string msg = String.Format (Locale.GetText ("Couldn't extract digital signature from {0}."), filename);
+ throw new COMException (msg, e);
}
- catch {
- return null;
- }*/
}
// constructors
}
}
- public X509Certificate (byte[] data) : this (data, true) {}
+ public X509Certificate (byte[] data) : this (data, true)
+ {
+ }
+ [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
public X509Certificate (IntPtr handle)
{
- CertificateContext cc = (CertificateContext) Marshal.PtrToStructure (handle, typeof (CertificateContext));
- byte[] data = new byte [cc.cbCertEncoded];
- Marshal.Copy (cc.pbCertEncoded, data, 0, (int)cc.cbCertEncoded);
- x509 = new Mono.Security.X509.X509Certificate (data);
+ if (handle != IntPtr.Zero) {
+ CertificateContext cc = (CertificateContext) Marshal.PtrToStructure (handle, typeof (CertificateContext));
+ byte[] data = new byte [cc.cbCertEncoded];
+ Marshal.Copy (cc.pbCertEncoded, data, 0, (int)cc.cbCertEncoded);
+ x509 = new Mono.Security.X509.X509Certificate (data);
+ }
+#if NET_2_0
+ else
+ throw new ArgumentException ("Invalid handle.");
+#endif
+ // IntPtr.Zero results in an "empty" certificate instance
}
public X509Certificate (System.Security.Cryptography.X509Certificates.X509Certificate cert)
{
+#if NET_2_0
+ if (cert == null)
+ throw new ArgumentNullException ();
+#endif
+
if (cert != null) {
byte[] data = cert.GetRawCertData ();
if (data != null)
hideDates = false;
}
}
-
+
+#if NET_2_0
+ [MonoTODO]
+ public X509Certificate ()
+ {
+ }
+
+ [MonoTODO]
+ public X509Certificate (byte[] rawData, string password)
+ {
+ Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
+ }
+
+ [MonoTODO]
+ public X509Certificate (byte[] rawData, SecureString password)
+ {
+ Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
+ }
+
+ [MonoTODO]
+ public X509Certificate (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
+ {
+ Import (rawData, password, keyStorageFlags);
+ }
+
+ [MonoTODO]
+ public X509Certificate (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
+ {
+ Import (rawData, password, keyStorageFlags);
+ }
+
+ [MonoTODO]
+ public X509Certificate (string fileName)
+ {
+ Import (fileName, (string)null, X509KeyStorageFlags.DefaultKeySet);
+ }
+
+ [MonoTODO]
+ public X509Certificate (string fileName, string password)
+ {
+ Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
+ }
+
+ [MonoTODO]
+ public X509Certificate (string fileName, SecureString password)
+ {
+ Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
+ }
+
+ [MonoTODO]
+ public X509Certificate (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
+ {
+ Import (fileName, password, keyStorageFlags);
+ }
+
+ [MonoTODO]
+ public X509Certificate (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
+ {
+ Import (fileName, password, keyStorageFlags);
+ }
+
+ [MonoTODO]
+ public X509Certificate (SerializationInfo info, StreamingContext context)
+ {
+ }
+#endif
+
// public methods
public virtual bool Equals (System.Security.Cryptography.X509Certificates.X509Certificate cert)
return false;
}
}
- return (x509.RawData == null);
+ else
+ return false;
+ return x509 == null || (x509.RawData == null);
}
// LAMESPEC: This is the equivalent of the "thumbprint" that can be seen
}
// strangly there are no DateTime returning function
- // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
- // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
public virtual string GetEffectiveDateString ()
{
if (hideDates)
return null;
- DateTime dt = x509.ValidFrom.ToUniversalTime().AddHours (-8);
- return dt.ToString (); //"yyyy-MM-dd HH:mm:ss");
+#if NET_2_0
+ return x509.ValidFrom.ToString ();
+#else
+ // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
+ // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
+ return x509.ValidFrom.ToUniversalTime ().AddHours (-8).ToString ();
+#endif
}
// strangly there are no DateTime returning function
- // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
- // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
public virtual string GetExpirationDateString ()
{
if (hideDates)
return null;
- DateTime dt = x509.ValidUntil.ToUniversalTime().AddHours (-8);
- return dt.ToString (); //"yyyy-MM-dd HH:mm:ss");
+#if NET_2_0
+ return x509.ValidUntil.ToString ();
+#else
+ // LAMESPEC: Microsoft returns the local time from Pacific Time (GMT-8)
+ // BUG: This will not be corrected in Framework 1.1 and also affect WSE 1.0
+ return x509.ValidUntil.ToUniversalTime ().AddHours (-8).ToString ();
+#endif
}
// well maybe someday there'll be support for PGP or SPKI ?
else
return 0;
}
-
+
+#if NET_2_0
+ [Obsolete ("Use the Issuer property.")]
+#endif
public virtual string GetIssuerName ()
{
return x509.IssuerName;
return tostr (x509.KeyAlgorithmParameters);
}
+#if NET_2_0
+ [Obsolete ("Use the Subject property.")]
+#endif
public virtual string GetName ()
{
return x509.SubjectName;
else
return base.ToString ();
}
+
+#if NET_2_0
+ public string Issuer {
+ get { return x509.IssuerName; }
+ }
+
+ public string Subject {
+ get { return x509.SubjectName; }
+ }
+
+ [ComVisible (false)]
+ public override bool Equals (object obj)
+ {
+ X509Certificate x = (obj as X509Certificate);
+ if (x != null)
+ return this.Equals (x);
+ return false;
+ }
+
+ [MonoTODO ("incomplete")]
+ [ComVisible (false)]
+ public virtual byte[] Export (X509ContentType contentType)
+ {
+ return Export (contentType, (byte[])null);
+ }
+
+ [MonoTODO ("incomplete")]
+ [ComVisible (false)]
+ public virtual byte[] Export (X509ContentType contentType, string password)
+ {
+ return Export (contentType, Encoding.UTF8.GetBytes (password));
+ }
+
+ [MonoTODO ("incomplete")]
+ public virtual byte[] Export (X509ContentType contentType, SecureString password)
+ {
+ return Export (contentType, password.GetBuffer ());
+ }
+
+ [MonoTODO ("export!")]
+ internal byte[] Export (X509ContentType contentType, byte[] password)
+ {
+ try {
+ switch (contentType) {
+ case X509ContentType.Cert:
+ return x509.RawData;
+ default:
+ throw new NotSupportedException ();
+ }
+ }
+ finally {
+ // protect password
+ if (password != null)
+ Array.Clear (password, 0, password.Length);
+ }
+ }
+
+ [MonoTODO]
+ void IDeserializationCallback.OnDeserialization (object sender)
+ {
+ }
+
+ [ComVisible (false)]
+ public virtual void Import (byte[] rawData)
+ {
+ Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
+ }
+
+ [MonoTODO ("missing KeyStorageFlags support")]
+ [ComVisible (false)]
+ public virtual void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
+ {
+ if (password == null) {
+ x509 = new Mono.Security.X509.X509Certificate (rawData);
+ // TODO - PKCS12 without password
+ } else {
+ // try PKCS#12
+ try {
+ PKCS12 pfx = new PKCS12 (rawData, password);
+ if (pfx.Certificates.Count > 0) {
+ x509 = pfx.Certificates [0];
+ } else {
+ x509 = null;
+ }
+ }
+ catch {
+ // it's possible to supply a (unrequired/unusued) password
+ // fix bug #79028
+ x509 = new Mono.Security.X509.X509Certificate (rawData);
+ }
+ }
+ }
+
+ [MonoTODO ("SecureString is incomplete")]
+ public virtual void Import (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
+ {
+ Import (rawData, (string)null, keyStorageFlags);
+ }
+
+ [ComVisible (false)]
+ public virtual void Import (string fileName)
+ {
+ byte[] rawData = Load (fileName);
+ Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
+ }
+
+ [MonoTODO ("missing KeyStorageFlags support")]
+ [ComVisible (false)]
+ public virtual void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
+ {
+ byte[] rawData = Load (fileName);
+ Import (rawData, password, keyStorageFlags);
+ }
+
+ [MonoTODO ("SecureString is incomplete")]
+ public virtual void Import (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
+ {
+ byte[] rawData = Load (fileName);
+ Import (rawData, (string)null, keyStorageFlags);
+ }
+
+ private byte[] Load (string fileName)
+ {
+ byte[] data = null;
+ using (FileStream fs = new FileStream (fileName, FileMode.Open)) {
+ data = new byte [fs.Length];
+ fs.Read (data, 0, data.Length);
+ fs.Close ();
+ }
+ return data;
+ }
+
+ [MonoTODO]
+ void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ }
+
+ [MonoTODO]
+ [ComVisible (false)]
+ public virtual void Reset ()
+ {
+ }
+
+ // properties
+
+ [ComVisible (false)]
+ public IntPtr Handle {
+ get { return (IntPtr) 0; }
+ }
+#endif
}
-}
\ No newline at end of file
+}