Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@@ -0,0 +1,291 @@
//
// AuthenticodeBase.cs: Authenticode signature base class
//
// 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.Security.Cryptography;
namespace Mono.Security.Authenticode {
// References:
// a. http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
#if INSIDE_CORLIB
internal
#else
public
#endif
enum Authority {
Individual,
Commercial,
Maximum
}
#if INSIDE_CORLIB
internal
#else
public
#endif
class AuthenticodeBase {
public const string spcIndirectDataContext = "1.3.6.1.4.1.311.2.1.4";
private byte[] fileblock;
private FileStream fs;
private int blockNo;
private int blockLength;
private int peOffset;
private int dirSecurityOffset;
private int dirSecuritySize;
private int coffSymbolTableOffset;
public AuthenticodeBase ()
{
fileblock = new byte [4096];
}
internal int PEOffset {
get {
if (blockNo < 1)
ReadFirstBlock ();
return peOffset;
}
}
internal int CoffSymbolTableOffset {
get {
if (blockNo < 1)
ReadFirstBlock ();
return coffSymbolTableOffset;
}
}
internal int SecurityOffset {
get {
if (blockNo < 1)
ReadFirstBlock ();
return dirSecurityOffset;
}
}
internal void Open (string filename)
{
if (fs != null)
Close ();
fs = new FileStream (filename, FileMode.Open, FileAccess.Read, FileShare.Read);
blockNo = 0;
}
internal void Close ()
{
if (fs != null) {
fs.Close ();
fs = null;
}
}
internal void ReadFirstBlock ()
{
int error = ProcessFirstBlock ();
if (error != 0) {
string msg = Locale.GetText ("Cannot sign non PE files, e.g. .CAB or .MSI files (error {0}).",
error);
throw new NotSupportedException (msg);
}
}
internal int ProcessFirstBlock ()
{
if (fs == null)
return 1;
fs.Position = 0;
// read first block - it will include (100% sure)
// the MZ header and (99.9% sure) the PE header
blockLength = fs.Read (fileblock, 0, fileblock.Length);
blockNo = 1;
if (blockLength < 64)
return 2; // invalid PE file
// 1. Validate the MZ header informations
// 1.1. Check for magic MZ at start of header
if (BitConverterLE.ToUInt16 (fileblock, 0) != 0x5A4D)
return 3;
// 1.2. Find the offset of the PE header
peOffset = BitConverterLE.ToInt32 (fileblock, 60);
if (peOffset > fileblock.Length) {
// just in case (0.1%) this can actually happen
string msg = String.Format (Locale.GetText (
"Header size too big (> {0} bytes)."),
fileblock.Length);
throw new NotSupportedException (msg);
}
if (peOffset > fs.Length)
return 4;
// 2. Read between DOS header and first part of PE header
// 2.1. Check for magic PE at start of header
// PE - NT header ('P' 'E' 0x00 0x00)
if (BitConverterLE.ToUInt32 (fileblock, peOffset) != 0x4550)
return 5;
// 2.2. Locate IMAGE_DIRECTORY_ENTRY_SECURITY (offset and size)
dirSecurityOffset = BitConverterLE.ToInt32 (fileblock, peOffset + 152);
dirSecuritySize = BitConverterLE.ToInt32 (fileblock, peOffset + 156);
// COFF symbol tables are deprecated - we'll strip them if we see them!
// (otherwise the signature won't work on MS and we don't want to support COFF for that)
coffSymbolTableOffset = BitConverterLE.ToInt32 (fileblock, peOffset + 12);
return 0;
}
internal byte[] GetSecurityEntry ()
{
if (blockNo < 1)
ReadFirstBlock ();
if (dirSecuritySize > 8) {
// remove header from size (not ASN.1 based)
byte[] secEntry = new byte [dirSecuritySize - 8];
// position after header and read entry
fs.Position = dirSecurityOffset + 8;
fs.Read (secEntry, 0, secEntry.Length);
return secEntry;
}
return null;
}
internal byte[] GetHash (HashAlgorithm hash)
{
if (blockNo < 1)
ReadFirstBlock ();
fs.Position = blockLength;
// hash the rest of the file
long n;
int addsize = 0;
// minus any authenticode signature (with 8 bytes header)
if (dirSecurityOffset > 0) {
// it is also possible that the signature block
// starts within the block in memory (small EXE)
if (dirSecurityOffset < blockLength) {
blockLength = dirSecurityOffset;
n = 0;
} else {
n = dirSecurityOffset - blockLength;
}
} else if (coffSymbolTableOffset > 0) {
fileblock[PEOffset + 12] = 0;
fileblock[PEOffset + 13] = 0;
fileblock[PEOffset + 14] = 0;
fileblock[PEOffset + 15] = 0;
fileblock[PEOffset + 16] = 0;
fileblock[PEOffset + 17] = 0;
fileblock[PEOffset + 18] = 0;
fileblock[PEOffset + 19] = 0;
// it is also possible that the signature block
// starts within the block in memory (small EXE)
if (coffSymbolTableOffset < blockLength) {
blockLength = coffSymbolTableOffset;
n = 0;
} else {
n = coffSymbolTableOffset - blockLength;
}
} else {
addsize = (int) (fs.Length & 7);
if (addsize > 0)
addsize = 8 - addsize;
n = fs.Length - blockLength;
}
// Authenticode(r) gymnastics
// Hash from (generally) 0 to 215 (216 bytes)
int pe = peOffset + 88;
hash.TransformBlock (fileblock, 0, pe, fileblock, 0);
// then skip 4 for checksum
pe += 4;
// Continue hashing from (generally) 220 to 279 (60 bytes)
hash.TransformBlock (fileblock, pe, 60, fileblock, pe);
// then skip 8 bytes for IMAGE_DIRECTORY_ENTRY_SECURITY
pe += 68;
// everything is present so start the hashing
if (n == 0) {
// hash the (only) block
hash.TransformFinalBlock (fileblock, pe, blockLength - pe);
}
else {
// hash the last part of the first (already in memory) block
hash.TransformBlock (fileblock, pe, blockLength - pe, fileblock, pe);
// hash by blocks of 4096 bytes
long blocks = (n >> 12);
int remainder = (int)(n - (blocks << 12));
if (remainder == 0) {
blocks--;
remainder = 4096;
}
// blocks
while (blocks-- > 0) {
fs.Read (fileblock, 0, fileblock.Length);
hash.TransformBlock (fileblock, 0, fileblock.Length, fileblock, 0);
}
// remainder
if (fs.Read (fileblock, 0, remainder) != remainder)
return null;
if (addsize > 0) {
hash.TransformBlock (fileblock, 0, remainder, fileblock, 0);
hash.TransformFinalBlock (new byte [addsize], 0, addsize);
} else {
hash.TransformFinalBlock (fileblock, 0, remainder);
}
}
return hash.Hash;
}
// for compatibility only
protected byte[] HashFile (string fileName, string hashName)
{
try {
Open (fileName);
HashAlgorithm hash = HashAlgorithm.Create (hashName);
byte[] result = GetHash (hash);
Close ();
return result;
}
catch {
return null;
}
}
}
}

