Decrypt my World

Cryptography, Security, Debugging and more!

SignerSignEx returns error 0x80070020

SignerSignEx returns error 0x80070020

  • Comments 6

Hi all,

A customer of mine used my code in How to sign EXE files with an Authenticode certificate (part 2) and followed my recommendations in SignerSignEx returns error 0x800b0003 to be able to sign all type of files (EXEs, DLLs, OCXs, MSIs, etc.) with SignerSignEx. This worked fine.

But when passing a valid time stamp server URL to the API's pwszHttpTimeStamp parameter in order to time stamp the signature, he got the following error when signing i.e. EXEs: 0x80070020 / -2147024864 / ERROR_SHARING_VIOLATION / "The process cannot access the file because it is being used by another process".

Note he didn't get the error when signing i.e. MSIs. Also note that after the API failed, the file was correctly signed as we could see with i.e. Explorer, but signature was not time stamped as expected.

Fortunatelly we could time stamp the signatures of all file types by doing the following: first, call SignerSignEx without passing time stamp server URL, so it signs all type of files without returning the error above. Immediately after that, call SignerTimeStampEx to time stamp the signature. The following sample shows how to achieve this:

#include "windows.h"
#include "Wincrypt.h"
#include "stdio.h"
#include "conio.h"


// STRUCTS

typedef struct _SIGNER_FILE_INFO {  
	DWORD cbSize;  
	LPCWSTR pwszFileName;  
	HANDLE hFile;
} SIGNER_FILE_INFO,  *PSIGNER_FILE_INFO;

typedef struct _SIGNER_BLOB_INFO {  
	DWORD cbSize;  
	GUID *pGuidSubject;  
	DWORD cbBlob;  
	BYTE *pbBlob;  
	LPCWSTR pwszDisplayName;
} SIGNER_BLOB_INFO,  *PSIGNER_BLOB_INFO;

typedef struct _SIGNER_SUBJECT_INFO {  
	DWORD cbSize;  
	DWORD *pdwIndex;  
	DWORD dwSubjectChoice;  
	union {    
		SIGNER_FILE_INFO *pSignerFileInfo;    
		SIGNER_BLOB_INFO *pSignerBlobInfo;  
	} ;
} SIGNER_SUBJECT_INFO,  *PSIGNER_SUBJECT_INFO;

typedef struct _SIGNER_CERT_STORE_INFO {  
	DWORD cbSize;  
	PCCERT_CONTEXT pSigningCert;  
	DWORD dwCertPolicy;  
	HCERTSTORE hCertStore;
} SIGNER_CERT_STORE_INFO,  *PSIGNER_CERT_STORE_INFO;

typedef struct _SIGNER_SPC_CHAIN_INFO {  
	DWORD cbSize;  
	LPCWSTR pwszSpcFile;  
	DWORD dwCertPolicy;  
	HCERTSTORE hCertStore;
} SIGNER_SPC_CHAIN_INFO,  *PSIGNER_SPC_CHAIN_INFO;

typedef struct _SIGNER_CERT {  
	DWORD cbSize;  
	DWORD dwCertChoice;  
	union {    
		LPCWSTR pwszSpcFile;    
		SIGNER_CERT_STORE_INFO *pCertStoreInfo;    
		SIGNER_SPC_CHAIN_INFO *pSpcChainInfo;  
	} ;  
	HWND hwnd;
} SIGNER_CERT,  *PSIGNER_CERT;

typedef struct _SIGNER_ATTR_AUTHCODE {  
	DWORD cbSize;  
	BOOL fCommercial;  
	BOOL fIndividual;  
	LPCWSTR pwszName;  
	LPCWSTR pwszInfo;
} SIGNER_ATTR_AUTHCODE,  *PSIGNER_ATTR_AUTHCODE;

typedef struct _SIGNER_SIGNATURE_INFO {  
	DWORD cbSize;  
	ALG_ID algidHash;  
	DWORD dwAttrChoice;  
	union {    
		SIGNER_ATTR_AUTHCODE *pAttrAuthcode;  
	} ;  
	PCRYPT_ATTRIBUTES psAuthenticated;  
	PCRYPT_ATTRIBUTES psUnauthenticated;
} SIGNER_SIGNATURE_INFO,  *PSIGNER_SIGNATURE_INFO;

