Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastien Pouliot <sebastien@ximian.com>2003-09-02 04:00:09 +0400
committerSebastien Pouliot <sebastien@ximian.com>2003-09-02 04:00:09 +0400
commite3c3cb1a3b19119b3e207359cf062b3c87e5ca9e (patch)
tree53a81a0674656295f4fb86d5dcfca4741ce673c1 /mcs/class/Mono.Security/Mono.Security.Authenticode
parent72e5383d31d86e17775b64593fc9dadfa998c303 (diff)
2003-09-01 Sebastien Pouliot <spouliot@videotron.ca>
* AuthenticodeBase.cs: New. Base class including how to hash a PE file. * AuthenticodeDeformatter.cs: New. Class to decode Authenticode(tm) signatures. * AuthenticodeFormatter.cs: New. Class to encode a Authenticode(tm) signature, and optionally a timestamp, into a PE file. * SoftwarePublisherCertificate.cs: Now use Mono.Security.X509. X509Certificate class. svn path=/trunk/mcs/; revision=17810
Diffstat (limited to 'mcs/class/Mono.Security/Mono.Security.Authenticode')
-rwxr-xr-xmcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeBase.cs86
-rwxr-xr-xmcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs365
-rwxr-xr-xmcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeFormatter.cs297
-rw-r--r--mcs/class/Mono.Security/Mono.Security.Authenticode/ChangeLog11
-rw-r--r--mcs/class/Mono.Security/Mono.Security.Authenticode/SoftwarePublisherCertificate.cs2
5 files changed, 760 insertions, 1 deletions
diff --git a/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeBase.cs b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeBase.cs
new file mode 100755
index 00000000000..d46d0f3e86c
--- /dev/null
+++ b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeBase.cs
@@ -0,0 +1,86 @@
+//
+// AuthenticodeBase.cs: Authenticode signature base class
+//
+// Author:
+// Sebastien Pouliot (spouliot@motus.com)
+//
+// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
+//
+
+using System;
+using System.IO;
+using System.Security.Cryptography;
+
+namespace Mono.Security.Authenticode {
+
+ // References:
+ // a. http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
+
+ public enum Authority {
+ Individual,
+ Commercial,
+ Maximum
+ }
+
+ public class AuthenticodeBase {
+
+ public const string spcIndirectDataContext = "1.3.6.1.4.1.311.2.1.4";
+
+ protected byte[] rawData;
+
+ public AuthenticodeBase () {}
+
+ protected byte[] HashFile (string fileName, string hashName)
+ {
+ FileStream fs = new FileStream (fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
+ byte[] file = new byte [fs.Length];
+ fs.Read (file, 0, file.Length);
+ fs.Close ();
+
+ // MZ - DOS header
+ if (BitConverter.ToUInt16 (file, 0) != 0x5A4D)
+ return null;
+
+ // find offset of PE header
+ int peOffset = BitConverter.ToInt32 (file, 60);
+ if (peOffset > file.Length)
+ return null;
+
+ // PE - NT header
+ if (BitConverter.ToUInt16 (file, peOffset) != 0x4550)
+ return null;
+
+ // IMAGE_DIRECTORY_ENTRY_SECURITY
+ int dirSecurityOffset = BitConverter.ToInt32 (file, peOffset + 152);
+ int dirSecuritySize = BitConverter.ToInt32 (file, peOffset + 156);
+
+ if (dirSecuritySize > 8) {
+ rawData = new byte [dirSecuritySize - 8];
+ Array.Copy (file, dirSecurityOffset + 8, rawData, 0, rawData.Length);
+/* DEBUG
+ FileStream debug = new FileStream (fileName + ".sig", FileMode.Create, FileAccess.Write);
+ debug.Write (rawData, 0, rawData.Length);
+ debug.Close ();*/
+ }
+ else
+ rawData = null;
+
+ HashAlgorithm hash = HashAlgorithm.Create (hashName);
+ // 0 to 215 (216) then skip 4 (checksum)
+ int pe = peOffset + 88;
+ hash.TransformBlock (file, 0, pe, file, 0);
+ pe += 4;
+ // 220 to 279 (60) then skip 8 (IMAGE_DIRECTORY_ENTRY_SECURITY)
+ hash.TransformBlock (file, pe, 60, file, pe);
+ pe += 68;
+ // 288 to end of file
+ int n = file.Length - pe;
+ // minus any authenticode signature (with 8 bytes header)
+ if (dirSecurityOffset != 0)
+ n -= (dirSecuritySize);
+ hash.TransformFinalBlock (file, pe, n);
+
+ return hash.Hash;
+ }
+ }
+}
diff --git a/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs
new file mode 100755
index 00000000000..26d4f928ed7
--- /dev/null
+++ b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs
@@ -0,0 +1,365 @@
+//
+// AuthenticodeSignature.cs: Authenticode signature validator and generator
+//
+// Author:
+// Sebastien Pouliot (spouliot@motus.com)
+//
+// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
+//
+
+using System;
+using System.IO;
+using System.Security.Cryptography;
+
+using Mono.Security;
+using Mono.Security.X509;
+
+namespace Mono.Security.Authenticode {
+
+ // References:
+ // a. http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
+
+ public 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 X509Chain signerChain;
+ private X509Chain timestampChain;
+
+ public AuthenticodeDeformatter () : base ()
+ {
+ reason = -1;
+ signerChain = new X509Chain ();
+ timestampChain = new X509Chain ();
+ }
+
+ public AuthenticodeDeformatter (string fileName) : this ()
+ {
+ CheckSignature (fileName);
+ }
+
+ public string FileName {
+ get { return filename; }
+ set { CheckSignature (value); }
+ }
+
+ public byte[] Hash {
+ get {
+ if (signedHash == null)
+ return null;
+ return signedHash.Value;
+ }
+ }
+
+ public int Reason {
+ get {
+ if (reason == -1)
+ IsTrusted ();
+ return reason;
+ }
+ }
+
+ public bool IsTrusted ()
+ {
+ if (rawData == null) {
+ reason = 1;
+ return false;
+ }
+
+ if (signingCertificate == null) {
+ reason = 7;
+ return false;
+ }
+
+ if (signerChain.Root == null) {
+ reason = 6;
+ return false;
+ }
+
+ if (timestamp != DateTime.MinValue) {
+ if (timestampChain.Root == null) {
+ 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 { return rawData; }
+ }
+
+ public DateTime Timestamp {
+ get { return timestamp; }
+ }
+
+ public X509CertificateCollection Certificates {
+ get { return coll; }
+ }
+
+ private bool CheckSignature (string fileName)
+ {
+ filename = fileName;
+
+ // by default we try with MD5
+ string hashName = "MD5";
+ // compare the signature's hash with the hash of the file
+ hash = HashFile (filename, hashName);
+
+ // is a signature present ?
+ if (rawData == null)
+ return false;
+
+ PKCS7.ContentInfo ci = new PKCS7.ContentInfo (rawData);
+ if (ci.ContentType != PKCS7.signedData)
+ return false;
+
+ PKCS7.SignedData sd = new PKCS7.SignedData (ci.Content);
+ if (sd.ContentInfo.ContentType != spcIndirectDataContext)
+ return false;
+
+ coll = sd.Certificates;
+
+ ASN1 spc = sd.ContentInfo.Content;
+ signedHash = spc [0][1][1];
+ if (signedHash.Length == 20) {
+ // seems to be SHA-1, restart hashing
+ hashName = "SHA1";
+ hash = HashFile (filename, hashName);
+ }
+
+ if (!signedHash.CompareValue (hash))
+ return false;
+
+ // messageDigest is a hash of spcIndirectDataContext (which includes the file hash)
+ byte[] spcIDC = spc [0].Value;
+ HashAlgorithm ha = HashAlgorithm.Create (hashName);
+ byte[] messageDigest = ha.ComputeHash (spcIDC);
+
+ return VerifySignature (sd, messageDigest, hashName);
+ }
+
+ 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, string hashName)
+ {
+ 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][1][0].Value);
+ }
+ catch {
+ 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 (hashName);
+
+ // 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);
+ HashAlgorithm ha = HashAlgorithm.Create (hashName);
+ 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;
+ foreach (X509Certificate x509 in coll) {
+ if (CompareIssuerSerial (issuer, serial, x509)) {
+ // don't verify is key size don't match
+ if (x509.PublicKey.Length > (signature.Length >> 3)) {
+ RSACryptoServiceProvider rsa = (RSACryptoServiceProvider) x509.RSA;
+ if (rsa.VerifyHash (p7hash, hashOID, signature)) {
+ signerChain.LoadCertificates (coll);
+ if (signerChain.GetChain (x509) != null)
+ signingCertificate = x509;
+ else
+ return false;
+ }
+ }
+ }
+ }
+
+ for (int i=0; i < sd.SignerInfo.UnauthenticatedAttributes.Count; i++) {
+ ASN1 attr = (ASN1) sd.SignerInfo.UnauthenticatedAttributes [i];
+ string oid = ASN1Convert.ToOID (attr [0]);
+ switch (oid) {
+ case PKCS7.countersignature:
+ // SEQUENCE {
+ // OBJECT IDENTIFIER
+ // countersignature (1 2 840 113549 1 9 6)
+ // SET {
+ PKCS7.SignerInfo cs = new PKCS7.SignerInfo (attr [1]);
+ return VerifyCounterSignature (cs, signature, hashName);
+ default:
+ // we don't support other unauthenticated attributes
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ //private bool VerifyCounterSignature (ASN1 cs, byte[] signature, string hashName)
+ private bool VerifyCounterSignature (PKCS7.SignerInfo cs, byte[] signature, string hashName)
+ {
+ // SEQUENCE {
+ // INTEGER 1
+ if (cs.Version != 1)
+ return false;
+ // SEQUENCE {
+ // SEQUENCE {
+
+ string contentType = null;
+ ASN1 messageDigest = null;
+ for (int i=0; i < cs.AuthenticatedAttributes.Count; i++) {
+ // SEQUENCE {
+ // OBJECT IDENTIFIER
+ ASN1 attr = (ASN1) cs.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.2.840.113549.1.9.5":
+ // SEQUENCE {
+ // OBJECT IDENTIFIER
+ // signingTime (1 2 840 113549 1 9 5)
+ // SET {
+ // UTCTime '030124013651Z'
+ // }
+ // }
+ timestamp = ASN1Convert.ToDateTime (attr[1][0]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (contentType != PKCS7.data)
+ return false;
+
+ // verify message digest
+ if (messageDigest == null)
+ return false;
+ // TODO: must be read from the ASN.1 structure
+ switch (messageDigest.Length) {
+ case 16:
+ hashName = "MD5";
+ break;
+ case 20:
+ hashName = "SHA1";
+ break;
+ }
+ HashAlgorithm ha = HashAlgorithm.Create (hashName);
+ if (!messageDigest.CompareValue (ha.ComputeHash (signature)))
+ return false;
+
+ // verify signature
+ byte[] counterSignature = cs.Signature;
+ string hashOID = CryptoConfig.MapNameToOID (hashName);
+
+ // change to SET OF (not [0]) as per PKCS #7 1.5
+ ASN1 aa = new ASN1 (0x31);
+ foreach (ASN1 a in cs.AuthenticatedAttributes)
+ aa.Add (a);
+ byte[] p7hash = ha.ComputeHash (aa.GetBytes ());
+
+ // we need to try all certificates
+ string issuer = cs.IssuerName;
+ byte[] serial = cs.SerialNumber;
+ foreach (X509Certificate x509 in coll) {
+ if (CompareIssuerSerial (issuer, serial, x509)) {
+ // don't verify is key size don't match
+ if (x509.PublicKey.Length > (counterSignature.Length >> 3)) {
+ RSACryptoServiceProvider rsa = (RSACryptoServiceProvider) x509.RSA;
+ if (rsa.VerifyHash (p7hash, hashOID, counterSignature)) {
+ timestampChain.LoadCertificates (coll);
+ return (timestampChain.GetChain (x509) != null);
+ }
+ }
+ }
+ }
+ // no certificate can verify this signature!
+ return false;
+ }
+ }
+}
diff --git a/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeFormatter.cs b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeFormatter.cs
new file mode 100755
index 00000000000..1410cc88f62
--- /dev/null
+++ b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeFormatter.cs
@@ -0,0 +1,297 @@
+//
+// AuthenticodeFormatter.cs: Authenticode signature generator
+//
+// Author:
+// Sebastien Pouliot (spouliot@motus.com)
+//
+// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
+//
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Security.Cryptography;
+//using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Net;
+
+using Mono.Security;
+using Mono.Security.X509;
+
+namespace Mono.Security.Authenticode {
+
+ public class AuthenticodeFormatter : AuthenticodeBase {
+
+ private Authority authority;
+ private X509CertificateCollection certs;
+ private ArrayList crls;
+ private string hash;
+ private RSA rsa;
+ private string timestamp;
+ private ASN1 authenticode;
+ private PKCS7.SignedData pkcs7;
+ private string description;
+ private string url;
+
+ public AuthenticodeFormatter () : base ()
+ {
+ certs = new X509CertificateCollection ();
+ crls = new ArrayList ();
+ authority = Authority.Maximum;
+ pkcs7 = new PKCS7.SignedData ();
+ }
+
+ public Authority Authority {
+ get { return authority; }
+ set { authority = value; }
+ }
+
+ public X509CertificateCollection Certificates {
+ get { return certs; }
+ }
+
+ public ArrayList CRL {
+ get { return crls; }
+ }
+
+ public string Hash {
+ get {
+ if (hash == null)
+ hash = "MD5";
+ return hash;
+ }
+ set {
+ string h = value.ToUpper ();
+ if ((h == "MD5") || (hash == "SHA1"))
+ hash = value;
+ else
+ throw new ArgumentException ("Invalid Authenticode hash algorithm");
+ }
+ }
+
+ public RSA RSA {
+ get { return rsa; }
+ set { rsa = value; }
+ }
+
+ public string TimestampURL {
+ get { return timestamp; }
+ set { timestamp = value; } // URL
+ }
+
+ public string Description {
+ get { return description; }
+ set { description = value; }
+ }
+
+ public string URL {
+ get { return url; }
+ set { url = value; } // URL
+ }
+
+ private ASN1 AlgorithmIdentifier (string oid)
+ {
+ ASN1 ai = new ASN1 (0x30);
+ ai.Add (ASN1Convert.FromOID (oid));
+ ai.Add (new ASN1 (0x05)); // NULL
+ return ai;
+ }
+
+ private ASN1 Attribute (string oid, ASN1 value)
+ {
+ ASN1 attr = new ASN1 (0x30);
+ attr.Add (ASN1Convert.FromOID (oid));
+ ASN1 aset = attr.Add (new ASN1 (0x31));
+ aset.Add (value);
+ return attr;
+ }
+
+ private ASN1 Opus (string description, string url)
+ {
+ ASN1 opus = new ASN1 (0x30);
+ if (description != null) {
+ ASN1 part1 = opus.Add (new ASN1 (0xA0));
+ part1.Add (new ASN1 (0x80, Encoding.BigEndianUnicode.GetBytes (description)));
+ }
+ if (url != null) {
+ ASN1 part2 = opus.Add (new ASN1 (0xA1));
+ part2.Add (new ASN1 (0x80, Encoding.ASCII.GetBytes (url)));
+ }
+ return opus;
+ }
+
+ // pkcs 1
+ private const string rsaEncryption = "1.2.840.113549.1.1.1";
+ // pkcs 7
+ private const string data = "1.2.840.113549.1.7.1";
+ private const string signedData = "1.2.840.113549.1.7.2";
+ // pkcs 9
+ private const string contentType = "1.2.840.113549.1.9.3";
+ private const string messageDigest = "1.2.840.113549.1.9.4";
+ private const string countersignature = "1.2.840.113549.1.9.6";
+ // microsoft spc (software publisher certificate)
+ private const string spcStatementType = "1.3.6.1.4.1.311.2.1.11";
+ private const string spcSpOpusInfo = "1.3.6.1.4.1.311.2.1.12";
+ private const string spcPelmageData = "1.3.6.1.4.1.311.2.1.15";
+ private const string individualCodeSigning = "1.3.6.1.4.1.311.2.1.21";
+ private const string commercialCodeSigning = "1.3.6.1.4.1.311.2.1.22";
+ private const string timestampCountersignature = "1.3.6.1.4.1.311.3.2.1";
+
+ private static byte[] version = { 0x01 };
+ private static byte[] obsolete = { 0x03, 0x01, 0x00, 0xA0, 0x20, 0xA2, 0x1E, 0x80, 0x1C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x4F, 0x00, 0x62, 0x00, 0x73, 0x00, 0x6F, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x74, 0x00, 0x65, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x3E };
+
+ private byte[] Header (byte[] fileHash, string hashAlgorithm)
+ {
+ string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm);
+ ASN1 content = new ASN1 (0x30);
+ ASN1 c1 = content.Add (new ASN1 (0x30));
+ c1.Add (ASN1Convert.FromOID (spcPelmageData));
+ c1.Add (new ASN1 (0x30, obsolete));
+ ASN1 c2 = content.Add (new ASN1 (0x30));
+ c2.Add (AlgorithmIdentifier (hashOid));
+ c2.Add (new ASN1 (0x04, fileHash));
+
+ pkcs7.HashName = hashAlgorithm;
+ pkcs7.Certificates.AddRange (certs);
+ pkcs7.ContentInfo.ContentType = spcIndirectDataContext;
+ pkcs7.ContentInfo.Content.Add (content);
+
+ pkcs7.SignerInfo.Certificate = certs [0];
+ pkcs7.SignerInfo.Key = rsa;
+ pkcs7.SignerInfo.AuthenticatedAttributes.Add (Attribute (spcSpOpusInfo, Opus (description, url)));
+ pkcs7.SignerInfo.AuthenticatedAttributes.Add (Attribute (contentType, ASN1Convert.FromOID (spcIndirectDataContext)));
+ pkcs7.SignerInfo.AuthenticatedAttributes.Add (Attribute (spcStatementType, new ASN1 (0x30, ASN1Convert.FromOID (commercialCodeSigning).GetBytes ())));
+
+ ASN1 temp = pkcs7.ASN1; // sign
+ return pkcs7.SignerInfo.Signature;
+ }
+
+ public ASN1 TimestampRequest (byte[] signature)
+ {
+ PKCS7.ContentInfo ci = new PKCS7.ContentInfo (PKCS7.data);
+ ci.Content.Add (new ASN1 (0x04, signature));
+ return PKCS7.AlgorithmIdentifier (timestampCountersignature, ci.ASN1);
+ }
+
+ public void ProcessTimestamp (byte[] tsres)
+ {
+ ASN1 ts = new ASN1 (Convert.FromBase64String (Encoding.ASCII.GetString (tsres)));
+ // first validate the received message
+ // TODO
+
+ // add the supplied certificates inside our signature
+ for (int i=0; i < ts[1][0][3].Count; i++)
+ pkcs7.Certificates.Add (new X509Certificate (ts[1][0][3][i].GetBytes ()));
+
+ // add an unauthentified attribute to our signature
+ pkcs7.SignerInfo.UnauthenticatedAttributes.Add (Attribute (countersignature, ts[1][0][4][0]));
+ }
+
+ public bool Sign (string fileName)
+ {
+ string hashAlgorithm = "MD5";
+ FileStream fs = new FileStream (fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
+ byte[] file = new byte [fs.Length];
+ fs.Read (file, 0, file.Length);
+ fs.Close ();
+
+ // MZ - DOS header
+ if (BitConverter.ToUInt16 (file, 0) != 0x5A4D)
+ return false;
+
+ // find offset of PE header
+ int peOffset = BitConverter.ToInt32 (file, 60);
+ if (peOffset > file.Length)
+ return false;
+
+ // PE - NT header
+ if (BitConverter.ToUInt16 (file, peOffset) != 0x4550)
+ return false;
+
+ // IMAGE_DIRECTORY_ENTRY_SECURITY
+ int dirSecurityOffset = BitConverter.ToInt32 (file, peOffset + 152);
+ int dirSecuritySize = BitConverter.ToInt32 (file, peOffset + 156);
+
+ if (dirSecuritySize > 8) {
+ rawData = new byte [dirSecuritySize - 8];
+ Array.Copy (file, dirSecurityOffset + 8, rawData, 0, rawData.Length);
+ }
+ else
+ rawData = null;
+
+ HashAlgorithm hash = HashAlgorithm.Create (hashAlgorithm);
+ // 0 to 215 (216) then skip 4 (checksum)
+ int pe = peOffset + 88;
+ hash.TransformBlock (file, 0, pe, file, 0);
+ pe += 4;
+ // 220 to 279 (60) then skip 8 (IMAGE_DIRECTORY_ENTRY_SECURITY)
+ hash.TransformBlock (file, pe, 60, file, pe);
+ pe += 68;
+ // 288 to end of file
+ int n = file.Length - pe;
+ // minus any authenticode signature (with 8 bytes header)
+ if (dirSecurityOffset != 0)
+ n -= (dirSecuritySize);
+ hash.TransformFinalBlock (file, pe, n);
+
+ //
+ byte[] signature = Header (hash.Hash, hashAlgorithm);
+ if (timestamp != null) {
+ ASN1 tsreq = TimestampRequest (signature);
+ WebClient wc = new WebClient ();
+ wc.Headers.Add ("Content-Type", "application/octet-stream");
+ wc.Headers.Add ("Accept", "application/octet-stream");
+ byte[] tsdata = Encoding.ASCII.GetBytes (Convert.ToBase64String (tsreq.GetBytes ()));
+ byte[] tsres = wc.UploadData (timestamp, tsdata);
+ ProcessTimestamp (tsres);
+ }
+ PKCS7.ContentInfo sign = new PKCS7.ContentInfo (signedData);
+ sign.Content.Add (pkcs7.ASN1);
+ authenticode = sign.ASN1;
+
+ // debug
+ fs = File.Open (fileName + ".sig", FileMode.Create, FileAccess.Write);
+ byte[] asn = authenticode.GetBytes ();
+ fs.Write (asn, 0, asn.Length);
+ fs.Close ();
+
+ File.Copy (fileName, fileName + ".bak", true);
+
+ fs = File.Open (fileName, FileMode.Create, FileAccess.Write);
+ // IMAGE_DIRECTORY_ENTRY_SECURITY (offset, size)
+ byte[] data = BitConverter.GetBytes (file.Length);
+ file [peOffset + 152] = data [0];
+ file [peOffset + 153] = data [1];
+ file [peOffset + 154] = data [2];
+ file [peOffset + 155] = data [3];
+ int size = asn.Length + 8;
+ // must be a multiple of 8 bytes
+ int addsize = (size % 8);
+ if (addsize > 0)
+ addsize = 8 - addsize;
+ size += addsize;
+ data = BitConverter.GetBytes (size); // header
+ file [peOffset + 156] = data [0];
+ file [peOffset + 157] = data [1];
+ file [peOffset + 158] = data [2];
+ file [peOffset + 159] = data [3];
+ fs.Write (file, 0, file.Length);
+ fs.Write (data, 0, data.Length); // length (again)
+ data = BitConverter.GetBytes (0x00020200); // magic
+ fs.Write (data, 0, data.Length);
+ fs.Write (asn, 0, asn.Length);
+ // fill up
+ byte[] fillup = new byte [addsize];
+ fs.Write (fillup, 0, fillup.Length);
+ fs.Close ();
+
+ return true;
+ }
+
+ // in case we just want to timestamp the file
+ public bool Timestamp (string fileName)
+ {
+ return true;
+ }
+ }
+}
diff --git a/mcs/class/Mono.Security/Mono.Security.Authenticode/ChangeLog b/mcs/class/Mono.Security/Mono.Security.Authenticode/ChangeLog
index 0acf2ed937c..caa31ff19ff 100644
--- a/mcs/class/Mono.Security/Mono.Security.Authenticode/ChangeLog
+++ b/mcs/class/Mono.Security/Mono.Security.Authenticode/ChangeLog
@@ -1,3 +1,14 @@
+2003-09-01 Sebastien Pouliot <spouliot@videotron.ca>
+
+ * AuthenticodeBase.cs: New. Base class including how to hash a
+ PE file.
+ * AuthenticodeDeformatter.cs: New. Class to decode Authenticode(tm)
+ signatures.
+ * AuthenticodeFormatter.cs: New. Class to encode a Authenticode(tm)
+ signature, and optionally a timestamp, into a PE file.
+ * SoftwarePublisherCertificate.cs: Now use Mono.Security.X509.
+ X509Certificate class.
+
2003-06-19 Nick Drochak <ndrochak@gol.com>
* PrivateKey.cs: Work around for mcs? bug 45127.
diff --git a/mcs/class/Mono.Security/Mono.Security.Authenticode/SoftwarePublisherCertificate.cs b/mcs/class/Mono.Security/Mono.Security.Authenticode/SoftwarePublisherCertificate.cs
index f7bd219bf63..0c7026b2519 100644
--- a/mcs/class/Mono.Security/Mono.Security.Authenticode/SoftwarePublisherCertificate.cs
+++ b/mcs/class/Mono.Security/Mono.Security.Authenticode/SoftwarePublisherCertificate.cs
@@ -11,9 +11,9 @@
using System;
using System.Collections;
using System.IO;
-using System.Security.Cryptography.X509Certificates;
using Mono.Security;
+using Mono.Security.X509;
namespace Mono.Security.Authenticode {