在C#中验证数字签名

3
我有一个已签名的"DLL"文件,我想在运行时("在我加载它之前")验证它的数字签名。
我在代码中嵌入了证书的公钥,有没有一种方法可以从数字签名中获取"消息摘要"?或者另一种验证文件未被篡改的方法?
我不想检查证书的"CA"和其他属性,因为恶意用户可以创建具有相同属性的证书。
*注意:我也不想使用SignTool :)

可以通过p/invoke和CryptoAPI非托管函数实现。您需要调用CryptQueryObject函数来获取PKCS#7对象,然后调用CryptGetMsgParam来提取原始签名。接下来使用.NETSignedCms来访问托管签名。 - Crypt32
嗨@CryptoGuy,你能给我一个关于那个的示例代码吗?谢谢 - Aviram Fireberger
1
可能是的。稍后。 - Crypt32
1个回答

5

有一些与CryptoAPI和p/invoke相关的工作(我不知道如何使用.NET提取authenticode签名)。这里是我在评论中所说的代码示例:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Cryptography.Pkcs;

namespace CLRSignatures {
    class Program {
        static void Main(string[] args) {
            IntPtr phCertStore = IntPtr.Zero;
            IntPtr phMsg = IntPtr.Zero;
            IntPtr ppvContext = IntPtr.Zero;
            int pdwMsgAndCertEncodingType = 0;
            int pdwContentType = 0;
            int pdwFormatType = 0;
            if (!Crypt32.CryptQueryObject(
                Wincrypt.CERT_QUERY_OBJECT_FILE,
                args[0],
                Wincrypt.CERT_QUERY_CONTENT_FLAG_ALL,
                Wincrypt.CERT_QUERY_FORMAT_FLAG_ALL,
                0,
                ref pdwMsgAndCertEncodingType,
                ref pdwContentType,
                ref pdwFormatType,
                ref phCertStore,
                ref phMsg,
                ref ppvContext
            )) {
                Console.WriteLine((new Win32Exception(Marshal.GetLastWin32Error())).Message);
                return;
            }
            int pcbData = 0;
            if (!Crypt32.CryptMsgGetParam(phMsg, Wincrypt.CMSG_ENCODED_MESSAGE, 0, null, ref pcbData)) {
                Console.WriteLine((new Win32Exception(Marshal.GetLastWin32Error())).Message);
                return;
            }
            byte[] pvData = new byte[pcbData];
            Crypt32.CryptMsgGetParam(phMsg, Wincrypt.CMSG_ENCODED_MESSAGE, 0, pvData, ref pcbData);
            var signedCms = new SignedCms();
            signedCms.Decode(pvData);
            try {
                signedCms.CheckSignature(false);
                Console.WriteLine("Signature check passed");
            } catch (Exception e) {
                Console.WriteLine(e.Message);
            } finally {
                Crypt32.CryptMsgClose(phMsg);
                Crypt32.CertCloseStore(phCertStore, 0);
            }
        }
    }
    static class Wincrypt {
        // source type
        public const int CERT_QUERY_OBJECT_FILE = 1;
        // object type
        const int CERT_QUERY_CONTENT_CERT = 1;
        const int CERT_QUERY_CONTENT_CTL = 2;
        const int CERT_QUERY_CONTENT_CRL = 3;
        const int CERT_QUERY_CONTENT_SERIALIZED_STORE = 4;
        const int CERT_QUERY_CONTENT_SERIALIZED_CERT = 5;
        const int CERT_QUERY_CONTENT_SERIALIZED_CTL = 6;
        const int CERT_QUERY_CONTENT_SERIALIZED_CRL = 7;
        const int CERT_QUERY_CONTENT_PKCS7_SIGNED = 8;
        const int CERT_QUERY_CONTENT_PKCS7_UNSIGNED = 9;
        const int CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED = 10;
        const int CERT_QUERY_CONTENT_PKCS10 = 11;
        const int CERT_QUERY_CONTENT_PFX = 12;
        const int CERT_QUERY_CONTENT_CERT_PAIR = 13;