View File

@@ -0,0 +1,456 @@
//
// 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 Mono.Security.Cryptography;
using Mono.Security.X509;
namespace Mono.Security.Authenticode {
// References:
// a. http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
#if INSIDE_CORLIB
internal
#else
public
#endif
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;
}
if (reason == -1)
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 = MD5.Create ();
hash = GetHash (ha);
break;
case 20:
ha = SHA1.Create ();
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;
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)) {
// return the signing certificate even if the signature isn't correct
// (required behaviour for 2.0 support)
signingCertificate = x509;
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider) x509.RSA;
if (rsa.VerifyHash (p7hash, hashOID, signature)) {
signerChain.LoadCertificates (coll);
trustedRoot = signerChain.Build (x509);
break;
}
}
}
}
// timestamp signature is optional
if (sd.SignerInfo.UnauthenticatedAttributes.Count == 0) {
trustedTimestampRoot = true;
} else {
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.Oid.countersignature:
// SEQUENCE {
// OBJECT IDENTIFIER
// countersignature (1 2 840 113549 1 9 6)
// SET {
PKCS7.SignerInfo cs = new PKCS7.SignerInfo (attr[1]);
trustedTimestampRoot = VerifyCounterSignature (cs, signature);
break;
default:
// we don't support other unauthenticated attributes
break;
}
}
}
return (trustedRoot && trustedTimestampRoot);
}
private bool VerifyCounterSignature (PKCS7.SignerInfo cs, byte[] signature)
{
// 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.Oid.data)
return false;
// verify message digest
if (messageDigest == null)
return false;
// TODO: must be read from the ASN.1 structure
string hashName = null;
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;
// 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)) {
if (x509.PublicKey.Length > counterSignature.Length) {
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider) x509.RSA;
// we need to HACK around bad (PKCS#1 1.5) signatures made by Verisign Timestamp Service
// and this means copying stuff into our own RSAManaged to get the required flexibility
RSAManaged rsam = new RSAManaged ();
rsam.ImportParameters (rsa.ExportParameters (false));
if (PKCS1.Verify_v15 (rsam, ha, p7hash, counterSignature, true)) {
timestampChain.LoadCertificates (coll);
return (timestampChain.Build (x509));
}
}
}
}
// no certificate can verify this signature!
return false;
}
private void Reset ()
{
filename = null;
entry = null;
hash = null;
signedHash = null;
signingCertificate = null;
reason = -1;
trustedRoot = false;
trustedTimestampRoot = false;
signerChain.Reset ();
timestampChain.Reset ();
timestamp = DateTime.MinValue;
}
}
}

