#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <wincrypt.h>

#define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)

void MyHandleError(char *s);

int main(int argc, char *argv[])
{
    HANDLE hFile;
    HCRYPTMSG hMsg;
    DWORD dwContentType;
    DWORD dwSignerInfo;
    DWORD cbEncoded;
    PCCERT_CONTEXT pSignerCert = NULL;
    BOOL fResult;
    DWORD dwEncoding, dwFormatType;
    HCERTSTORE hStoreHandle;
    HCRYPTPROV hCryptProv;
    CERT_INFO CertInfo;
    BOOL fCallerFreeProvOrNCryptKey;
    PCCERT_CONTEXT pCertContext;
    DWORD dwKeySpec;
    BOOL fResult2;
    DWORD cbData;
    PBYTE pbData;
    DWORD cbSize;
    DWORD dwCount;
    PCCERT_CONTEXT pCertContext2;
    BOOL fResult3;
    BOOL fResult4;
    BOOL fResult5;
    BOOL fResult6;
    BOOL fResult7;

    //-------------------------------------------------------------------
    // Begin processing.

    if(argc != 3)
    {
        printf("Usage: SignTool <file to sign> <file to copy to>\n");
        printf("Example: SignTool C:\\Test\\Test.exe C:\\Test\\Test2.exe\n");
        exit(1);
    }

    //-------------------------------------------------------------------
    // Open the message.

    hFile = CreateFile(argv[1],
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if(hFile == NULL)
    {
        MyHandleError("Could not open file!");
    }

    fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
        argv[1],
        CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
        CERT_QUERY_FORMAT_FLAG_BINARY,
        0,
        &dwContentType,
        &dwEncoding,
        &dwFormatType,
        &hStoreHandle,
        &hMsg,
        NULL);

    if(!fResult)
    {
        MyHandleError("Could not open file!");
    }

    //-------------------------------------------------------------------
    // Get signer information size.

    fResult = CryptMsgGetParam(hMsg,
        CMSG_SIGNER_INFO_PARAM,
        0,
        NULL,
        &cbEncoded);

    if(!fResult)
    {
        MyHandleError("CryptMsgGetParam failed.");
    }

    //-------------------------------------------------------------------
    // Allocate memory for signer information.

    pSignerCert = (PCERT_CONTEXT) malloc(cbEncoded);

    if(!pSignerCert)
    {
        MyHandleError("Memory allocation failed.");
    }

    //-------------------------------------------------------------------
    // Get Signer Information.

    fResult = CryptMsgGetParam(hMsg,
        CMSG_SIGNER_INFO_PARAM,
        0,
        (PVOID) pSignerCert,
        &cbEncoded);

    if(!fResult)
    {
        MyHandleError("CryptMsgGetParam failed.");
    }

    //-------------------------------------------------------------------
    // Get a handle to the certificate context.

    CertInfo.Issuer = pSignerCert->pCertInfo->Issuer;
    CertInfo.SerialNumber = pSignerCert->pCertInfo->SerialNumber;

    pCertContext = CertFindCertificateInStore(hStoreHandle,
        MY_ENCODING_TYPE,
        0,
        CERT_FIND_SUBJECT_CERT,
        (PVOID) &CertInfo,
        NULL);

    if(!pCertContext)
    {
        MyHandleError("CertFindCertificateInStore failed.");
    }

    //-------------------------------------------------------------------
    // Get a handle to the private key.

    fResult = CryptAcquireCertificatePrivateKey(pCertContext,
        0,
        NULL,
        &hCryptProv,
        &dwKeySpec,
        &fCallerFreeProvOrNCryptKey);

    if(!fResult)
    {
        MyHandleError("CryptAcquireCertificatePrivateKey failed.");
    }

    //-------------------------------------------------------------------
    // Get the size of the file's signature.

    fResult = CryptGetMessageSignerCount(dwEncoding,
        hMsg,
        &dwSignerInfo);

    if(!fResult)
    {
        MyHandleError("CryptGetMessageSignerCount failed.");
    }

    if(dwSignerInfo != 1)
    {
        MyHandleError("This file has more than one signature.");
    }

    //-------------------------------------------------------------------
    // Get the size of the signature.

    fResult = CryptGetMessageSignature(dwEncoding,
        hMsg,
        0,
        NULL,
        &cbData,
        NULL);

    if(!fResult)
    {
        MyHandleError("CryptGetMessageSignature failed.");
    }

    //-------------------------------------------------------------------
    // Allocate memory for the signature.

    pbData = (BYTE *) malloc(cbData);

    if(!pbData)
    {
        MyHandleError("Memory allocation failed.");
    }

    //-------------------------------------------------------------------
    // Get the signature.

    fResult = CryptGetMessageSignature(dwEncoding,
        hMsg,
        0,
        pbData,
        &cbData,
        NULL);

    if(!fResult)
    {
        MyHandleError("CryptGetMessageSignature failed.");
    }

    //-------------------------------------------------------------------
    // Get the size of the certificate store.

    fResult = CertGetStoreProperty(hStoreHandle,
        CERT_STORE_CERTIFICATE_CONTEXT_PROP_ID,
        NULL,
        &cbSize);

    if(!fResult)
    {
        MyHandleError("CertGetStoreProperty failed.");
    }

    //-------------------------------------------------------------------
    // Allocate memory for the certificate store.

    pCertContext2 = (PCCERT_CONTEXT) malloc(cbSize);

    if(!pCertContext2)
    {
        MyHandleError("Memory allocation failed.");
    }

    //-------------------------------------------------------------------
    // Get a pointer to the certificate store.

    fResult = CertGetStoreProperty(hStoreHandle,
        CERT_STORE_CERTIFICATE_CONTEXT_PROP_ID,
        pCertContext2,
        &cbSize);

    if(!fResult)
    {
        MyHandleError("CertGetStoreProperty failed.");
    }

    //-------------------------------------------------------------------
    // Count the number of certificates in the store.

    dwCount = 0;

    while(pCertContext2 != NULL)
    {
        dwCount++;
        pCertContext2 = CertEnumCertificatesInStore(hStoreHandle,
            pCertContext2);
    }

    //-------------------------------------------------------------------
    // Allocate memory for an array of pointers to the certificates.

    pCertContext2 = (PCCERT_CONTEXT *) malloc(sizeof(PCCERT_CONTEXT) *
        dwCount);

    if(!pCertContext2)
    {
        MyHandleError("Memory allocation failed.");
    }

    //-------------------------------------------------------------------
    // Get the first certificate context.

    pCertContext2[0] = CertEnumCertificatesInStore(hStoreHandle, NULL);

    if(!pCertContext2[0])
    {
        MyHandleError("CertEnumCertificatesInStore failed.");
    }

    //-------------------------------------------------------------------
    // Get the rest of the certificate contexts.

    for(dwCount = 1; dwCount < dwCount; dwCount++)
    {
        pCertContext2[dwCount] = CertEnumCertificatesInStore(hStoreHandle,
            pCertContext2[dwCount - 1]);

        if(!pCertContext2[dwCount])
        {
            MyHandleError("CertEnumCertificatesInStore failed.");
        }
    }

    //-------------------------------------------------------------------
    // Open the message to be signed.

    hFile = CreateFile(argv[2],
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if(hFile == NULL)
    {
        MyHandleError("Could not open file!");
    }

    //-------------------------------------------------------------------
    // Sign the message.

    fResult = CryptSignMessage(NULL,
        FALSE,
        dwCount,
        (const PCCERT_CONTEXT *) pCertContext2,
        pbData,
        &cbData);

    if(!fResult)
    {
        MyHandleError("CryptSignMessage failed.");
    }

    //-------------------------------------------------------------------
    // Write the signature to the file.

    fResult = WriteFile(hFile,
        pbData,
        cbData,
        &cbSize,
        NULL);

    if(!fResult)
    {
        MyHandleError("WriteFile failed.");
    }

    //-------------------------------------------------------------------
    // Clean up.

    fResult2 = CloseHandle(hFile);
    fResult3 = CertFreeCertificateContext(pCertContext);
    fResult4 = CertCloseStore(hStoreHandle, 0);
    fResult5 = CryptReleaseContext(hCryptProv, 0);
    fResult6 = CryptMsgClose(hMsg);
    fResult7 = CloseHandle(hFile);

    if(!fResult2 || !fResult3 || !fResult4 || !fResult5 || !fResult6 ||
        !fResult7)
    {
        MyHandleError("CloseHandle failed.");
    }

    printf("The file was signed.\n");
    return 0;
}

//-------------------------------------------------------------------
//  This example uses the function MyHandleError, a simple error
//  handling function, to print an error message to the   
//  standard error (stderr) file and exit the program. 
//  For most applications, replace this function with one 
//  that does more extensive error reporting.

void MyHandleError(char *s)
{
    fprintf(stderr,"An error occurred in running the program. \n");
    fprintf(stderr,"%s\n",s);
    fprintf(stderr, "Error number %x.\n", GetLastError());
    fprintf(stderr, "Program terminating. \n");
    exit(1);
}