typedef struct _SIGNER_PROVIDER_INFO {  
	DWORD cbSize;  
	LPCWSTR pwszProviderName;  
	DWORD dwProviderType;  
	DWORD dwKeySpec;  
	DWORD dwPvkChoice;  
	union {    
		LPWSTR pwszPvkFileName;    
		LPWSTR pwszKeyContainer;  
	} ;
} SIGNER_PROVIDER_INFO,  *PSIGNER_PROVIDER_INFO;

typedef struct _SIGNER_CONTEXT {  
	DWORD cbSize;  
	DWORD cbBlob;  
	BYTE *pbBlob;
} SIGNER_CONTEXT,  *PSIGNER_CONTEXT;


// EXPORTS 

typedef HRESULT (WINAPI* SignerFreeSignerContextType)(
  __in  SIGNER_CONTEXT *pSignerContext
);

typedef HRESULT (WINAPI *SignerSignExType)(
  __in      DWORD dwFlags,
  __in      SIGNER_SUBJECT_INFO *pSubjectInfo,
  __in      SIGNER_CERT *pSignerCert,
  __in      SIGNER_SIGNATURE_INFO *pSignatureInfo,
  __in_opt  SIGNER_PROVIDER_INFO *pProviderInfo,
  __in_opt  LPCWSTR pwszHttpTimeStamp,
  __in_opt  PCRYPT_ATTRIBUTES psRequest,
  __in_opt  LPVOID pSipData,
  __out     SIGNER_CONTEXT **ppSignerContext
);

typedef HRESULT (WINAPI *SignerTimeStampExType)(
  __reserved  DWORD dwFlags,
  __in        SIGNER_SUBJECT_INFO *pSubjectInfo,
  __in        LPCWSTR pwszHttpTimeStamp,
  __in        PCRYPT_ATTRIBUTES psRequest,
  __in        LPVOID pSipData,
  __out       SIGNER_CONTEXT **ppSignerContext 
);

// MAIN

