New test.
[mono.git] / mcs / class / corlib / System.Security.Cryptography.X509Certificates / X509Certificate.cs
index 1c47348a5a084475d05cb1c23f685c929bd5598d..c226033ad20e0f0e33f393a09dc27565e9eea7ad 100644 (file)
@@ -5,7 +5,7 @@
 //     Sebastien Pouliot  <sebastien@ximian.com>
 //
 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004 Novell, Inc (http://www.novell.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
 // 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;
@@ -50,10 +49,11 @@ namespace System.Security.Cryptography.X509Certificates {
        // LAMESPEC: the MSDN docs always talks about X509v3 certificates
        // 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
-       [Serializable]
        public class X509Certificate {
 #endif
                // typedef struct _CERT_CONTEXT {
@@ -98,30 +98,41 @@ namespace System.Security.Cryptography.X509Certificates {
                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);
                }
        
                [MonoTODO ("Incomplete - minimal validation in this version")]
                public static X509Certificate CreateFromSignedFile (string filename)
                {
-                       AuthenticodeDeformatter a = new AuthenticodeDeformatter (filename);
-                       if (a.SigningCertificate != null) {
-                               return new X509Certificate (a.SigningCertificate.RawData);
-                       }
-                       else {
+                       try {
+                               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);
+                               }
+
                                // if no signature is present return an empty certificate
                                byte[] cert = null; // must not confuse compiler about null ;)
                                return new X509Certificate (cert);
                        }
+                       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);
+                       }
                }
        
                // constructors
@@ -136,18 +147,33 @@ namespace System.Security.Cryptography.X509Certificates {
                        }
                }
        
-               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)
@@ -157,23 +183,69 @@ namespace System.Security.Cryptography.X509Certificates {
                }
 
 #if NET_2_0
-               public X509Certificate () {}
+               [MonoTODO]
+               public X509Certificate ()
+               {
+               }
+
+               [MonoTODO]
+               public X509Certificate (byte[] rawData, string password)
+               {
+                       Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
+               }
 
-               public X509Certificate (byte[] rawData, string password) {}
+               [MonoTODO]
+               public X509Certificate (byte[] rawData, SecureString password)
+               {
+                       Import (rawData, password, X509KeyStorageFlags.DefaultKeySet);
+               }
 
-               public X509Certificate (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags) {}
+               [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);
                }
 
-               public X509Certificate (string fileName, string password, X509KeyStorageFlags keyStorageFlags) {}
+               [MonoTODO]
+               public X509Certificate (string fileName, SecureString password)
+               {
+                       Import (fileName, password, X509KeyStorageFlags.DefaultKeySet);
+               }
 
-               public X509Certificate (SerializationInfo info, StreamingContext context) {}
+               [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
@@ -199,7 +271,9 @@ namespace System.Security.Cryptography.X509Certificates {
                                                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
@@ -223,25 +297,31 @@ namespace System.Security.Cryptography.X509Certificates {
                }
        
                // 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 ?
@@ -263,7 +343,10 @@ namespace System.Security.Cryptography.X509Certificates {
                        else
                                return 0;
                }
-       
+
+#if NET_2_0
+               [Obsolete ("Use the Issuer property.")]
+#endif
                public virtual string GetIssuerName () 
                {
                        return x509.IssuerName;
@@ -284,6 +367,9 @@ namespace System.Security.Cryptography.X509Certificates {
                        return tostr (x509.KeyAlgorithmParameters);
                }
        
+#if NET_2_0
+               [Obsolete ("Use the Subject property.")]
+#endif
                public virtual string GetName ()
                {
                        return x509.SubjectName;
@@ -376,32 +462,150 @@ namespace System.Security.Cryptography.X509Certificates {
                }
 
 #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 null;
+                       return Export (contentType, (byte[])null);
                }
 
+               [MonoTODO ("incomplete")]
+               [ComVisible (false)]
                public virtual byte[] Export (X509ContentType contentType, string password)
                {
-                       return null;
+                       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);
                }
 
-               void IDeserializationCallback.OnDeserialization (object sender) {}
+               [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);
+                               }
+                       }
+               }
 
-               public virtual void Import (byte[] rawData) {}
+               [MonoTODO ("SecureString is incomplete")]
+               public virtual void Import (byte[] rawData, SecureString password, X509KeyStorageFlags keyStorageFlags)
+               {
+                       Import (rawData, (string)null, keyStorageFlags);
+               }
 
-               public virtual void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags) {}
+               [ComVisible (false)]
+               public virtual void Import (string fileName)
+               {
+                       byte[] rawData = Load (fileName);
+                       Import (rawData, (string)null, X509KeyStorageFlags.DefaultKeySet);
+               }
 
-               public virtual void Import (string fileName) {}
+               [MonoTODO ("missing KeyStorageFlags support")]
+               [ComVisible (false)]
+               public virtual void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
+               {
+                       byte[] rawData = Load (fileName);
+                       Import (rawData, password, keyStorageFlags);
+               }
 
-               public virtual void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags) {}
+               [MonoTODO ("SecureString is incomplete")]
+               public virtual void Import (string fileName, SecureString password, X509KeyStorageFlags keyStorageFlags)
+               {
+                       byte[] rawData = Load (fileName);
+                       Import (rawData, (string)null, keyStorageFlags);
+               }
 
-               void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) {}
+               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)
+               {
+               }
 
-               public virtual void Reset() {}
+               [MonoTODO]
+               [ComVisible (false)]
+               public virtual void Reset ()
+               {
+               }
 
                // properties
 
+               [ComVisible (false)]
                public IntPtr Handle {
                        get { return (IntPtr) 0; }
                }