context: //
  // AuthenticodeDeformatter.cs: Authenticode signature validator
  //
  // Author:
  //	Sebastien Pouliot <sebastien@ximian.com>
  //
  // (C) 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;
  using System.Security.Cryptography;
  
  using CryptoSharp.Security.X509;
  
  namespace CryptoSharp.Security.Authenticode {
  
  	// References:
  	// a.	http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
  
  
  	internal class AuthenticodeDeformatter : AuthenticodeBase {
  
  		private string filename;
  		private byte[] hash;
  		private X509CertificateCollection coll;
  		private ASN1 signedHash;
  		private DateTime timestamp;
  		private X509Certificate signingCertificate;
  		private int reason;
  		private bool trustedRoot;
  		private bool trustedTimestampRoot;
  		private byte[] entry;
  
  		private X509Chain signerChain;
  		private X509Chain timestampChain;
  
  		public AuthenticodeDeformatter () : base ()
  		{
  			reason = -1;
  			signerChain = new X509Chain ();
  			timestampChain = new X509Chain ();
  		}
  
  		public AuthenticodeDeformatter (string fileName) : this () 
  		{
  			FileName = fileName;
  		}
  
  		public string FileName {
  			get { return filename; }
  			set { 
  				Reset ();
  				try {
  					CheckSignature (value); 
  				}
  				catch (SecurityException) {
  					throw;
  				}
  				catch (Exception) {
  					reason = 1;
  				}
  			}
  		}
  
  		public byte[] Hash {
  			get { 
  				if (signedHash == null)
  					return null;
  				return (byte[]) signedHash.Value.Clone ();
  			}
  		}
  
  		public int Reason {
  			get { 
  				if (reason == -1)
  					IsTrusted ();
  				return reason; 
  			}
  		}
  
  		public bool IsTrusted ()
  		{
  			if (entry == null) {
  				reason = 1;
  				return false;
  			}
  
  			if (signingCertificate == null) {
  				reason = 7;
  				return false;
  			}
  
  			if ((signerChain.Root == null) || !trustedRoot) {
  				reason = 6;
  				return false;
  			}
  
  			if (timestamp != DateTime.MinValue) {
  				if ((timestampChain.Root == null) || !trustedTimestampRoot) {
  					reason = 6;
  					return false;
  				}
  
  				// check that file was timestamped when certificates were valid
  				if (!signingCertificate.WasCurrent (Timestamp)) {
  					reason = 4;
  					return false;
  				}
  			}
  			else if (!signingCertificate.IsCurrent) {
  				// signature only valid if the certificate is valid
  				reason = 8;
  				return false;
  			}
  
  			reason = 0;
  			return true;
  		}
  
  		public byte[] Signature {
  			get {
  				if (entry == null)
  					return null;
  				return (byte[]) entry.Clone (); 
  			}
  		}
  
  		public DateTime Timestamp {
  			get { return timestamp; }
  		}
  
  		public X509CertificateCollection Certificates {
  			get { return coll; }
  		}
  
  		public X509Certificate SigningCertificate {
  			get { return signingCertificate; }
  		}
  
  		private bool CheckSignature (string fileName) 
  		{
  			filename = fileName;
  			Open (filename);
  			entry = GetSecurityEntry ();
  			if (entry == null) {
  				// no signature is present
  				reason = 1;
  				Close ();
  				return false;
  			}
  
  			PKCS7.ContentInfo ci = new PKCS7.ContentInfo (entry);
  			if (ci.ContentType != PKCS7.Oid.signedData) {
  				Close ();
  				return false;
  			}
  
  			PKCS7.SignedData sd = new PKCS7.SignedData (ci.Content);
  			if (sd.ContentInfo.ContentType != spcIndirectDataContext) {
  				Close ();
  				return false;
  			}
  
  			coll = sd.Certificates;
  
  			ASN1 spc = sd.ContentInfo.Content;
  			signedHash = spc [0][1][1];
  
  			HashAlgorithm ha = null; 
  			switch (signedHash.Length) {
  				case 16:
  					ha = HashAlgorithm.Create ("MD5"); 
  					hash = GetHash (ha);
  					break;
  				case 20:
  					ha = HashAlgorithm.Create ("SHA1");
  					hash = GetHash (ha);
  					break;
  				default:
  					reason = 5;
  					Close ();
  					return false;
  			}
  			Close ();
  
  			if (!signedHash.CompareValue (hash)) {
  				reason = 2;
  			}
  
  			// messageDigest is a hash of spcIndirectDataContext (which includes the file hash)
  			byte[] spcIDC = spc [0].Value;
  			ha.Initialize (); // re-using hash instance
  			byte[] messageDigest = ha.ComputeHash (spcIDC);
  
  			bool sign = VerifySignature (sd, messageDigest, ha);
  			return (sign && (reason == 0));
  		}
  
  		private bool CompareIssuerSerial (string issuer, byte[] serial, X509Certificate x509) 
  		{
  			if (issuer != x509.IssuerName)
  				return false;
  			if (serial.Length != x509.SerialNumber.Length)
  				return false;
  			// MS shows the serial number inversed (so is Mono.Security.X509.X509Certificate)
  			int n = serial.Length;
  			for (int i=0; i < serial.Length; i++) {
  				if (serial [i] != x509.SerialNumber [--n])
  					return false;
  			}
  			// must be true
  			return true;
  		}
  
  		//private bool VerifySignature (ASN1 cs, byte[] calculatedMessageDigest, string hashName) 
  		private bool VerifySignature (PKCS7.SignedData sd, byte[] calculatedMessageDigest, HashAlgorithm ha) 
  		{
  			string contentType = null;
  			ASN1 messageDigest = null;
  //			string spcStatementType = null;
  //			string spcSpOpusInfo = null;
  
  			for (int i=0; i < sd.SignerInfo.AuthenticatedAttributes.Count; i++) {
  				ASN1 attr = (ASN1) sd.SignerInfo.AuthenticatedAttributes [i];
  				string oid = ASN1Convert.ToOid (attr[0]);
  				switch (oid) {
  					case "1.2.840.113549.1.9.3":
  						// contentType
  						contentType = ASN1Convert.ToOid (attr[1][0]);
  						break;
  					case "1.2.840.113549.1.9.4":
  						// messageDigest
  						messageDigest = attr[1][0];
  						break;
  					case "1.3.6.1.4.1.311.2.1.11":
  						// spcStatementType (Microsoft code signing)
  						// possible values
  						// - individualCodeSigning (1 3 6 1 4 1 311 2 1 21)
  						// - commercialCodeSigning (1 3 6 1 4 1 311 2 1 22)
  //						spcStatementType = ASN1Convert.ToOid (attr[1][0][0]);
  						break;
  					case "1.3.6.1.4.1.311.2.1.12":
  						// spcSpOpusInfo (Microsoft code signing)
  /*						try {
  							spcSpOpusInfo = System.Text.Encoding.UTF8.GetString (attr[1][0][0][0].Value);
  						}
  						catch (NullReferenceException) {
  							spcSpOpusInfo = null;
  						}*/
  						break;
  					default:
  						break;
  				}
  			}
  			if (contentType != spcIndirectDataContext)
  				return false;
  
  			// verify message digest
  			if (messageDigest == null)
  				return false;
  			if (!messageDigest.CompareValue (calculatedMessageDigest))
  				return false;
  
  			// verify signature
  			string hashOID = CryptoConfig.MapNameToOID (ha.ToString ());
  			
  			// change to SET OF (not [0]) as per PKCS #7 1.5
  			ASN1 aa = new ASN1 (0x31);
  			foreach (ASN1 a in sd.SignerInfo.AuthenticatedAttributes)
  				aa.Add (a);
  			ha.Initialize ();
  			byte[] p7hash = ha.ComputeHash (aa.GetBytes ());
  
  			byte[] signature = sd.SignerInfo.Signature;
  			// we need to find the specified certificate
  			string issuer = sd.SignerInfo.IssuerName;
  			byte[] serial = sd.SignerInfo.SerialNumber;
  
gt: 			foreach (X509Certificate x509 in coll) {