View File

@@ -0,0 +1,376 @@
//
// AuthenticodeFormatter.cs: Authenticode signature generator
//
// Author:
// Sebastien Pouliot <sebastien@ximian.com>
//
// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
// Copyright (C) 2004, 2006-2007 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.Collections;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
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 Uri timestamp;
private ASN1 authenticode;
private PKCS7.SignedData pkcs7;
private string description;
private Uri 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 {
if (value == null)
throw new ArgumentNullException ("Hash");
string h = value.ToUpper (CultureInfo.InvariantCulture);
switch (h) {
case "MD5":
case "SHA1":
hash = h;
break;
default:
throw new ArgumentException ("Invalid Authenticode hash algorithm");
}
}
}
public RSA RSA {
get { return rsa; }
set { rsa = value; }
}
public Uri TimestampUrl {
get { return timestamp; }
set { timestamp = value; }
}
public string Description {
get { return description; }
set { description = value; }
}
public Uri Url {
get { return url; }
set { url = value; }
}
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;
ASN1 opus = null;
if (url == null)
opus = Attribute (spcSpOpusInfo, Opus (description, null));
else
opus = Attribute (spcSpOpusInfo, Opus (description, url.ToString ()));
pkcs7.SignerInfo.AuthenticatedAttributes.Add (opus);
// When using the MS Root Agency (test) we can't include this attribute in the signature or it won't validate!
// pkcs7.SignerInfo.AuthenticatedAttributes.Add (Attribute (spcStatementType, new ASN1 (0x30, ASN1Convert.FromOid (commercialCodeSigning).GetBytes ())));
pkcs7.GetASN1 (); // sign
return pkcs7.SignerInfo.Signature;
}
public ASN1 TimestampRequest (byte[] signature)
{
PKCS7.ContentInfo ci = new PKCS7.ContentInfo (PKCS7.Oid.data);
ci.Content.Add (new ASN1 (0x04, signature));
return PKCS7.AlgorithmIdentifier (timestampCountersignature, ci.ASN1);
}
public void ProcessTimestamp (byte[] response)
{
ASN1 ts = new ASN1 (Convert.FromBase64String (Encoding.ASCII.GetString (response)));
// 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]));
}
private byte[] Timestamp (byte[] signature)
{
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 ()));
return wc.UploadData (timestamp.ToString (), tsdata);
}
private bool Save (string fileName, byte[] asn)
{
#if DEBUG
using (FileStream fs = File.Open (fileName + ".sig", FileMode.Create, FileAccess.Write)) {
fs.Write (asn, 0, asn.Length);
fs.Close ();
}
#endif
// someday I may be sure enough to move this into DEBUG ;-)
File.Copy (fileName, fileName + ".bak", true);
using (FileStream fs = File.Open (fileName, FileMode.Open, FileAccess.ReadWrite)) {
int filesize;
if (SecurityOffset > 0) {
// file was already signed, we'll reuse the position for the updated signature
filesize = SecurityOffset;
} else if (CoffSymbolTableOffset > 0) {
// strip (deprecated) COFF symbol table
fs.Seek (PEOffset + 12, SeekOrigin.Begin);
for (int i = 0; i < 8; i++)
fs.WriteByte (0);
// we'll put the Authenticode signature at this same place (just after the last section)
filesize = CoffSymbolTableOffset;
} else {
// file was never signed, nor does it contains (deprecated) COFF symbols
filesize = (int)fs.Length;
}
// must be a multiple of 8 bytes
int addsize = (filesize & 7);
if (addsize > 0)
addsize = 8 - addsize;
// IMAGE_DIRECTORY_ENTRY_SECURITY (offset, size)
byte[] data = BitConverterLE.GetBytes (filesize + addsize);
fs.Seek (PEOffset + 152, SeekOrigin.Begin);
fs.Write (data, 0, 4);
int size = asn.Length + 8;
int addsize_signature = (size & 7);
if (addsize_signature > 0)
addsize_signature = 8 - addsize_signature;
data = BitConverterLE.GetBytes (size + addsize_signature);
fs.Seek (PEOffset + 156, SeekOrigin.Begin);
fs.Write (data, 0, 4);
fs.Seek (filesize, SeekOrigin.Begin);
// align certificate entry to a multiple of 8 bytes
if (addsize > 0) {
byte[] fillup = new byte[addsize];
fs.Write (fillup, 0, fillup.Length);
}
fs.Write (data, 0, data.Length); // length (again)
data = BitConverterLE.GetBytes (0x00020200); // magic
fs.Write (data, 0, data.Length);
fs.Write (asn, 0, asn.Length);
if (addsize_signature > 0) {
byte[] fillup = new byte[addsize_signature];
fs.Write (fillup, 0, fillup.Length);
}
fs.Close ();
}
return true;
}
public bool Sign (string fileName)
{
try {
Open (fileName);
HashAlgorithm hash = HashAlgorithm.Create (Hash);
// 0 to 215 (216) then skip 4 (checksum)
byte[] digest = GetHash (hash);
byte[] signature = Header (digest, Hash);
if (timestamp != null) {
byte[] ts = Timestamp (signature);
// add timestamp information inside the current pkcs7 SignedData instance
// (this is possible because the data isn't yet signed)
ProcessTimestamp (ts);
}
PKCS7.ContentInfo sign = new PKCS7.ContentInfo (signedData);
sign.Content.Add (pkcs7.ASN1);
authenticode = sign.ASN1;
Close ();
return Save (fileName, authenticode.GetBytes ());
}
catch (Exception e) {
Console.WriteLine (e);
}
return false;
}
// in case we just want to timestamp the file
public bool Timestamp (string fileName)
{
try {
AuthenticodeDeformatter def = new AuthenticodeDeformatter (fileName);
byte[] signature = def.Signature;
if (signature != null) {
Open (fileName);
PKCS7.ContentInfo ci = new PKCS7.ContentInfo (signature);
pkcs7 = new PKCS7.SignedData (ci.Content);
byte[] response = Timestamp (pkcs7.SignerInfo.Signature);
ASN1 ts = new ASN1 (Convert.FromBase64String (Encoding.ASCII.GetString (response)));
// insert new certificates and countersignature into the original signature
ASN1 asn = new ASN1 (signature);
ASN1 content = asn.Element (1, 0xA0);
if (content == null)
return false;
ASN1 signedData = content.Element (0, 0x30);
if (signedData == null)
return false;
// add the supplied certificates inside our signature
ASN1 certificates = signedData.Element (3, 0xA0);
if (certificates == null) {
certificates = new ASN1 (0xA0);
signedData.Add (certificates);
}
for (int i = 0; i < ts[1][0][3].Count; i++) {
certificates.Add (ts[1][0][3][i]);
}
// add an unauthentified attribute to our signature
ASN1 signerInfoSet = signedData[signedData.Count - 1];
ASN1 signerInfo = signerInfoSet[0];
ASN1 unauthenticated = signerInfo[signerInfo.Count - 1];
if (unauthenticated.Tag != 0xA1) {
unauthenticated = new ASN1 (0xA1);
signerInfo.Add (unauthenticated);
}
unauthenticated.Add (Attribute (countersignature, ts[1][0][4][0]));
return Save (fileName, asn.GetBytes ());
}
}
catch (Exception e) {
Console.WriteLine (e);
}
return false;
}
}
}

