15

Is there a simple way to check if a digital signature exists on a file without trying to verify the certificate it was signed with?

I want to sign a long list of exe & dll files in a directory, but only files that haven't yet been signed.

For example, if one of the files has been signed by Microsoft or some other 3rd party I don't want to sign them again with my company's certificate.

It is easy to check if a file has a digital signature or not by right-clicking the file and viewing its properties (if the digital signature tab shows up then it has been signed). I'm searching for a simple way to check for this digital signature property using C#.

Right now, I am using the verify command with signtool.exe - which doesn't only check to see if a digital signature exists, but also whether the certificate used to sign the file was issued by a trusted authority.

Here is my simple, yet clunky approach for doing this:

private static Boolean AlreadySigned(string file)
{
    ProcessStartInfo startInfo = new ProcessStartInfo();
    startInfo.RedirectStandardOutput = true;
    startInfo.RedirectStandardError = true;
    startInfo.FileName = "signTool.exe";
    startInfo.Arguments = "verify /v " + file;
    startInfo.UseShellExecute = false;

    Process process = new Process();
    process.StartInfo = startInfo;
    process.EnableRaisingEvents = true;
    process.Start();
    process.WaitForExit();
    string output = process.StandardOutput.ReadToEnd();

    return output.Contains("Signing Certificate Chain:");
}

Notice that I'm using the verbose flag "/v" and checking if the output contains the text "Signing Certificate chain:" - just because I noticed that that string was always in the output of a file that had been signed - and was omitted from any file that hadn't been signed.

Anyway, despite its clumsiness, this code seems to get the job done. One reason I'd like to change it though is because it appears to take a few hundred milliseconds to execute the verify command on a file. I don't need to verify the certificate I'm just trying to see if a digital signature exists on the file.

So in short, is there a simple way to check if a digital signature exists - without trying to verify it?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
ksun
  • 1,369
  • 2
  • 13
  • 21
  • p.s. You may think that it's picky to worry about a few hundred milliseconds, but that adds up if you are checking thousands of files. – ksun Apr 11 '13 at 01:00
  • The reason it's slow is probably not because you're verifying the signature, the reason it's slow is because you're spawning a new process for every file. Get a digital signature library that is native to C#, for example Bouncy Castle. – Lie Ryan Apr 11 '13 at 01:16
  • Some low level background info on Authenticode: https://reversea.me/index.php/authenticode-i-understanding-windows-authenticode/ – Seva Alekseyev Apr 20 '23 at 14:45

3 Answers3

13

I came up with a pretty decent solution using the "Get-AuthenticodeSignature" powershell command. I found this site that explains how to use powershell commands in c#. This way I don't need to spawn several processes, and it's pretty quick.

using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;


private static Boolean alreadySigned(string file)
{            
            try
            {
                RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
                Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
                runspace.Open();

                Pipeline pipeline = runspace.CreatePipeline();
                pipeline.Commands.AddScript("Get-AuthenticodeSignature \"" + file + "\"");

                Collection<PSObject> results = pipeline.Invoke();
                runspace.Close();
                Signature signature = results[0].BaseObject as Signature;
                return signature == null ? false : (signature.Status != SignatureStatus.NotSigned);
            }
            catch (Exception e)
            {
                throw new Exception("Error when trying to check if file is signed:" + file + " --> " + e.Message);
            }
}
ksun
  • 1,369
  • 2
  • 13
  • 21
  • 1
    Make sure to add the System.Management.Automation reference to your project. It should be located here:C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0 – ksun Apr 16 '13 at 01:03
  • 1
    Works fine, even with PowerShell 3.0 API from NuGet – Carsten Schütte Mar 09 '15 at 18:07
12

If you want to do this for .NET assemblies, you can do this:

        string filePath = "C:/path/to/file.dll";
        Assembly assembly = Assembly.LoadFrom(filePath);
        Module module = assembly.GetModules().First();
        X509Certificate certificate = module.GetSignerCertificate();
        if (certificate == null)
        {
             // file is not signed.
        }

The X509Certificate class has some static methods that may also be of use (e.g. CreateFromSignedFile), but I am not as familiar with them. This is method we use to double check that a digital signature has been applied by our internal tools.

Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
  • This looks like a great solution, but our products use a mixture of both .NET assemblies and unmanaged assemblies compiled from C++. Please correct me if I'm mistaken, but I believe this solution won't work for the unmanaged assemblies that were compiled from C++. – ksun Apr 11 '13 at 17:22
  • This is the solution I needed for my app (all managed code C# DLLs). But files can be "append" signed as well where it can contain more than 1 certificate. Above code handles only when there is 1 certificate present. –  Oct 25 '16 at 13:13
2

First of all, signtool works only with PE files. For other file types signing can be done using either wrapping signature or embedded signature or detached signature.

Wrapping signature changes the actual file making it unreadable by those applications that are usually used to read it. Eg. if you create a wrapping signature on PDF, no PDF tool will be able to read the file until the signature is removed.

Some file formats support embedded signatures right in their format (EXE files and Authenticode format are the example). There you can embed the signature into the file and other applications are still able to read it. But this is format-specific and not many formats support this.

Finally detached signatures are kept separately from the file. I.e. if you have a file a.txt, you create a detached signature and put it into, for example, a.txt.pkcs7detached.

As you see, the detached signatures are optimal for your task. And in this case you have a list of .pkcs7detached files so you can check - if the file has no corresponding .pkcs7detached file with a signature, you create a new signature.

All of the above signature types are supported by PKIBlackbox package of our SecureBlackbox product.

Eugene Mayevski 'Callback
  • 45,135
  • 8
  • 71
  • 121
  • Thank you Eugene. I neglected to mention in my question that I'm actually only interested in signing DLL and Executable files (not all the files like I incorrectly stated in my question). This is definitely good information to know though if we ever decide to sign files that aren't dlls or executables. – ksun Apr 11 '13 at 16:43
  • @ksun In this case you can use our Authenticode components - they let you verify if Authenticode is present in the file without validating the signature. Also you need to note that Authenticode in PE files and strong-naming in managed assemblies are different things which can be used together in managed assemblies. – Eugene Mayevski 'Callback Apr 11 '13 at 17:12
  • Thanks for the tip. I tried looking up [Authenticode on MSDN](http://msdn.microsoft.com/en-us/library/ms537364(v=vs.85).aspx) It looks like maybe the ChkTrust command would work? I believe this also checks the validity of the certificate though. However, I found [another post](http://stackoverflow.com/questions/5555214/what-is-the-best-way-to-check-programatically-if-dll-exe-file-is-signed-with-aut) on StackOverflow that had an easy way to check authenticode signature with a powershell command. Get-AuthenticodeSignature We are not currently strong-naming the files. – ksun Apr 12 '13 at 00:07