How to sign EXE files with an Authenticode certificate (part 2)
Hi all, welcome back,
The other day a customer of mine was having an issue with SignTool.exe when signing an EXE file. The EXE file was getting corrupted/unusable after signing it.
When troubleshooting this issue, I had the chance to play a bit more with SignTool and check what it does behind the scenes.
Note: I already talked a bit about signing EXEs in post How to sign EXE files with an Authenticode certificate (VB.NET) . This new post will add more details and samples.
SignTool.exe uses CAPICOM.SignedCode class and its Sign method to do the signing.
The following VBScript shows how we may use CAPICOM to do the signing programmatically:
Option Explicit
Dim szCertName, szExeToSign
szCertName = "My cert Subject"
szExeToSign = "MyApplication.exe"
Const CAPICOM_CURRENT_USER_STORE = 2
Const CAPICOM_STORE_OPEN_READ_ONLY = 0
Const CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME = 1
Const CAPICOM_AUTHENTICATED_ATTRIBUTE_DOCUMENT_DESCRIPTION = 2
' Get certificate for signing
'
Dim objStore
Set objStore = WScript.CreateObject("CAPICOM.Store")
objStore.Open CAPICOM_CURRENT_USER_STORE, "My", CAPICOM_STORE_OPEN_READ_ONLY
Dim objSigningCert
Set objSigningCert = objStore.Certificates.Find(CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME, szCertName).Item(1)
' Create a signer for the code
'
Dim objSigner
Set objSigner = WScript.CreateObject("CAPICOM.Signer")
objSigner.Certificate = objSigningCert
' Sign the file
'
Dim objSignedCode
Set objSignedCode = WScript.CreateObject("CAPICOM.SignedCode")
objSignedCode.FileName = szExeToSign
objSignedCode.Sign objSigner
objSignedCode.TimeStamp "https://timestamp.globalsign.com/scripts/timstamp.dll"
WScript.Echo "Done!"
Note that this sample also shows how to time stamp a signature programmatically.
This sample was also reproducing my customer's issue. So I checked what CAPICOM does behind the scenes to further troubleshoot the issue.
CAPICOM.SignedCode.Sign uses CryptUIWizDigitalSign API to do the signing.
The following VB.NET sample uses CryptUIWizDigitalSign through P/Invoke to do the signing programmatically:
<SAMPLE file="Crypto.vb">
Imports System.Runtime.InteropServices
Imports System.Security.Cryptography
Imports System.ComponentModel
Imports System.Windows.Forms
Public Class Crypto
' #define CRYPTUI_WIZ_NO_UI 1
Public Const CRYPTUI_WIZ_NO_UI As Int32 = 1
' #define CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE 0x01
Public Const CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE As Int32 = 1
' #define CRYPTUI_WIZ_DIGITAL_SIGN_CERT 0x01
Public Const CRYPTUI_WIZ_DIGITAL_SIGN_CERT As Int32 = 1
' typedef struct _CRYPTUI_WIZ_DIGITAL_SIGN_INFO {
' DWORD dwSize;
' DWORD dwSubjectChoice;
' union {
' LPCWSTR pwszFileName;
' PCCRYPTUI_WIZ_DIGITAL_SIGN_BLOB_INFO pSignBlobInfo;
' };
' DWORD dwSigningCertChoice;
' union {
' PCCERT_CONTEXT pSigningCertContext;
' PCCRYPTUI_WIZ_DIGITAL_SIGN_STORE_INFO pSigningCertStore;
' PCCRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO pSigningCertPvkInfo;
' };
' LPCWSTR pwszTimestampURL;
' DWORD dwAdditionalCertChoice;
' PCCRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO pSignExtInfo;
' } CRYPTUI_WIZ_DIGITAL_SIGN_INFO;
<StructLayout(LayoutKind.Sequential)> _
Public Structure CRYPTUI_WIZ_DIGITAL_SIGN_INFO
Public dwSize As Int32
Public dwSubjectChoice As Int32
<MarshalAs(UnmanagedType.LPWStr)> Public pwszFileName As String
Public dwSigningCertChoice As Int32
Public pSigningCertContext As IntPtr
Public pwszTimestampURL As String
Public dwAdditionalCertChoice As Int32
Public pSignExtInfo As IntPtr
End Structure
' typedef struct _CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT {
' DWORD dwSize;
' DWORD cbBlob;
' BYTE* pbBlob;
' } CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT;
<StructLayout(LayoutKind.Sequential)> _
Public Structure CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT
Public dwSize As Int32
Public cbBlob As Int32
Public pbBlob As IntPtr
End Structure
' BOOL WINAPI CryptUIWizDigitalSign(
' DWORD dwFlags,
' HWND hwndParent,
' LPCWSTR pwszWizardTitle,
' PCCRYPTUI_WIZ_DIGITAL_SIGN_INFO pDigitalSignInfo,
' PCCRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT* ppSignContext
' );
<DllImport("Cryptui.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> _
Public Shared Function CryptUIWizDigitalSign( _
ByVal dwFlags As Int32, _
ByVal hwndParent As IntPtr, _
ByVal pwszWizardTitle As String, _
ByRef pDigitalSignInfo As CRYPTUI_WIZ_DIGITAL_SIGN_INFO, _
ByRef ppSignContext As IntPtr _
) As Boolean
End Function
' BOOL WINAPI CryptUIWizFreeDigitalSignContext(
' PCCRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT pSignContext
' );
<DllImport("Cryptui.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Public Shared Function CryptUIWizFreeDigitalSignContext( _
ByVal pSignContext As IntPtr _
) As Boolean
End Function
End Class
</SAMPLE>
<SAMPLE file="Module1.vb">
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports SignExe.Crypto
Imports System.Security.Cryptography.X509Certificates
Imports System.IO
Module Module1
Sub Main()
' Parameters
Dim certPath As String = "MyCert.pfx"
Dim exePath As String = "MyApplication.exe"
Dim sigPath As String = "signature.sig"
' Variables
'
Dim cert As X509Certificate2
Dim digitalSignInfo As CRYPTUI_WIZ_DIGITAL_SIGN_INFO
Dim pSignContext As IntPtr
Dim pSigningCertContext As IntPtr
Dim signContext As CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT
Dim fileOut As FileStream
Dim binWriter As BinaryWriter
Dim blob() As Byte
Try
' Get certificate context
'
cert = New X509Certificate2(certPath, "")
pSigningCertContext = cert.Handle
' Prepare signing info: exe and cert
'
digitalSignInfo = New CRYPTUI_WIZ_DIGITAL_SIGN_INFO
digitalSignInfo.dwSize = Marshal.SizeOf(digitalSignInfo)
digitalSignInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE
digitalSignInfo.pwszFileName = exePath
digitalSignInfo.dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_CERT
digitalSignInfo.pSigningCertContext = pSigningCertContext
digitalSignInfo.pwszTimestampURL = vbNullString
digitalSignInfo.dwAdditionalCertChoice = 0
digitalSignInfo.pSignExtInfo = IntPtr.Zero
' Sign exe
'
If (Not CryptUIWizDigitalSign( _
CRYPTUI_WIZ_NO_UI, _
IntPtr.Zero, _
vbNullString, _
digitalSignInfo, _
pSignContext _
)) Then
Throw New Win32Exception(Marshal.GetLastWin32Error(), "CryptUIWizDigitalSign")
End If
' Get the blob with the signature
'
signContext = Marshal.PtrToStructure(pSignContext, GetType(CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT))
blob = New Byte(signContext.cbBlob) {}
Marshal.Copy(signContext.pbBlob, blob, 0, signContext.cbBlob)
' Store the signature in a new file
'
fileOut = File.Open(sigPath, FileMode.Create)
binWriter = New BinaryWriter(fileOut)
binWriter.Write(blob)
binWriter.Close()
fileOut.Close()
' Free blob
'
If (Not CryptUIWizFreeDigitalSignContext(pSignContext)) Then
Throw New Win32Exception(Marshal.GetLastWin32Error(), "CryptUIWizFreeDigitalSignContext")
End If
' We are done
Console.WriteLine("Done!!!")
Catch ex As Win32Exception
' Any expected errors?
'
Console.WriteLine(ex.Message + " error#" + ex.NativeErrorCode.ToString)
Catch ex As Exception
' Any unexpected errors?
'
Console.WriteLine(ex.Message)
End Try
' We are done
'
Console.WriteLine("<< Press any key to continue >>")
Console.ReadKey()
End Sub
End Module
</SAMPLE>
This sample was also reproducing the issue. So I checked what CryptUIWizDigitalSign does behind the scenes.
CryptUIWizDigitalSign API uses SignerSignEx API to do the signing.
The following VC++ sample uses SignerSignEx API to do the signing programmatically:
#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
);
// MAIN
void main()
{
// PARAMETERS
// File to sign
LPCWSTR pwszFileName = L"C:\\TEST\\MyApplication.exe";
// Signing Cert Subject
LPCWSTR pwszCertSubject = L"My cert Subject";
// VARIABLES
HRESULT hResult = S_OK;
BOOL bResult = TRUE;
HMODULE hMssign32 = NULL;
SignerSignExType pfSignerSignEx = NULL;
SignerFreeSignerContextType pfSignerFreeSignerContext = NULL;
HANDLE hFile = 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 SignerFreeSignerContext function
printf("GetProcAddress(SignerFreeSignerContext)...");
pfSignerFreeSignerContext = (SignerFreeSignerContextType) GetProcAddress(hMssign32, "SignerFreeSignerContext");
if (!pfSignerFreeSignerContext)
{
printf("Error #%d\n", GetLastError()); goto cleanup;
}
printf("Done!\n");
// Open file to sign
printf("CreateFile...");
hFile = CreateFile(
pwszFileName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (!hFile)
{
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 = hFile;
// 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");
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 (hFile)
{
bResult = CloseHandle(hFile);
}
if (hMssign32)
{
bResult = FreeLibrary(hMssign32);
}
// Exit
printf("<< Press any key to exit >>\n");
_getch();
return;
}
This sample was also reproducing the issue.
Finally we saw that the issue was not in SignTool.exe / CAPICOM.SignCode.Sign / CryptUIWizDigitalSign / SignerSignEx, but in the EXE itself!
I run Visual Studio's Dumpbin.exe with its "/HEADER" parameter to list the problematic EXE's PE header information before and after signing it (the Microsoft Portable Executable and Common Object File Format Specification gives detailed information about PE header information).
Before signing, I could see the following Optional Header Value:
1400 [ C40] RVA [size] of Certificates Directory
After signing, I could see the following value:
1400 [ 1650] RVA [size] of Certificates Directory
Certificates Directory points to the location of the code signing signature in the binary. So an EXE which has not been signed should contain the following values:
0 [ 0] RVA [size] of Certificates Directory
Which was not our case. So SignerSignEx was not corrupting the EXE when signing it. It was already corrupted before that, even if it was successfully running before signing!
Just for testing, I opened the problematic EXE with a binary editor before signing it. I looked for "00 14 00 00 04 0c 00 00" bytes (correspondent to "1400 [ C40] RVA [size] of Certificates Directory" values), changed them to "00 00 00 00 00 00 00 00" and signed the EXE. It worked! Now the EXE is not corrupted and I can see the Digital Signature in Explorer, the certificate I used to sign, launch the EXE and run it as expected.
Manually modifying the PE header is not supported by Microsoft. If you face a similar issue, you should work with the team that developed the EXE and focus on why the EXE got generated with an invalid PE header in the first place.
I looked on the Internet and found that there are some third-party tools which may help us to see and modify the PE header if needed for testing purposes, for example PEInfo 0.9 BETA which I haven't tried so I can't neither recommend nor discourage its use.
I hope this helps.
Regards,
Alex (Alejandro Campos Magencio)
Comments
- Anonymous
January 23, 2009
Hey great article, I spent all day googling for some information on this subject, now I found working code and all :)Big thanks. - Anonymous
June 05, 2009
I've made a Delphi port of your C++ code that work just fine :)But, I'd like to add Authenticated attributes, and then I just crash my code :(I do something like this :signerSignatureInfo.psAuthenticated = &Attributes;Attributes.pszObjId = '1.3.6.1.4.1.311.2.1.12';Attributes.cValue = 2;Attributes.rgBalue = &Values;Values[0].cbData = strlen(Desc);Values[0].pbData = &Desc;Values[1].cbData = strlen(DescURL);Values[1].pbData = &DescURL;Any idea ?Best regards - Anonymous
June 07, 2009
Don't know Delphi, sorry... - Anonymous
July 02, 2009
use this code to sign a exe file is ok.but how to sign a sys file use cross-certificate ?thanks - Anonymous
January 18, 2011
This is great stuff, and really helped me out after 3 days wasted wading through Capicom, etc.I had one problem deploying this to a 2008 Server but found a fix here: social.msdn.microsoft.com/.../f1981374-5a25-45c7-aea8-aad859800ae7Apparently, creating and loading the certificate like this:X509Certificate2 cert = new X509Certificate2(certBytes, string.Empty, X509KeyStorageFlags.MachineKeySet);gets rid of the strange "network password" exceptions, which apparently have nothing to do with the password used. - Anonymous
November 11, 2011
The comment has been removed - Anonymous
November 13, 2011
Thx for your comment JRV! You are right, you have to run the script that way on x64 systems, and the reason for that is that there is no x64 version of CAPICOM.dll:x64 version of CAPICOM?blogs.msdn.com/.../x64-version-of-capicom.aspxRegards,Alex - Anonymous
August 27, 2012
The comment has been removed - Anonymous
June 18, 2014
when i try to use signerSignEx to sign BLOB instead of a pe file, it does not work, return 0x80070057( E_INVALIDARG), as we know, it can sign pe file well. what's the correct param i should to config, is any one make it work?here are my problem code:does signerSignEx support signing any user memory block?when i try to use signerSignEx to sign BLOB instead of a pe file, it does not work, return 0x80070057( E_INVALIDARG), but it can sign pe file well. what's the correct param i should to config, any one make it work?here are my problem code:SIGNER_BLOB_INFO signerBlobInfo;signerBlobInfo.cbSize = sizeof(SIGNER_BLOB_INFO); signerBlobInfo.pGuidSubject = NULL;//? i dont know which sip guid i should set.signerBlobInfo.cbBlob = blob.cbSize;signerBlobInfo.pbBlob = blob.pBlobData;//signerBlobInfo.pwszDisplayName = NULL;//param 2-----------------------------// Prepare SIGNER_SUBJECT_INFO structsignerSubjectInfo.cbSize = sizeof(SIGNER_SUBJECT_INFO);dwIndex = 0;signerSubjectInfo.pdwIndex = &dwIndex;signerSubjectInfo.dwSubjectChoice = 2; // SIGNER_SUBJECT_BLOBsignerSubjectInfo.pSignerBlobInfo = &signerBlobInfo;// Prepare SIGNER_CERT_STORE_INFO structsignerCertStoreInfo.cbSize = sizeof(SIGNER_CERT_STORE_INFO);signerCertStoreInfo.pSigningCert = pCertContext;signerCertStoreInfo.dwCertPolicy = 2; // SIGNER_CERT_POLICY_CHAINsignerCertStoreInfo.hCertStore = NULL;//param 3-----------------------------// Prepare SIGNER_CERT structsignerCert.cbSize = sizeof(SIGNER_CERT);signerCert.dwCertChoice = 2; // SIGNER_CERT_STOREsignerCert.pCertStoreInfo = &signerCertStoreInfo;signerCert.hwnd = NULL;//param 4-----------------------------// Prepare SIGNER_SIGNATURE_INFO structsignerSignatureInfo.cbSize = sizeof(SIGNER_SIGNATURE_INFO);signerSignatureInfo.algidHash = CALG_SHA1;signerSignatureInfo.dwAttrChoice = 0; // SIGNER_NO_ATTR:The signature does not have Authenticode attributes.signerSignatureInfo.pAttrAuthcode = NULL;signerSignatureInfo.psAuthenticated = NULL;signerSignatureInfo.psUnauthenticated = NULL;// Sign blob with certhResult = pfSignerSignEx( //error return: 0x80070057( E_INVALIDARG0,&signerSubjectInfo,&signerCert,&signerSignatureInfo,NULL,NULL,NULL,NULL,&pSignerContext);