Реализация на C # для получения SignedCms из подписанного файла - PullRequest
1 голос
/ 19 марта 2019

У меня есть реализация, использующая C ++ CRYPT32.DLL для извлечения объекта SignedCms из dll сборки c # со знаком.

signed c# assembly

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

CertificateChain

    private static readonly int CERT_QUERY_OBJECT_FILE = 0x00000001;
    private static readonly int CERT_QUERY_CONTENT_FLAG_ALL = 0x00003ffe;
    private static readonly int CERT_QUERY_FORMAT_FLAG_ALL = 0x0000000e;
    private static readonly int CMSG_ENCODED_MESSAGE = 29;

    [DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool CryptQueryObject(
        int dwObjectType,
        IntPtr pvObject,
        int dwExpectedContentTypeFlags,
        int dwExpectedFormatTypeFlags,
        int dwFlags,
        IntPtr pdwMsgAndCertEncodingType,
        IntPtr pdwContentType,
        IntPtr pdwFormatType,
        IntPtr phCertStore,
        IntPtr phMsg,
        IntPtr ppvContext
    );

    [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool CryptMsgGetParam(
        IntPtr hCryptMsg, int dwParamType, int dwIndex, IntPtr pvData, ref int pcbData );

    public static SignedCms GetSignedCmsFromFile(string fileName)
    {
        var pvObject = Marshal.StringToHGlobalUni(fileName);
        var phMessage = Marshal.AllocHGlobal(IntPtr.Size);
        var pvData = IntPtr.Zero;

        try
        {
            SignedCms signedCms = null;
            var success = CryptQueryObject(
                CERT_QUERY_OBJECT_FILE,
                pvObject,
                CERT_QUERY_CONTENT_FLAG_ALL,
                CERT_QUERY_FORMAT_FLAG_ALL,
                0,
                IntPtr.Zero,
                IntPtr.Zero,
                IntPtr.Zero,
                IntPtr.Zero,
                phMessage,
                IntPtr.Zero);

            if (success)
            {
                var hMessage = Marshal.ReadIntPtr(phMessage);
                var cbData = 0;
                success = CryptMsgGetParam(
                    hMessage,
                    CMSG_ENCODED_MESSAGE,
                    0,
                    IntPtr.Zero,
                    ref cbData);

                if (success)
                {
                    var data = new byte[cbData];
                    pvData = Marshal.AllocHGlobal(sizeof(byte) * data.Length);

                    success = CryptMsgGetParam(
                        hMessage,
                        CMSG_ENCODED_MESSAGE,
                        0,
                        pvData,
                        ref cbData);

                    if (success)
                    {
                        Marshal.Copy(pvData, data, 0, cbData);
                        signedCms = new SignedCms();

                        try
                        {
                            signedCms.Decode(data);
                            File.WriteAllBytes(fileName + ".export", data);
                        }
                        catch (CryptographicException)
                        {
                            signedCms = null;
                        }
                    }
                }
            }

            return signedCms;
        }
        finally
        {
            if (pvObject != IntPtr.Zero)
                Marshal.FreeHGlobal(pvObject);

            if (phMessage != IntPtr.Zero)
                Marshal.FreeHGlobal(phMessage);

            if (pvData != IntPtr.Zero)
                Marshal.FreeHGlobal(pvData);
        }
    }

Функция работает очень хорошо, но так как функция crypt32.dll устарела, я ищу чистую реализацию C #, которая делает то же самое и предоставляет объект SignedCms.

Самое близкое, что я смог найти, - сертификат X509.

X509Certificate2 cert = new X509Certificate2(fileName)

Функция CryptQueryObject


Решение

Благодаря посту ниже я смог реализовать чистый метод C # для извлечения SignedCms.

Единственное, что не подходит идеально, это то, что startindex выключен на 8 байтов, а размер на 10 байтов меньше.

    public static SignedCms GetSignedCmsFromFile(string fileName)
    {
        var result = new SignedCms();

        uint startIndex = 0;
        uint size = 0;

        var reader = new PeHeaderReader(fileName);
        if (reader.Is32BitHeader)
        {
            startIndex = reader.OptionalHeader32.CertificateTable.VirtualAddress;
            size = reader.OptionalHeader32.CertificateTable.Size;
        }
        else
        {
            startIndex = reader.OptionalHeader64.CertificateTable.VirtualAddress;
            size = reader.OptionalHeader64.CertificateTable.Size;
        }

        //var data = File.ReadAllBytes(fileName).Skip((int)startIndex).Take((int)size).ToArray();

        //Somehow the start index and size are not fitting perfectly and I have to adapt them
        var data = File.ReadAllBytes(fileName).Skip((int)startIndex + 8).Take((int)size - 10).ToArray();

        result.Decode(data);

        return result;
    }

1 Ответ

1 голос
/ 29 марта 2019

Я не нашел CNG-эквивалент вызова, но вот спецификация , которая должна позволить вам извлечь PKCS # 7, который является байтами для SignedCms:

Signature in executable

Спецификация гласит:

Сигнатуры Authenticode могут быть «встроены» в файл Windows PE в месте, указанном в записи таблицы сертификатов в разделе «Необязательно».Каталоги данных заголовка

Вот хорошие обзоры байтов в PE-заголовке: - https://resources.infosecinstitute.com/presenting-the-pe-header/ - https://www.red -gate.com / simple-talk / blogs / anatomy-of-a-net-assembly-pe-headers /

И почти все детали: https://blog.kowalczyk.info/articles/pefileformat.html

На github я нашел управляемый пример, который читает заголовок PE: https://gist.github.com/caioproiete/b51f29f74f5f5b2c59c39e47a8afc3a3

Все части вместе должны делать работу.

...