Xamarin Public Jenkins (auto-signing) e79aa3c0ed Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
2016-08-03 10:59:49 +00:00

478 lines
12 KiB
C#

//
// 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;
case 32:
ha = SHA256.Create ();
hash = GetHash (ha);
break;
case 48:
ha = SHA384.Create ();
hash = GetHash (ha);
break;
case 64:
ha = SHA512.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;
case 32:
hashName = "SHA256";
break;
case 48:
hashName = "SHA384";
break;
case 64:
hashName = "SHA512";
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;
}
}
}