void main()
{
	// PARAMETERS

	// File to sign
	LPCWSTR pwszFileName = L"C:\\PathToFile\\FileToSign.msi";

	// Signing Cert Subject
	LPCWSTR pwszCertSubject = L"My cert Subject";

	// VARIABLES
	HRESULT hResult = S_OK;
	BOOL bResult = TRUE;
	HMODULE hMssign32 = NULL;
	SignerSignExType pfSignerSignEx = NULL;
	SignerTimeStampExType pfSignerTimeStampEx = NULL;	
	SignerFreeSignerContextType pfSignerFreeSignerContext = NULL;
	HCERTSTORE hCertStore = NULL; 
	PCCERT_CONTEXT pCertContext = NULL;
	DWORD dwIndex = 0;
	SIGNER_FILE_INFO signerFileInfo;
	SIGNER_SUBJECT_INFO signerSubjectInfo;
	SIGNER_CERT_STORE_INFO signerCertStoreInfo;
	SIGNER_CERT signerCert;
	SIGNER_SIGNATURE_INFO signerSignatureInfo;
	SIGNER_CONTEXT * pSignerContext = NULL;

	// MAIN

	// Attach a debugger now!
	printf("<< Press any key to continue>>\n");
	_getch();

	// Load library containing SignerSignEx and SignerFreeSignerContext
	printf("LoadLibrary...");
	hMssign32 = LoadLibrary(L"Mssign32.dll");
	if (!hMssign32)
	{
		printf("Error #%d\n", GetLastError()); goto cleanup;
	}
	printf("Done!\n");

	// Get SignerSignEx function
	printf("GetProcAddress(SignerSignEx)...");
	pfSignerSignEx = (SignerSignExType) GetProcAddress(hMssign32, "SignerSignEx");
	if (!pfSignerSignEx)
	{
		printf("Error #%d\n", GetLastError()); goto cleanup;
	}
	printf("Done!\n");

	// Get SignerTimeStampEx function
	printf("GetProcAddress(SignerTimeStampEx)...");
	pfSignerTimeStampEx = (SignerTimeStampExType) GetProcAddress(hMssign32, "SignerTimeStampEx");
	if (!pfSignerTimeStampEx)
	{
		printf("Error #%d\n", GetLastError()); goto cleanup;
	}
	printf("Done!\n");

	// Get SignerFreeSignerContext function
	printf("GetProcAddress(SignerFreeSignerContext)...");
	pfSignerFreeSignerContext = (SignerFreeSignerContextType) GetProcAddress(hMssign32, "SignerFreeSignerContext");
	if (!pfSignerFreeSignerContext)
	{
		printf("Error #%d\n", GetLastError()); goto cleanup;
	}
	printf("Done!\n");

	// Open MY cert store
	printf("CertOpenStore...");
	hCertStore = CertOpenStore(
		CERT_STORE_PROV_SYSTEM, 
		0,
		NULL,
		CERT_SYSTEM_STORE_CURRENT_USER,
		L"MY"
	);                 
	if (!hCertStore)
	{
		printf("Error #%d\n", GetLastError()); goto cleanup;
	}
	printf("Done!\n");

	// Find signing cert in MY cert store
	printf("CertFindCertificateInStore...");
	pCertContext = CertFindCertificateInStore(
		hCertStore,
		X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
		0,
		CERT_FIND_SUBJECT_STR,
		(void *)pwszCertSubject,
		NULL
	);
	if (!pCertContext)
	{
		printf("Error #%d\n", GetLastError()); goto cleanup;
	}
	printf("Done!\n");

	// Prepare SIGNER_FILE_INFO struct
	signerFileInfo.cbSize = sizeof(SIGNER_FILE_INFO);
	signerFileInfo.pwszFileName = pwszFileName;
	signerFileInfo.hFile = NULL;

	// Prepare SIGNER_SUBJECT_INFO struct
	signerSubjectInfo.cbSize = sizeof(SIGNER_SUBJECT_INFO);
	dwIndex = 0;
	signerSubjectInfo.pdwIndex = &dwIndex;
	signerSubjectInfo.dwSubjectChoice = 1; // SIGNER_SUBJECT_FILE
	signerSubjectInfo.pSignerFileInfo = &signerFileInfo;

	// Prepare SIGNER_CERT_STORE_INFO struct
	signerCertStoreInfo.cbSize = sizeof(SIGNER_CERT_STORE_INFO);
	signerCertStoreInfo.pSigningCert = pCertContext;
	signerCertStoreInfo.dwCertPolicy = 2; // SIGNER_CERT_POLICY_CHAIN
	signerCertStoreInfo.hCertStore = NULL;

	// Prepare SIGNER_CERT struct
	signerCert.cbSize = sizeof(SIGNER_CERT);
	signerCert.dwCertChoice = 2; // SIGNER_CERT_STORE
	signerCert.pCertStoreInfo = &signerCertStoreInfo;
	signerCert.hwnd = NULL;

	// Prepare SIGNER_SIGNATURE_INFO struct
	signerSignatureInfo.cbSize = sizeof(SIGNER_SIGNATURE_INFO);
	signerSignatureInfo.algidHash = CALG_SHA1;
	signerSignatureInfo.dwAttrChoice = 0; // SIGNER_NO_ATTR
	signerSignatureInfo.pAttrAuthcode = NULL;
	signerSignatureInfo.psAuthenticated = NULL;
	signerSignatureInfo.psUnauthenticated = NULL;

	// Sign file with cert
	printf("SignerSignEx...");
	hResult = pfSignerSignEx(
		0,
		&signerSubjectInfo,
		&signerCert,
		&signerSignatureInfo,
		NULL,
		NULL,
		NULL,
		NULL,
		&pSignerContext
	);
	if (S_OK != hResult)
	{
		printf("Error #%d\n", hResult); goto cleanup;
	}
	printf("Done!\n");

	// Time stamp the signature
	printf("SignerTimeStampEx...");
	hResult = pfSignerTimeStampEx(
		0,
		&signerSubjectInfo,
		L"http://timestamp.globalsign.com/scripts/timstamp.dll",
		NULL,
		NULL,
		&pSignerContext
	);
	if (S_OK != hResult)
	{
		printf("Error #%d\n", hResult); goto cleanup;
	}
	printf("Done!\n");

	printf("\nSUCCESS!!!\n");

	// Clean up
cleanup:

	if (pSignerContext)
	{
		hResult = pfSignerFreeSignerContext(pSignerContext);
	}

	if (pCertContext)
	{
		bResult = CertFreeCertificateContext(pCertContext);
	}

	if (hCertStore)
	{
		bResult = CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG);
	}

	if (hMssign32)
	{
		bResult = FreeLibrary(hMssign32);
	}

	// Exit
	printf("<< Press any key to exit >>\n");
	_getch();
	return;
}

