如何使用CryptoAPI和SignerSign

时间:2015-08-25 17:43:30

标签: c++ windows winapi cryptoapi signtool

我正在尝试构建一个基于内核模式代码签名要求对大量文件进行大量签名的工具。我知道signtool可以通过/ ac参数为交叉签名信任获取额外的证书,但是无法使用SignerSign或SignerSignEx来弄清楚如何做同样的事情。我甚至窥探了signtool的API调用,并且镜像它们似乎不会产生同样的影响。

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

是否有关于如何完成此任务的文档或示例?

1 个答案:

答案 0 :(得分: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);