Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / corlib / System.Security.Cryptography.X509Certificates / X509Certificate.cs
index c226033ad20e0f0e33f393a09dc27565e9eea7ad..8ca0307c4dcf3f245f61d41c4fecc2354afb541e 100644 (file)
@@ -1,5 +1,5 @@
 //
-// X509Certificates.cs: Handles X.509 certificates.
+// X509Certificate.cs: Handles X.509 certificates.
 //
 // Author:
 //     Sebastien Pouliot  <sebastien@ximian.com>
@@ -33,12 +33,10 @@ 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
+using Mono.Security.Authenticode;
 
 namespace System.Security.Cryptography.X509Certificates {
 
@@ -50,11 +48,10 @@ namespace System.Security.Cryptography.X509Certificates {
        // 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 {
+#if NET_2_1
+       public partial class X509Certificate {
 #else
-       public class X509Certificate {
+       public partial class X509Certificate : IDeserializationCallback, ISerializable {
 #endif
                // typedef struct _CERT_CONTEXT {
                 //     DWORD                   dwCertEncodingType;
@@ -97,44 +94,30 @@ namespace System.Security.Cryptography.X509Certificates {
        
                public static X509Certificate CreateFromCertFile (string filename) 
                {
-                       byte[] data = null;
-                       using (FileStream fs = File.OpenRead (filename)) {
-                               data = new byte [fs.Length];
-                               fs.Read (data, 0, data.Length);
-                               fs.Close ();
-                       }
+                       byte[] data = File.ReadAllBytes (filename);
                        return new X509Certificate (data);
                }
-       
+
                [MonoTODO ("Incomplete - minimal validation in this version")]
                public static X509Certificate CreateFromSignedFile (string filename)
                {
                        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);
+                               string msg = Locale.GetText ("Couldn't extract digital signature from {0}.", filename);
                                throw new COMException (msg, e);
                        }
+                       throw new CryptographicException (Locale.GetText ("{0} isn't signed.", filename));
                }
-       
+
                // constructors
        
                // special constructor for Publisher (and related classes).
@@ -142,7 +125,7 @@ namespace System.Security.Cryptography.X509Certificates {
                internal X509Certificate (byte[] data, bool dates) 
                {
                        if (data != null) {
-                               x509 = new Mono.Security.X509.X509Certificate (data);
+                               Import (data, (string)null, X509KeyStorageFlags.DefaultKeySet);
                                hideDates = !dates;
                        }
                }
@@ -151,28 +134,36 @@ namespace System.Security.Cryptography.X509Certificates {
                {
                }
        
-               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
                public X509Certificate (IntPtr handle) 
+               {
+                       if (handle == IntPtr.Zero)
+                               throw new ArgumentException ("Invalid handle.");
+#if NET_2_1
+                       // this works on Windows-only so it's of no use for Moonlight
+                       // even more since this ctor is [SecurityCritical]
+                       throw new NotSupportedException ();
+#else
+                       InitFromHandle (handle);
+#endif
+               }
+
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
+               private void InitFromHandle (IntPtr handle)
                {
                        if (handle != IntPtr.Zero) {
+                               // both Marshal.PtrToStructure and Marshal.Copy use LinkDemand (so they will always success from here)
                                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
+                       // for 1.x 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
+                               throw new ArgumentNullException ("cert");
 
                        if (cert != null) {
                                byte[] data = cert.GetRawCertData ();
@@ -182,78 +173,21 @@ namespace System.Security.Cryptography.X509Certificates {
                        }
                }
 
-#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)
+               public virtual bool Equals (System.Security.Cryptography.X509Certificates.X509Certificate other)
                {
-                       if (cert != null) {
-                               byte[] raw = cert.GetRawCertData ();
+                       if (other == null) {
+                               return false;
+                       } else {
+                               if (other.x509 == null) {
+                                       if (x509 == null)
+                                               return true;
+                                       throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
+                               }
+
+                               byte[] raw = other.x509.RawData;
                                if (raw != null) {
                                        if (x509 == null)
                                                return false;
@@ -271,9 +205,7 @@ namespace System.Security.Cryptography.X509Certificates {
                                                return false;
                                }
                        }
-                       else
-                               return false;
-                       return x509 == null || (x509.RawData == null);
+                       return ((x509 == null) || (x509.RawData == null));
                }
        
                // LAMESPEC: This is the equivalent of the "thumbprint" that can be seen
@@ -282,6 +214,8 @@ namespace System.Security.Cryptography.X509Certificates {
                // algorithm used to sign the certificate).
                public virtual byte[] GetCertHash () 
                {
+                       if (x509 == null)
+                               throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
                        // we'll hash the cert only once and only if required
                        if ((cachedCertificateHash == null) && (x509 != null)) {
                                SHA1 sha = SHA1.Create ();
@@ -301,13 +235,10 @@ namespace System.Security.Cryptography.X509Certificates {
                {
                        if (hideDates)
                                return null;
-#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
+                       if (x509 == null)
+                               throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
+
+                       return x509.ValidFrom.ToLocalTime ().ToString ();
                }
        
                // strangly there are no DateTime returning function
@@ -315,13 +246,10 @@ namespace System.Security.Cryptography.X509Certificates {
                {
                        if (hideDates)
                                return null;
-#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
+                       if (x509 == null)
+                               throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
+
+                       return x509.ValidUntil.ToLocalTime ().ToString ();
                }
        
                // well maybe someday there'll be support for PGP or SPKI ?
@@ -332,6 +260,8 @@ namespace System.Security.Cryptography.X509Certificates {
        
                public override int GetHashCode ()
                {
+                       if (x509 == null)
+                               return 0;
                        // the cert hash may not be (yet) calculated
                        if (cachedCertificateHash == null)
                                GetCertHash();
@@ -344,65 +274,84 @@ namespace System.Security.Cryptography.X509Certificates {
                                return 0;
                }
 
-#if NET_2_0
                [Obsolete ("Use the Issuer property.")]
-#endif
                public virtual string GetIssuerName () 
                {
+                       if (x509 == null)
+                               throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
                        return x509.IssuerName;
                }
        
                public virtual string GetKeyAlgorithm () 
                {
+                       if (x509 == null)
+                               throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
                        return x509.KeyAlgorithm;
                }
        
                public virtual byte[] GetKeyAlgorithmParameters () 
                {
-                       return x509.KeyAlgorithmParameters;
+                       if (x509 == null)
+                               throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
+
+                       byte[] kap = x509.KeyAlgorithmParameters;
+                       if (kap == null)
+                               throw new CryptographicException (Locale.GetText ("Parameters not part of the certificate"));
+
+                       return kap;
                }
        
                public virtual string GetKeyAlgorithmParametersString () 
                {
-                       return tostr (x509.KeyAlgorithmParameters);
+                       return tostr (GetKeyAlgorithmParameters ());
                }
        
-#if NET_2_0
                [Obsolete ("Use the Subject property.")]
-#endif
                public virtual string GetName ()
                {
+                       if (x509 == null)
+                               throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
                        return x509.SubjectName;
                }
        
                public virtual byte[] GetPublicKey () 
                {
+                       if (x509 == null)
+                               throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
                        return x509.PublicKey;
                }
        
                public virtual string GetPublicKeyString () 
                {
-                       return tostr (x509.PublicKey);
+                       return tostr (GetPublicKey ());
                }
        
                public virtual byte[] GetRawCertData () 
                {
-                       return ((x509 != null) ? x509.RawData : null);
+                       if (x509 == null)
+                               throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
+                       return x509.RawData;
                }
        
                public virtual string GetRawCertDataString () 
                {
-                       return ((x509 != null) ? tostr (x509.RawData) : null);
+                       if (x509 == null)
+                               throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
+                       return tostr (x509.RawData);
                }
        
                public virtual byte[] GetSerialNumber () 
                {
+                       if (x509 == null)
+                               throw new CryptographicException (Locale.GetText ("Certificate instance is empty."));
                        return x509.SerialNumber;
                }
        
                public virtual string GetSerialNumberString () 
                {
-                       return tostr (x509.SerialNumber);
+                       byte[] sn = GetSerialNumber ();
+                       Array.Reverse (sn);
+                       return tostr (sn);
                }
        
                // to please corcompare ;-)
@@ -411,203 +360,26 @@ namespace System.Security.Cryptography.X509Certificates {
                        return base.ToString ();
                }
        
-               public virtual string ToString (bool details
+               public virtual string ToString (bool fVerbose
                {
-                       if (details) {
-                               string nl = Environment.NewLine;
-                               StringBuilder sb = new StringBuilder ();
-                               sb.Append ("CERTIFICATE:");
-                               sb.Append (nl);
-                               sb.Append ("\tFormat:  ");
-                               sb.Append (GetFormat ());
-                               if (x509.SubjectName != null) {
-                                       sb.Append (nl);
-                                       sb.Append ("\tName:  ");
-                                       sb.Append (GetName ());
-                               }
-                               if (x509.IssuerName != null) {
-                                       sb.Append (nl);
-                                       sb.Append ("\tIssuing CA:  ");
-                                       sb.Append (GetIssuerName ());
-                               }
-                               if (x509.SignatureAlgorithm != null) {
-                                       sb.Append (nl);
-                                       sb.Append ("\tKey Algorithm:  ");
-                                       sb.Append (GetKeyAlgorithm ());
-                               }
-                               if (x509.SerialNumber != null) {
-                                       sb.Append (nl);
-                                       sb.Append ("\tSerial Number:  ");
-                                       sb.Append (GetSerialNumberString ());
-                               }
-                               // Note: Algorithm is not spelled right as the actual 
-                               // MS implementation (we do exactly the same for the
-                               // comparison in the unit tests)
-                               if (x509.KeyAlgorithmParameters != null) {
-                                       sb.Append (nl);
-                                       sb.Append ("\tKey Alogrithm Parameters:  ");
-                                       sb.Append (GetKeyAlgorithmParametersString ());
-                               }
-                               if (x509.PublicKey != null) {
-                                       sb.Append (nl);
-                                       sb.Append ("\tPublic Key:  ");
-                                       sb.Append (GetPublicKeyString ());
-                               }
-                               sb.Append (nl);
-                               sb.Append (nl);
-                               return sb.ToString ();
-                       }
-                       else
+                       if (!fVerbose || (x509 == null))
                                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;
+                       string nl = Environment.NewLine;
+                       StringBuilder sb = new StringBuilder ();
+                       sb.AppendFormat ("[Subject]{0}  {1}{0}{0}", nl, Subject);
+                       sb.AppendFormat ("[Issuer]{0}  {1}{0}{0}", nl, Issuer);
+                       sb.AppendFormat ("[Not Before]{0}  {1}{0}{0}", nl, GetEffectiveDateString ());
+                       sb.AppendFormat ("[Not After]{0}  {1}{0}{0}", nl, GetExpirationDateString ());
+                       sb.AppendFormat ("[Thumbprint]{0}  {1}{0}", nl, GetCertHashString ());
+                       sb.Append (nl);
+                       return sb.ToString ();
                }
 
-               [MonoTODO]
-               void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
+#if NET_4_0
+               protected static string FormatDate (DateTime date)
                {
-               }
-
-               [MonoTODO]
-               [ComVisible (false)]
-               public virtual void Reset ()
-               {
-               }
-
-               // properties
-
-               [ComVisible (false)]
-               public IntPtr Handle {
-                       get { return (IntPtr) 0; }
+                       throw new NotImplementedException ();
                }
 #endif
        }