Note that this sample also deals with error 0x800b0003 commented at the beginning of this post.

Regards,

 

Alex (Alejandro Campos Magencio)

  • Great article! How do you go about adding the description and description url attributes (/d and /du options for signtool)?

  • Figured it out, in case anyone else is wondering:

    // Prepare the SIGNER_ATTR_AUTHCODE struct

    SIGNER_ATTR_AUTHCODE att;

    att.cbSize = sizeof(SIGNER_ATTR_AUTHCODE);

    att.fCommercial = true;

    att.fIndividual = false;

    att.pwszName = L"MY DESCRIPTION";

    att.pwszInfo = L"MY DESCRIPTION URL";

    // Prepare SIGNER_SIGNATURE_INFO struct

    SIGNER_SIGNATURE_INFO signerSignatureInfo;

    signerSignatureInfo.cbSize = sizeof(SIGNER_SIGNATURE_INFO);

    signerSignatureInfo.algidHash = CALG_SHA1;

    // Set the SIGNER_SIGNATURE_INFO object to have authenticode attributes

    signerSignatureInfo.dwAttrChoice = SIGNER_AUTHCODE_ATTR;

    // Set the pointer to the Authenticode Attribute struct to the address of 'att'

    signerSignatureInfo.pAttrAuthcode = &att;

    signerSignatureInfo.psAuthenticated = NULL;

    signerSignatureInfo.psUnauthenticated = NULL;

  • Great! Thx for sharing!

  • Alejandro, hi!

    I found following SignerSignEx behaviour trying to sign my DLL on Windows Server 2008 R2 x64:

    1) signerFileInfo.hFile = NULL, no timestamp - file is signed successfully without timestamp

    2) signerFileInfo.hFile = valid handle, no timestamp - file is signed successfully without timestamp

    3) signerFileInfo.hFile = NULL, valid timestamp - I get 0x80070020, but my file is not signed at all, instead of yours

    "Also note that after the API failed, the file was correctly signed as we could see with i.e. Explorer, but signature was not time stamped as expected"

    4) signerFileInfo.hFile = valid handle, valid timestamp - file is successfully signed and timestamped

    Also I succeeded to first sign file as in (1) and then timestamp it using SignerTimeStampEx.

    Another thing, MSDN says following about SIGNER_SUBJECT_INFO pdwIndex member "This member is reserved. It must be set to zero.", but you'll get 0x80070057 if zero it. So use

    dwIndex = 0;

    signerSubjectInfo.pdwIndex = &dwIndex;

    Regards, Andrew

  • Sorry, an error was in my code, (3) works as you mentioned - SignerSignEx returns 0x80070020 but the file is signed without timestamp.

    Regards, Andrew

  • Hello again.

    I used Geoff's sample and discovered that specifying

          att.fCommercial = true;

    leads to 0x80028ca0 be returned from SignerSignEx call. Instead you must set fCommercial to false.

    Regards, Andrew

Page 1 of 1 (6 items)
Leave a Comment
  • Please add 8 and 4 and type the answer here:
  • Post
Translate This Page