View File

@@ -0,0 +1,166 @@
2008-12-23 Sebastien Pouliot <sebastien@ximian.com>
* SoftwarePublisherCertificate.cs: Support PKCS7 files that with
PEM headers around the base64 content.
[Fix bug #457658]
2008-05-16 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeFormatter.cs: Throw an NotSupportedException if we're
trying to sign a non-PE (portable executable) file. We do not support
signing CAB and MSI files (nor does mono generates them).
[Partial fix for #388602, kept open as Enhancement]
2008-01-10 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeDeformatter.cs: Use RSAManaged and the new overloaded
PKCS1.Verify_v15 with tryNonStandardEncoding == true when verifying
timestamping certificate signatures. Fix for #350958
2007-11-01 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeFormatter.cs: Allow signature of very big files (e.g. a
1.5Gb EXE). Original patch from Ondrej Kelle;
2007-04-26 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeDeformatter.cs: Don't reset an existing reason inside
IsTrusted.
2007-01-12 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeFormatter.cs: The ASN.1 structure must be padded to a
multiple of 8 bytes, else the signature is invalid - even if the hash
is correct!
2006-12-14 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeBase.cs: Support (strip) COFF symbol table when
calculating the hash value of a PE file.
* AuthenticodeDeformatter.cs: Adapt to changes in base class.
* AuthenticodeFormatter.cs: Reuse more code from base class. Strip
COFF symbol table (if present). Fix alignment (on 8 bytes) for the
signature location.
2006-11-08 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeDeformatter.cs: Return (find) the SigningCertificate
even if the signature isn't verifiable. This is the behaviour required
for 2.0.
2006-06-14 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeBase.cs: Fix destination offset. Note that this works
under MS but not under Mono.
* AuthenticodeDeformatter.cs: Report a more useful error if the file
hash doesn't match the signed hash.
* AuthenticodeFormatter.cs: Implemented support for Timestamp method.
2005-04-18 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeFormatter.cs: Commented unused private constants to
remove compiler warnings.
2005-04-08 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeDeformatter.cs: In synch with corlib version.
2004-11-05 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeFormatter.cs: Now use BitConverterLE for explicit
little-endian convertion.
* PrivateKey.cs: Now use BitConverterLE for explicit little-endian
convertion for PVK files.
2004-10-29 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeFormatter.cs: Fixed spcSpOpusInfo attribute (it wasn't
added) and contentType attribute (was added 2 times). Also fixed the
case where we signed an already Authenticode signed file (re-signing).
* SoftwarePublisherCertificate.cs: Support for base64 encoded ASN.1
files (either Unicode or ASCII).
2004-10-22 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeFormatter.cs: Fixed case where Url is null (broken since
we switched from string to Uri). Now use "using" for FileStream.
2004-09-17 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeDeformatter.cs: In synch with corlib version. Fixed all
level 4 compilation warnings.
* AuthenticodeFormatter.cs: Fixed all level 4 compilation warnings.
* PrivateKey.cs: Fixed all level 4 compilation warnings.
2004-09-07 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeBase.cs: Reworked not to load the entire file into
memory before hashing it (now in 4kb blocks). Splitted code in many
methods to allow "lighter" use by the deformatter.
* AuthenticodeDeformatter.cs: Less memory hungry so it can be used to
create Publisher evidences. No hash instance are created if the file
isn't signed.
* AuthenticodeFormatter.cs: Adapted to API changes.
2004-05-11 Sebastien Pouliot <sebastien@ximian.com>
* PrivateKey.cs: Better exception reporting. Added globalization to
exceptions.
* SoftwarePublisherCertificate.cs: Better exception reporting. Added
globalization to exceptions.
2004-04-28 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeBase.cs: Added missing (overwritten) #if INSIDE_CORLIB
directives to hides type and enum in corlib.
2004-04-22 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeBase.cs: FxCop-ized. CLS compliance. Also includes
endian fixes from Bernie Solomon.
* AuthenticodeDeformatter.cs: FxCop-ized. CLS compliance.
* AuthenticodeFormatter.cs: FxCop-ized. CLS compliance.
* PrivateKey.cs: Replaced Array.Copy with Buffer.BlockCopy.
* SoftwarePublisherCertificate.cs: FxCop-ized.
2004-02-23 Sebastien Pouliot <sebastien@ximian.com>
* PrivateKey.cs: Adjusted to catch exceptions from CryptoConvert.
FromCapiPrivateKeyBlob when dealing with weakly encrypted keys.
2004-02-20 Sebastien Pouliot <sebastien@ximian.com>
* AuthenticodeDeformatter.cs: Updated to use the new X509Chain syntax.
2003-12-15 Sebastien Pouliot <spouliot@videotron.ca>
* AuthenticodeDeformatter.cs: Now throw a COMException for invalid
signature. Added a SigningCertificate property (to be independant of
the certificate collection ordering).
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.
2003-03-15 Sebastien Pouliot <spouliot@videotron.ca>
* PrivateKey.cs: New. Class to load or create PVK (PriVate Key)
files (a Microsoft specific file format for private keys).
2003-03-06 Sebastien Pouliot <spouliot@videotron.ca>
* SoftwarePublisherCertificate.cs: New. Class to load or create
SPC files (which are PKCS#7 files containing only certificates
and CRL).

View File

@@ -0,0 +1,254 @@
//
// PrivateKey.cs - Private Key (PVK) Format Implementation
//
// Author:
// Sebastien Pouliot <sebastien@ximian.com>
//
// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
// Copyright (C) 2004 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.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Mono.Security.Cryptography;
namespace Mono.Security.Authenticode {
// References:
// a. http://www.drh-consultancy.demon.co.uk/pvk.html
#if INSIDE_SYSTEM
internal
#else
public
#endif
class PrivateKey {
private const uint magic = 0xb0b5f11e;
private bool encrypted;
private RSA rsa;
private bool weak;
private int keyType;
public PrivateKey ()
{
keyType = 2; // required for MS makecert !!!
}
public PrivateKey (byte[] data, string password)
{
if (data == null)
throw new ArgumentNullException ("data");
if (!Decode (data, password)) {
throw new CryptographicException (
Locale.GetText ("Invalid data and/or password"));
}
}
public bool Encrypted {
get { return encrypted; }
}
public int KeyType {
get { return keyType; }
set { keyType = value; }
}
public RSA RSA {
get { return rsa; }
set { rsa = value; }
}
public bool Weak {
get { return ((encrypted) ? weak : true); }
set { weak = value; }
}
private byte[] DeriveKey (byte[] salt, string password)
{
byte[] pwd = Encoding.ASCII.GetBytes (password);
SHA1 sha1 = (SHA1)SHA1.Create ();
sha1.TransformBlock (salt, 0, salt.Length, salt, 0);
sha1.TransformFinalBlock (pwd, 0, pwd.Length);
byte[] key = new byte [16];
Buffer.BlockCopy (sha1.Hash, 0, key, 0, 16);
sha1.Clear ();
Array.Clear (pwd, 0, pwd.Length);
return key;
}
private bool Decode (byte[] pvk, string password)
{
// DWORD magic
if (BitConverterLE.ToUInt32 (pvk, 0) != magic)
return false;
// DWORD reserved
if (BitConverterLE.ToUInt32 (pvk, 4) != 0x0)
return false;
// DWORD keytype
keyType = BitConverterLE.ToInt32 (pvk, 8);
// DWORD encrypted
encrypted = (BitConverterLE.ToUInt32 (pvk, 12) == 1);
// DWORD saltlen
int saltlen = BitConverterLE.ToInt32 (pvk, 16);
// DWORD keylen
int keylen = BitConverterLE.ToInt32 (pvk, 20);
byte[] keypair = new byte [keylen];
Buffer.BlockCopy (pvk, 24 + saltlen, keypair, 0, keylen);
// read salt (if present)
if (saltlen > 0) {
if (password == null)
return false;
byte[] salt = new byte [saltlen];
Buffer.BlockCopy (pvk, 24, salt, 0, saltlen);
// first try with full (128) bits
byte[] key = DeriveKey (salt, password);
// decrypt in place and try this
RC4 rc4 = RC4.Create ();
ICryptoTransform dec = rc4.CreateDecryptor (key, null);
dec.TransformBlock (keypair, 8, keypair.Length - 8, keypair, 8);
try {
rsa = CryptoConvert.FromCapiPrivateKeyBlob (keypair);
weak = false;
}
catch (CryptographicException) {
weak = true;
// second chance using weak crypto
Buffer.BlockCopy (pvk, 24 + saltlen, keypair, 0, keylen);
// truncate the key to 40 bits
Array.Clear (key, 5, 11);
// decrypt
RC4 rc4b = RC4.Create ();
dec = rc4b.CreateDecryptor (key, null);
dec.TransformBlock (keypair, 8, keypair.Length - 8, keypair, 8);
rsa = CryptoConvert.FromCapiPrivateKeyBlob (keypair);
}
Array.Clear (key, 0, key.Length);
}
else {
weak = true;
// read unencrypted keypair
rsa = CryptoConvert.FromCapiPrivateKeyBlob (keypair);
Array.Clear (keypair, 0, keypair.Length);
}
// zeroize pvk (which could contain the unencrypted private key)
Array.Clear (pvk, 0, pvk.Length);
return (rsa != null);
}
public void Save (string filename)
{
Save (filename, null);
}
public void Save (string filename, string password)
{
if (filename == null)
throw new ArgumentNullException ("filename");
byte[] blob = null;
FileStream fs = File.Open (filename, FileMode.Create, FileAccess.Write);
try {
// header
byte[] empty = new byte [4];
byte[] data = BitConverterLE.GetBytes (magic);
fs.Write (data, 0, 4); // magic
fs.Write (empty, 0, 4); // reserved
data = BitConverterLE.GetBytes (keyType);
fs.Write (data, 0, 4); // key type
encrypted = (password != null);
blob = CryptoConvert.ToCapiPrivateKeyBlob (rsa);
if (encrypted) {
data = BitConverterLE.GetBytes (1);
fs.Write (data, 0, 4); // encrypted
data = BitConverterLE.GetBytes (16);
fs.Write (data, 0, 4); // saltlen
data = BitConverterLE.GetBytes (blob.Length);
fs.Write (data, 0, 4); // keylen
byte[] salt = new byte [16];
RC4 rc4 = RC4.Create ();
byte[] key = null;
try {
// generate new salt (16 bytes)
RandomNumberGenerator rng = RandomNumberGenerator.Create ();
rng.GetBytes (salt);
fs.Write (salt, 0, salt.Length);
key = DeriveKey (salt, password);
if (Weak)
Array.Clear (key, 5, 11);
ICryptoTransform enc = rc4.CreateEncryptor (key, null);
// we don't encrypt the header part of the BLOB
enc.TransformBlock (blob, 8, blob.Length - 8, blob, 8);
}
finally {
Array.Clear (salt, 0, salt.Length);
Array.Clear (key, 0, key.Length);
rc4.Clear ();
}
}
else {
fs.Write (empty, 0, 4); // encrypted
fs.Write (empty, 0, 4); // saltlen
data = BitConverterLE.GetBytes (blob.Length);
fs.Write (data, 0, 4); // keylen
}
fs.Write (blob, 0, blob.Length);
}
finally {
// BLOB may include an uncrypted keypair
Array.Clear (blob, 0, blob.Length);
fs.Close ();
}
}
static public PrivateKey CreateFromFile (string filename)
{
return CreateFromFile (filename, null);
}
static public PrivateKey CreateFromFile (string filename, string password)
{
if (filename == null)
throw new ArgumentNullException ("filename");
byte[] pvk = null;
using (FileStream fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
pvk = new byte [fs.Length];
fs.Read (pvk, 0, pvk.Length);
fs.Close ();
}
return new PrivateKey (pvk, password);
}
}
}

View File

@@ -0,0 +1,129 @@
//
// SoftwarePublisherCertificate.cs
// - Software Publisher Certificates Implementation
//
// Author:
// Sebastien Pouliot <sebastien@ximian.com>
//
// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
// Copyright (C) 2004,2008 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.Collections;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Mono.Security;
using Mono.Security.X509;
namespace Mono.Security.Authenticode {
public class SoftwarePublisherCertificate {
private PKCS7.SignedData pkcs7;
public SoftwarePublisherCertificate ()
{
pkcs7 = new PKCS7.SignedData ();
pkcs7.ContentInfo.ContentType = PKCS7.Oid.data;
}
public SoftwarePublisherCertificate (byte[] data) : this ()
{
if (data == null)
throw new ArgumentNullException ("data");
PKCS7.ContentInfo ci = new PKCS7.ContentInfo (data);
if (ci.ContentType != PKCS7.Oid.signedData) {
throw new ArgumentException (
Locale.GetText ("Unsupported ContentType"));
}
pkcs7 = new PKCS7.SignedData (ci.Content);
}
public X509CertificateCollection Certificates {
get { return pkcs7.Certificates; }
}
public ArrayList Crls {
get { return pkcs7.Crls; }
}
public byte[] GetBytes ()
{
PKCS7.ContentInfo ci = new PKCS7.ContentInfo (PKCS7.Oid.signedData);
ci.Content.Add (pkcs7.ASN1);
return ci.GetBytes ();
}
static public SoftwarePublisherCertificate CreateFromFile (string filename)
{
if (filename == null)
throw new ArgumentNullException ("filename");
byte[] data = null;
using (FileStream fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
data = new byte [fs.Length];
fs.Read (data, 0, data.Length);
fs.Close ();
}
// It seems that VeriSign send the SPC file in Unicode
// (base64 encoded) and Windows accept them.
if (data.Length < 2)
return null;
if (data [0] != 0x30) {
// this isn't an ASN.1 SEQUENCE (so not legal), check for PEM/base64 encoding
try {
data = PEM (data);
}
catch (Exception ex) {
throw new CryptographicException ("Invalid encoding", ex);
}
}
#if DEBUG
using (FileStream fs = File.OpenWrite (filename + ".der")) {
fs.Write (data, 0, data.Length);
fs.Close ();
}
#endif
return new SoftwarePublisherCertificate (data);
}
const string header = "-----BEGIN PKCS7-----";
const string footer = "-----END PKCS7-----";
static byte[] PEM (byte[] data)
{
// this could be base64/unicode (e.g. VeriSign) otherwise default to ASCII
string pem = (data [1] == 0x00) ? Encoding.Unicode.GetString (data) : Encoding.ASCII.GetString (data);
int start = pem.IndexOf (header) + header.Length;
int end = pem.IndexOf (footer, start);
string base64 = ((start == -1) || (end == -1)) ? pem : pem.Substring (start, (end - start));
return Convert.FromBase64String (base64);
}
}
}