The way I do it is :
Open the token
PCCERT_CONTEXT cert = OpenToken(SAFENET_TOKEN, EV_PASS);
Sign the file using the token, root / cross certificates when required and the EV certificate loaded into memory.
HRESULT hr = SignAppxPackage(cert, FILETOSIGN);
Using SignerSignEx2():
The file is signed using SignerSignEx2() which needs to be loaded into memory using LoadLibrary() and GetProcAddress():
// Type definition for invoking SignerSignEx2 via GetProcAddress
typedef HRESULT(WINAPI *SignerSignEx2Function)(
DWORD,
PSIGNER_SUBJECT_INFO,
PSIGNER_CERT,
PSIGNER_SIGNATURE_INFO,
PSIGNER_PROVIDER_INFO,
DWORD,
PCSTR,
PCWSTR,
PCRYPT_ATTRIBUTES,
PVOID,
PSIGNER_CONTEXT *,
PVOID,
PVOID);
// Load the SignerSignEx2 function from MSSign32.dll
HMODULE msSignModule = LoadLibraryEx(
L"MSSign32.dll",
NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32);
if (msSignModule)
{
SignerSignEx2Function SignerSignEx2 = reinterpret_cast<SignerSignEx2Function>(
GetProcAddress(msSignModule, "SignerSignEx2"));
if (SignerSignEx2)
{
hr = SignerSignEx2(
signerParams.dwFlags,
signerParams.pSubjectInfo,
signerParams.pSigningCert,
signerParams.pSignatureInfo,
signerParams.pProviderInfo,
signerParams.dwTimestampFlags,
signerParams.pszAlgorithmOid,
signerParams.pwszTimestampURL,
signerParams.pCryptAttrs,
signerParams.pSipData,
signerParams.pSignerContext,
signerParams.pCryptoPolicy,
signerParams.pReserved);
}
else
{
DWORD lastError = GetLastError();
hr = HRESULT_FROM_WIN32(lastError);
}
FreeLibrary(msSignModule);
}
else
{
DWORD lastError = GetLastError();
hr = HRESULT_FROM_WIN32(lastError);
}
// Free any state used during app package signing
if (sipClientData.pAppxSipState)
{
sipClientData.pAppxSipState->Release();
}
Time Stamping
Further, you must Time Stamp your signed file and do that using a Time Stamping authority to which you connect.
That is done by securely checking a Time Stamping server via URL for the current date and time. Each Signing authority have their own Time Stamping server. Time Stamping is an extra step in the Code Signing process, but when it comes to EV Code Signing it is a requirement which adds an additional layer of security to the signed PE.
For that reason, add to your code a check whether the user is connected to the Internet.
DWORD dwReturnedFlag;
if (InternetGetConnectedState(&dwReturnedFlag,0) == NULL) // use https://docs.microsoft.com/en-us/windows/desktop/api/netlistmgr/nf-netlistmgr-inetworklistmanager-getconnectivity
{
wprintf(L"Certificate can't be dated with no Internet connection\n");
return 1;
}
Loading a certificate from a file
std::tuple<DWORD, DWORD, std::string> GetCertificateFromFile
(const wchar_t* FileName
, std::shared_ptr<const CERT_CONTEXT>* ResultCert)
{
std::vector<unsigned char> vecAsn1CertBuffer;
auto tuple_result = ReadFileToVector(FileName, &vecAsn1CertBuffer);
if (std::get<0>(tuple_result) != 0)
{
return tuple_result;
}
return GetCertificateFromMemory(vecAsn1CertBuffer, ResultCert);
}
Loading a certificate into memory
std::tuple<DWORD, DWORD, std::string> GetCertificateFromMemory
(const std::vector<unsigned char>& CertData
, std::shared_ptr<const CERT_CONTEXT>* ResultCert)
{
const CERT_CONTEXT* crtResultCert = ::CertCreateCertificateContext
(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
, &CertData[0]
, static_cast<DWORD>(CertData.size()));
if (crtResultCert == NULL)
{
return std::make_tuple(E_FAIL
, ::GetLastError()
, "CertCreateCertificateContext");
}
*ResultCert = std::shared_ptr<const CERT_CONTEXT>(crtResultCert
, ::CertFreeCertificateContext);
return std::make_tuple(0, 0, "");
}
After the certificate has been loaded following accessing the hardware token we load it:
std::vector<unsigned char> dataCertEV(signingCertContext->pbCertEncoded,
signingCertContext->pbCertEncoded + signingCertContext->cbCertEncoded);
Finally, the signing is done in the following function:
HRESULT SignAppxPackage(
_In_ PCCERT_CONTEXT signingCertContext,
_In_ LPCWSTR packageFilePath)
{
HRESULT hr = S_OK;
if (PathFileExists(CertAuthority_ROOT))
{
wprintf(L"Cross Certificate '%s' was found\n", CertAuthority_ROOT);
}
else
{
wprintf(L"Error: Cross Certificate '%s' was not found\n", CertAuthority_ROOT);
return 3;
}
DWORD dwReturnedFlag;
if (InternetGetConnectedState(&dwReturnedFlag,0) == NULL)
{
wprintf(L"Certificate can't be dated with no Internet connection\n");
return 1;
}
if (PathFileExists(CertAuthority_RSA))
{
wprintf(L"Cross Certificate '%s' was found\n", CertAuthority_RSA);
}
else
{
wprintf(L"Error: Cross Certificate '%s' was not found\n", CertAuthority_RSA);
return 2;
}
if (PathFileExists(CROSSCERTPATH))
{
wprintf(L"Microsoft Cross Certificate '%s' was found\n", CROSSCERTPATH);
}
else
{
wprintf(L"Error: Microsoft Cross Certificate '%s' was not found\n", CROSSCERTPATH);
return 3;
}
// Initialize the parameters for SignerSignEx2
DWORD signerIndex = 0;
SIGNER_FILE_INFO fileInfo = {};
fileInfo.cbSize = sizeof(SIGNER_FILE_INFO);
fileInfo.pwszFileName = packageFilePath;
SIGNER_SUBJECT_INFO subjectInfo = {};
subjectInfo.cbSize = sizeof(SIGNER_SUBJECT_INFO);
subjectInfo.pdwIndex = &signerIndex;
subjectInfo.dwSubjectChoice = SIGNER_SUBJECT_FILE;
subjectInfo.pSignerFileInfo = &fileInfo;
SIGNER_CERT_STORE_INFO certStoreInfo = {};
certStoreInfo.cbSize = sizeof(SIGNER_CERT_STORE_INFO);
certStoreInfo.dwCertPolicy = SIGNER_CERT_POLICY_STORE;// SIGNER_CERT_POLICY_CHAIN_NO_ROOT;
certStoreInfo.pSigningCert = signingCertContext;
// Issuer: 'CertAuthority RSA Certification Authority'
// Subject 'CertAuthority RSA Extended Validation Code Signing CA'
auto fileCertAuthorityRsaEVCA = CertAuthority_RSA;
std::shared_ptr<const CERT_CONTEXT> certCertAuthorityRsaEVCA;
auto tuple_result = GetCertificateFromFile(fileCertAuthorityRsaEVCA, &certCertAuthorityRsaEVCA);
if (std::get<0>(tuple_result) != 0)
{
std::cout << "Error: " << std::get<0>(tuple_result) << " " << std::get<1>(tuple_result) << " " << std::get<2>(tuple_result) << "\n";
return std::get<0>(tuple_result);
}
std::shared_ptr<const CERT_CONTEXT> certCertEV;
std::vector<unsigned char> dataCertEV(signingCertContext->pbCertEncoded,
signingCertContext->pbCertEncoded + signingCertContext->cbCertEncoded);
tuple_result = GetCertificateFromMemory(dataCertEV, &certCertEV);
if (std::get<0>(tuple_result) != 0)
{
std::cout << "Error: " << std::get<0>(tuple_result) << " " << std::get<1>(tuple_result) << " " << std::get<2>(tuple_result) << "\n";
return std::get<0>(tuple_result);
}
// Issuer: 'Microsoft Code Verification Root'
// Subject: 'CertAuthority RSA Certification Authority'
auto fileCertCross = CertAuthority_ROOT;
std::shared_ptr<const CERT_CONTEXT> certCertCross;
tuple_result = GetCertificateFromFile(fileCertCross, &certCertCross);
if (std::get<0>(tuple_result) != 0)
{
std::cout << "Error: " << std::get<0>(tuple_result) << " " << std::get<1>(tuple_result) << " " << std::get<2>(tuple_result) << "\n";
return std::get<0>(tuple_result);
}
//certificate 1 Issuer : '<Certificate Provider> RSA Certification Authority'
// Subject : '<Certificate Provider> Extended Validation Code Signing CA'
//
//certificate 2 Issuer : '<Certificate Provider> Extended Validation Code Signing CA'
// Subject : '<Your company / entity name>'
//
//certificate 3 Issuer : 'Microsoft Code Verification Root'
// Subject : '<Certificate Provider> Certification Authority'
std::vector<std::shared_ptr<const CERT_CONTEXT> > certs;
certs.push_back(certCertAuthorityRsaEVCA);
certs.push_back(certCertEV);
certs.push_back(certCertCross);
std::shared_ptr<void> resultStore;
tuple_result = FormMemoryCertStore(certs, CERT_STORE_ADD_NEW, &resultStore);
if (std::get<0>(tuple_result) != 0)
{
std::cout << "Error: " << std::get<0>(tuple_result) << " " << std::get<1>(tuple_result) << " " << std::get<2>(tuple_result) << "\n";
return std::get<0>(tuple_result);
}
certStoreInfo.hCertStore = resultStore.get();
//--------------------------------------------------------------------
SIGNER_CERT cert = {};
cert.cbSize = sizeof(SIGNER_CERT);
cert.dwCertChoice = SIGNER_CERT_STORE;
cert.pCertStoreInfo = &certStoreInfo;
// The algidHash of the signature to be created must match the
// hash algorithm used to create the app package
SIGNER_SIGNATURE_INFO signatureInfo = {};
signatureInfo.cbSize = sizeof(SIGNER_SIGNATURE_INFO);
signatureInfo.algidHash = CALG_SHA_256;
signatureInfo.dwAttrChoice = SIGNER_NO_ATTR;
SIGNER_SIGN_EX2_PARAMS signerParams = {};
signerParams.pSubjectInfo = &subjectInfo;
signerParams.pSigningCert = &cert;
signerParams.pSignatureInfo = &signatureInfo;
signerParams.dwTimestampFlags = SIGNER_TIMESTAMP_RFC3161;
signerParams.pszAlgorithmOid = szOID_NIST_sha256;
//signerParams.dwTimestampFlags = SIGNER_TIMESTAMP_AUTHENTICODE;
//signerParams.pszAlgorithmOid = NULL;
signerParams.pwszTimestampURL = TIMESTAMPURL;
APPX_SIP_CLIENT_DATA sipClientData = {};
sipClientData.pSignerParams = &signerParams;
signerParams.pSipData = &sipClientData;
// Type definition for invoking SignerSignEx2 via GetProcAddress
typedef HRESULT(WINAPI *SignerSignEx2Function)(
DWORD,
PSIGNER_SUBJECT_INFO,
PSIGNER_CERT,
PSIGNER_SIGNATURE_INFO,
PSIGNER_PROVIDER_INFO,
DWORD,
PCSTR,
PCWSTR,
PCRYPT_ATTRIBUTES,
PVOID,
PSIGNER_CONTEXT *,
PVOID,
PVOID);
// Load the SignerSignEx2 function from MSSign32.dll
HMODULE msSignModule = LoadLibraryEx(
L"MSSign32.dll",
NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32);
if (msSignModule)
{
SignerSignEx2Function SignerSignEx2 = reinterpret_cast<SignerSignEx2Function>(
GetProcAddress(msSignModule, "SignerSignEx2"));
if (SignerSignEx2)
{
hr = SignerSignEx2(
signerParams.dwFlags,
signerParams.pSubjectInfo,
signerParams.pSigningCert,
signerParams.pSignatureInfo,
signerParams.pProviderInfo,
signerParams.dwTimestampFlags,
signerParams.pszAlgorithmOid,
signerParams.pwszTimestampURL,
signerParams.pCryptAttrs,
signerParams.pSipData,
signerParams.pSignerContext,
signerParams.pCryptoPolicy,
signerParams.pReserved);
}
else
{
DWORD lastError = GetLastError();
hr = HRESULT_FROM_WIN32(lastError);
}
FreeLibrary(msSignModule);
}
else
{
DWORD lastError = GetLastError();
hr = HRESULT_FROM_WIN32(lastError);
}
// Free any state used during app package signing
if (sipClientData.pAppxSipState)
{
sipClientData.pAppxSipState->Release();
}
return hr;
}
See this article I wrote.
signtool.exe
. With an out-of-date version (mine was from 2016) I got the error ``` Error information: "CryptExportPublicKeyInfoEx failed" (87/0x57) ``` You can get an up-to-date version by installing the Windows SDK. At least at the time of writing this, the version provided with the SDK supports using [method described by Austin Mor – Crown