        const int CERT_QUERY_CONTENT_FLAG_CERT = (1 << CERT_QUERY_CONTENT_CERT);
        const int CERT_QUERY_CONTENT_FLAG_CTL = (1 << CERT_QUERY_CONTENT_CTL);
        const int CERT_QUERY_CONTENT_FLAG_CRL = (1 << CERT_QUERY_CONTENT_CRL);
        const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE = (1 << CERT_QUERY_CONTENT_SERIALIZED_STORE);
        const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT = (1 << CERT_QUERY_CONTENT_SERIALIZED_CERT);
        const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL = (1 << CERT_QUERY_CONTENT_SERIALIZED_CTL);
        const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL = (1 << CERT_QUERY_CONTENT_SERIALIZED_CRL);
        const int CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED = (1 << CERT_QUERY_CONTENT_PKCS7_SIGNED);
        const int CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED = (1 << CERT_QUERY_CONTENT_PKCS7_UNSIGNED);
        const int CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED = (1 << CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED);
        const int CERT_QUERY_CONTENT_FLAG_PKCS10 = (1 << CERT_QUERY_CONTENT_PKCS10);
        const int CERT_QUERY_CONTENT_FLAG_PFX = (1 << CERT_QUERY_CONTENT_PFX);
        const int CERT_QUERY_CONTENT_FLAG_CERT_PAIR = (1 << CERT_QUERY_CONTENT_CERT_PAIR);
        public const int CERT_QUERY_CONTENT_FLAG_ALL =
            CERT_QUERY_CONTENT_FLAG_CERT |
            CERT_QUERY_CONTENT_FLAG_CTL |
            CERT_QUERY_CONTENT_FLAG_CRL |
            CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
            CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT |
            CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL |
            CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL |
            CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
            CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
            CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
            CERT_QUERY_CONTENT_FLAG_PKCS10 |
            CERT_QUERY_CONTENT_FLAG_PFX |
            CERT_QUERY_CONTENT_FLAG_CERT_PAIR;

        // format type
        const int CERT_QUERY_FORMAT_BINARY = 1;
        const int CERT_QUERY_FORMAT_BASE64_ENCODED = 2;
        const int CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED = 3;
        const int CERT_QUERY_FORMAT_FLAG_BINARY = 1 << CERT_QUERY_FORMAT_BINARY;
        const int CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED = 1 << CERT_QUERY_FORMAT_BASE64_ENCODED;
        const int CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED = 1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED;
        public const int CERT_QUERY_FORMAT_FLAG_ALL =
            CERT_QUERY_FORMAT_FLAG_BINARY |
            CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED |
            CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED;

        public const int CMSG_ENCODED_MESSAGE = 29;

    }
    static class Crypt32 {

        [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool CryptQueryObject(
            int dwObjectType,
            [MarshalAs(UnmanagedType.LPWStr)]
            string pvObject,
            int dwExpectedContentTypeFlags,
            int dwExpectedFormatTypeFlags,
            int dwFlags,
            ref int pdwMsgAndCertEncodingType,
            ref int pdwContentType,
            ref int pdwFormatType,
            ref IntPtr phCertStore,
            ref IntPtr phMsg,
            ref IntPtr ppvContext
        );
        [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool CryptMsgGetParam(
            IntPtr hCryptMsg,
            int dwParamType,
            int dwIndex,
            byte[] pvData,
            ref int pcbData
        );
        [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool CryptMsgClose(
            IntPtr hCryptMsg
        );
        [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool CertCloseStore(
            IntPtr hCertStore,
            int dwFlags
        );
    }
}

注意需要引用 System.Security 组件。 args[0] 接收到一个DLL文件的路径。


非常感谢!但是我想要例如,不是使用“signedCms.CheckSignature(false);”来进行检查,而是使用外部公钥/X509Certificate2进行检查。似乎使用X509Certificate2Collection的“CheckSignature”方法并不能达到要求... - Aviram Fireberger
您可以在SignerInfos属性中调用CheckSignature(false)来验证所需签名。该属性包含签名者信息的数组,其中包括公共证书。 - Crypt32
但我想使用外部证书进行检查, 该证书未包含在DLL中 但具有相同的公钥 - Aviram Fireberger
我建议使用附加到签名的证书,因为您的证书可能会产生不同的结果。 - Crypt32
4
你的说法是不正确的,因为私钥和公钥是严格关联的。一个特定的公钥只能有一个对应的私钥,反之亦然。如果恶意用户能够生成一个被同一公钥(与你的)验证的签名,那么该恶意用户就拥有了你的私钥。你被黑客攻击了。 - Crypt32
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接