【问题标题】:How to Sign an EXE with Additional Certificates using CryptoAPI and SignerSign如何使用 CryptoAPI 和 SignerSign 使用附加证书签署 EXE
【发布时间】:2015-11-19 13:14:10
【问题描述】:

我正在尝试构建一个工具,该工具将根据内核模式代码签名要求对一堆文件进行批量签名。我知道 signtool 可以通过 /ac 参数获取额外的交叉签名信任证书,但无法弄清楚如何使用 SignerSign 或 SignerSignEx 来做同样的事情。我什至监视过 signtool 的 API 调用,并且镜像它们似乎不会产生相同的效果。

请注意,由于项目限制,signtool 或其他命令行实用程序不能用于此目的。

是否有任何文档或示例说明如何做到这一点?

【问题讨论】:

    标签: c++ windows winapi cryptoapi signtool


    【解决方案1】:

    好的,经过一段时间的研究,我终于弄清楚了如何使用交叉证书进行签名。首先,根据您的signtool 版本,您将需要四个或五个证书,这些证书嵌入在CERTIFICATE 资源类型下的signtool EXE 资源中,它们都以MS 开头。现在我让我的版本从文件中提取所有证书,因此以下示例伪代码将解释如何执行此操作。它不是纯粹的 CryptoAPI 调用,而是解释了 signtool 使用的基本过程。为简单起见,所有证书使用验证也被省略了。


    // inputs: string pfx_file_path, string cross_cert_file_path, string password, string file_to_sign
    
    Certificate_Store signer_collection = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL);
    Certificate_Store signer_store = CertOpenStore(CERT_STORE_PROV_MEMORY, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL,
        CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | CERT_STORE_READONLY_FLAG, NULL);
    
    File pfx_file = File::open(pifx_file_path, GENERIC_READ, OPEN_EXISTING);
    File_Mapping<BYTE> pfx_mapping = File_Mapping<BYTE>::map(pfx_file);
    
    CRYPT_DATA_BLOB pfx_blob = { pfx_mapping.size(), pfx_mapping.data() };
    Certificate_Store signer_pfx = PFXImportCertStore(&pfx_blob, password, CRYPT_USER_KEYSET);
    
    // CertEnumCertificatesInStore
    for (Certificate certificate: signer_pfx) {
        signer_store.add(certificate); // CertAddCertificateContextToStore
    }
    
    signer_collection.add(signer_store); // CertAddStoreToCollection
    
    Certificate signer;
    for (Certificate certificate: signer_collection) {
        // Assumes first certificate is the signer, need better validation.
        signer = CertDuplicateCertificateContext(certificate);
        break;
    }
    
    if (signer != NULL) {
        throw NoSginerException();
    }
    
    Certificate_Store additional_collection;
    if (cross_cert_file_path != NULL) {
        Certificate_Store cross_collection;
        Certificate_Store cross_store;
    
        Certificate cross_certificate = Certificate::load(cross_cert_file_path);
        // CryptQueryObject(CERT_QUERY_OBJECT_FILE, cross_cert_file_path, 
        //  CERT_QUERY_CONTENT_FLAG_CERT, CERT_QUERY_FORMAT_FLAG_ALL,
        //  0, NULL, NULL, NULL, NULL, NULL, &cross_certificate);
    
        cross_collection = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL);
        cross_collection.add(signer_collection);
    
        cross_store = CertOpenStore(CERT_STORE_PROV_MEMORY, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL,
            CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | CERT_STORE_READONLY_FLAG, NULL);
        cross_store.add(cross_certificate);
        cross_collection.add(cross_store);
    
        Certificate_Store ms_root_store = CertOpenStore(sz_CERT_STORE_PROV_MEMORY, 0, NULL,
            CERT_STORE_CREATE_NEW_FLAG | CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL);
        // This is where the embedded certificates from the MS Code Validation roots are collectioned.
        for (Resource resource: Program_Resources::resources_under("CERTIFICATE")) { // EnumResourceNames, Find/Load/Lock|Resource
            Certificate certificate = Certificate::from_blob(resource.size(), resource.data());
            // CERT_BLOB blob = { resource.size(), resources.data() };
            // CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, 
            //  CERT_QUERY_CONTENT_FLAG_CERT | CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT,
            //  CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, NULL, NULL, NULL, NULL,
            //  &certificate);
            ms_root_store.add(certificate);
        }
    
        cross_collection.add(certificate);
    
        static const DWORD CHAIN_FLAGS = CERT_CHAIN_DISABLE_PASS1_QUALITY_FILTERING |
            CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS |
            CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
        static const CERT_CHAIN_PARA CHAIN_PARAMS = { sizeof(CERT_CHAIN_PARA) };
        Certificate_Chain chain = Certificate_Chain::get(HCCE_LOCAL_MACHINE, signer, NULL,
            cross_collection, &CHAIN_PARAMS, CHAIN_FLAGS, NULL);
        // CertGetCertificateChain(HCCE_LOCAL_MACHINE, signer, NULL,
        //  cross_collection, &CHAIN_PARAMS, CHAIN_FLAGS, NULL, &chain);
    
        Certificate_Store additional_store = CertOpenStore(CERT_STORE_PROV_MEMORY,
            PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG |
            CERT_STORE_READONLY_FLAG, NULL);
        for (DWORD l = 0; l != chain->cLowerQualityChainContext; ++l) {
            PCCERT_CHAIN_CONTEXT low_chain = pChain->rgpLowerQualityChainContext[l];
            for (DWORD c = 0; c != low_chain->cChain; ++c) {
                PCERT_SIMPLE_CHAIN simple_chain = low_chain->rgpChain[c];
                for (DWORD e = 0; e != simple_chain->cElement; ++e) {
                    PCERT_CHAIN_ELEMENT element = simple_chain->rgpElement[e];
                    additional_store.add(element->pCertContext);
                }
            }
        }
    
        additional_collection = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL);
        additional_collection.add(additional_store);
    }
    
    SIGNER_FILE_INFO file_info = { sizeof(SIGNER_FILE_INFO) };
    file_info.pwszFileName = file_to_sign;
    
    DWORD index = 0;
    SIGNER_SUBJECT_INFO subject_info = { sizeof(SIGNER_SUBJECT_INFO) };
    subject_info.pdwIndex = &index;
    subject_info.dwSubjectChoice = SIGNER_SUBJECT_FILE;
    subject_info.pSignerFileInfo = &file_info;
    
    SIGNER_CERT_STORE_INFO store_info = { sizeof(SIGNER_CERT_STORE_INFO) };
    store_info.dwCertPolicy = SIGNER_CERT_POLICY_STORE;
    store_info.pSigningCert = signer;
    store_info.hCertStore = additional_collection;
    
    SIGNER_CERT cert_info = { sizeof(SIGNER_CERT) };
    cert_info.dwCertChoice = SIGNER_CERT_STORE;
    cert_info.pCertStoreInfo = &store_info;
    
    SIGNER_ATTR_AUTHCODE authcode_attr = { sizeof(SIGNER_ATTR_AUTHCODE) };
    
    SIGNER_SIGNATURE_INFO signature_info = { sizeof(SIGNER_SIGNATURE_INFO) };
    signature_info.algidHash = CALG_SHA;
    signature_info.dwAttrChoice = SIGNER_AUTHCODE_ATTR;
    signature_info.pAttrAuthcode = &authcode_attr;
    
    SIGNER_PROVIDER_INFO provider_info = { sizeof(SIGNER_PROVIDER_INFO) };
    provider_info.pwszProviderName = L"";
    provider_info.dwPvkChoice = PVK_TYPE_KEYCONTAINER;
    provider_info.pwszKeyContainer = L"";
    
    HRESULT hr = SignerSign(&subject_info, &cert_info, &signature_info, &provider_info, NULL, NULL, NULL);
    

    【讨论】:

    • 我希望我能在这方面为您提供帮助,因为我在为以前的雇主工作时曾这样做过。但由于我不知道更多,我不再有权访问该源代码。不过也不会太远了。
    • 已经知道如何做到这一点并发表了这篇文章codeproject.com/Articles/1271890/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多