16

My .NET exe is signed using signtool. Using this code, I can verify the validity of the certificate itself:

var cert = X509Certificate.CreateFromSignedFile("application.exe");
var cert2 = new X509Certificate2(cert.Handle);
bool valid = cert2.Verify();

However, this only checks the certificate itself, and not the signature of the EXE. Therefore, if the EXE is tampered with, this method doesn't detect it.

How can I check the signature?

LTR
  • 1,226
  • 2
  • 17
  • 39
  • `X509Certificate.CreateFromSignedFile("").Verify()` ? – Sinatr Jun 05 '14 at 12:58
  • .CreateFromSignedFile returns an instance of X509Certificate, and that doesn't have a .Verify method. – LTR Jun 05 '14 at 13:14
  • Ok, then another stupid idea: construct `X509Certificate2` by using `X509Certificate`, not `Handle`. I am guessing: `Verify` method *should* check the file also or certificate will throw exception (when instantiating?) if containing exe-file is tampered. Perhaps you have to implement own validation [chain](http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509chain.aspx). – Sinatr Jun 05 '14 at 13:32
  • Checking the signature of the EXE by this EXE makes no sense - the one who removes the signature will remove the check as well. – Eugene Mayevski 'Callback Jun 05 '14 at 15:27
  • I posted a pure .Net solution [here](http://stackoverflow.com/a/34200959/1037208). – OSH Dec 10 '15 at 12:11

3 Answers3

14

You need to call (P/Invoke) WinVerifyTrust() function from wintrust.dll. There is (as far as I know) no alternative in managed .NET.

You can find documentation of this method here.

Someone already asked this question on SO. It was not accepted, but it should be correct (I only scrolled through). Take a look.

You could also take a look at this guide but they really do the same.

Community
  • 1
  • 1
pepo
  • 8,644
  • 2
  • 27
  • 42
  • Thank you! I thought if you can load the signature from the EXE and verify the chain using the X590Certificate class, there must be some way to check the file integrity as well. Will go with P/Invoke then. – LTR Jun 07 '14 at 16:01
  • Actually, if you are dealing with .NET assemblies then one way to verify the signature would be to load the assembly and let the CLR verify the signature for you. Unless explicitly disabled, the loading process will generate Publisher evidence if it finds valid signature - something like `var assembly = Assembly.LoadFile(); foreach (var evidence in assembly.Evidence) { if (evidence is Publisher) {//success break; } }` – Vivek Rathod Apr 16 '20 at 02:54
  • @VivekRathod I don't see `Assembly.Evidence` - are you using any extra NuGet packages, or on .NET 5 or something? – Cocowalla Jul 08 '20 at 17:07
  • @Cocowalla my code was for regular .NET (not core) But I just looked up and it seems .NET Core may allow similar via Evidence.GetAssemblyEvidence https://learn.microsoft.com/en-us/dotnet/api/system.security.policy.evidence.getassemblyevidence?view=dotnet-plat-ext-3.1 – Vivek Rathod Jul 09 '20 at 20:08
3

I searched github and found Azure Microsoft C# code that uses the PowerShell object to check for a valid Authenticode Signature.

    /// <summary>
    /// Check for Authenticode Signature
    /// </summary>
    /// <param name="providedFilePath"></param>
    /// <returns></returns>
    private bool VerifyAuthenticodeSignature(string providedFilePath)
    {
        bool isSigned = true;
        string fileName = Path.GetFileName(providedFilePath);
        string calculatedFullPath = Path.GetFullPath(providedFilePath);

        if (File.Exists(calculatedFullPath))
        {
            Log.LogMessage(string.Format("Verifying file '{0}'", calculatedFullPath));
            using (PowerShell ps = PowerShell.Create())
            {
                ps.AddCommand("Get-AuthenticodeSignature", true);
                ps.AddParameter("FilePath", calculatedFullPath);
                var cmdLetResults = ps.Invoke();

                foreach (PSObject result in cmdLetResults)
                {
                    Signature s = (Signature)result.BaseObject;
                    isSigned = s.Status.Equals(SignatureStatus.Valid);
                    if (isSigned == false)
                    {
                        ErrorList.Add(string.Format("!!!AuthenticodeSignature status is '{0}' for file '{1}' !!!", s.Status.ToString(), calculatedFullPath));
                    }
                    else
                    {
                        Log.LogMessage(string.Format("!!!AuthenticodeSignature status is '{0}' for file '{1}' !!!", s.Status.ToString(), calculatedFullPath));
                    }
                    break;
                }
            }
        }
        else
        {
            ErrorList.Add(string.Format("File '{0}' does not exist. Unable to verify AuthenticodeSignature", calculatedFullPath));
            isSigned = false;
        }

        return isSigned;
    }
Veener
  • 4,771
  • 2
  • 29
  • 37
-2

To validate the integrity of the signed .exe file, we can use StrongNameSignatureVerificationEx method:

[DllImport("mscoree.dll", CharSet = CharSet.Unicode)]
public static extern bool StrongNameSignatureVerificationEx(
        string wszFilePath, bool fForceVerification, ref bool pfWasVerified);    

var assembly = Assembly.GetExecutingAssembly();
bool pfWasVerified = false;
if (!StrongNameSignatureVerificationEx(assembly.Location, true, ref pfWasVerified))
{           
    // it's a patched .exe file!   
}

But it's not enough. It's possible to remove the signature and then apply/re-create it again! (there are a lot of tools to do that) In this case you need to store the public key of your signature somewhere (as a resource) and then compare it with the new/present public key. more info here

jww
  • 97,681
  • 90
  • 411
  • 885
VahidN
  • 18,457
  • 8
  • 73
  • 117