Определение наличия у файла цифровой подписи в С# без фактической проверки подписи

Есть ли простой способ проверить, существует ли цифровая подпись в файле, не пытаясь проверить сертификат, которым он был подписан?

Я хочу подписать длинный список файлов exe и dll в каталоге, но только файлы, которые еще не были подписаны.

Например, если один из файлов был подписан Microsoft или какой-либо другой третьей стороной, я не хочу снова подписывать их сертификатом моей компании.

Легко проверить, имеет ли файл цифровую подпись или нет, щелкнув файл правой кнопкой мыши и просмотрев его свойства (если отображается вкладка цифровой подписи, значит, он был подписан). Я ищу простой способ проверить это свойство цифровой подписи с помощью С#.

Прямо сейчас я использую команду verify с signtool.exe, которая не только проверяет, существует ли цифровая подпись, но и выдает ли сертификат, используемый для подписи файла, доверенный центр.

Вот мой простой, но неуклюжий способ сделать это:

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:");
}

Обратите внимание, что я использую подробный флаг «/v» и проверяю, содержит ли вывод текст «Цепочка сертификатов подписи:» — просто потому, что я заметил, что эта строка всегда была в выводе файла, который был подписан — и был исключен из любого файла, который не был подписан.

В любом случае, несмотря на свою неуклюжесть, этот код, кажется, выполняет свою работу. Одна из причин, по которой я хотел бы изменить его, заключается в том, что для выполнения команды проверки файла требуется несколько сотен миллисекунд. Мне не нужно проверять сертификат, я просто пытаюсь проверить, существует ли в файле цифровая подпись.

Короче говоря, есть ли простой способ проверить, существует ли цифровая подпись, не пытаясь ее проверить?


person ksun    schedule 11.04.2013    source источник
comment
p.s. Вы можете подумать, что волноваться о нескольких сотнях миллисекунд слишком придирчиво, но это складывается, если вы проверяете тысячи файлов.   -  person ksun    schedule 11.04.2013
comment
Причина, по которой он медленный, вероятно, не в том, что вы проверяете подпись, а в том, что вы создаете новый процесс для каждого файла. Получите библиотеку цифровых подписей, встроенную в C#, например Bouncy Castle.   -  person Lie Ryan    schedule 11.04.2013


Ответы (3)


Я придумал довольно приличное решение, используя команду powershell «Get-AuthenticodeSignature». Я нашел этот сайт, на котором объясняется, как для использования команд powershell в С#. Таким образом, мне не нужно создавать несколько процессов, и это довольно быстро.

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);
            }
}
person ksun    schedule 16.04.2013
comment
Обязательно добавьте ссылку System.Management.Automation в свой проект. Он должен находиться здесь: C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0. - person ksun; 16.04.2013
comment
Отлично работает даже с PowerShell 3.0 API от NuGet. - person Carsten Schütte; 09.03.2015

Если вы хотите сделать это для сборок .NET, вы можете сделать это:

        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.
        }

В классе X509Certificate есть несколько статических методов, которые также могут быть полезны (например, CreateFromSignedFile), но я с ними не знаком. Это метод, который мы используем для двойной проверки того, что цифровая подпись была применена нашими внутренними инструментами.

person Mike Zboray    schedule 11.04.2013
comment
Это выглядит как отличное решение, но наши продукты используют смесь как сборок .NET, так и неуправляемых сборок, скомпилированных из C++. Пожалуйста, поправьте меня, если я ошибаюсь, но я считаю, что это решение не будет работать для неуправляемых сборок, скомпилированных из C++. - person ksun; 11.04.2013
comment
Это решение, которое мне нужно для моего приложения (все DLL-библиотеки C# с управляемым кодом). Но файлы также могут быть добавлены подписанными, если они могут содержать более 1 сертификата. Приведенный выше код обрабатывается только при наличии 1 сертификата. - person ; 25.10.2016

Во-первых, signtool работает только с PE-файлами. Для других типов файлов подпись может быть выполнена с использованием либо обертывающей подписи, либо встроенной подписи, либо отдельной подписи.

Оборачивающая подпись изменяет фактический файл, делая его нечитаемым для тех приложений, которые обычно используются для его чтения. Например. если вы создадите обертывающую подпись в PDF, ни один инструмент PDF не сможет прочитать файл, пока подпись не будет удалена.

Некоторые форматы файлов поддерживают встроенные подписи прямо в своем формате (например, файлы EXE и формат Authenticode). Там вы можете встроить подпись в файл, и другие приложения все равно смогут ее прочитать. Но это зависит от формата, и не многие форматы поддерживают это.

Окончательно отсоединенные подписи хранятся отдельно от файла. т.е. если у вас есть файл a.txt, вы создаете отдельную подпись и вставляете ее, например, в a.txt.pkcs7detached.

Как видите, отдельные подписи оптимальны для вашей задачи. И в этом случае у вас есть список файлов .pkcs7detached, чтобы вы могли проверить - если у файла нет соответствующего файла .pkcs7detached с подписью, вы создаете новую подпись.

Все вышеперечисленные типы подписей поддерживаются пакетом PKIBlackbox нашего продукта SecureBlackbox.

person Eugene Mayevski 'Callback    schedule 11.04.2013
comment
Спасибо Евгений. Я не упомянул в своем вопросе, что на самом деле меня интересует только подписание DLL и исполняемых файлов (не всех файлов, как я неправильно указал в своем вопросе). Это, безусловно, полезная информация, которую нужно знать, если мы когда-нибудь решим подписать файлы, которые не являются DLL или исполняемыми файлами. - person ksun; 11.04.2013
comment
@ksun В этом случае вы можете использовать наши компоненты Authenticode — они позволяют вам проверить, присутствует ли Authenticode в файле, без проверки подписи. Также необходимо отметить, что Authenticode в PE-файлах и строгое именование в управляемых сборках — это разные вещи, которые можно использовать вместе в управляемых сборках. - person Eugene Mayevski 'Callback; 11.04.2013
comment
Спасибо за совет. Я попытался найти Authenticode в MSDN. Похоже, может сработать команда ChkTrust? Я считаю, что это также проверяет действительность сертификата. Однако я нашел еще один пост на StackOverflow, в котором был простой способ проверить подпись аутентификации с помощью команды powershell. Get-AuthenticodeSignature В настоящее время мы не присваиваем строгое имя файлам. - person ksun; 12.04.2013