How to Sign an EXE with Additional Certificates using CryptoAPI and SignerSign

主宰稳场 提交于 2019-12-04 17:13:44

Okay, after working on this for awhile, I've finally figured our how to do signing with a cross certificate. First, you will need four, or five depending on your version of signtool, certificates that are embedded in the signtool EXE's resources under the CERTIFICATE resource type, they all start with MS. Now I made my version pull all certificates from files so the following example pseudo code will explain how to do this. It's not purely the CryptoAPI calls, but does explain the basic process used by signtool. All certificate usage validation has also been left out for simplicity.


// 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);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!