diff --git a/src/Common.h b/src/Common.h index 1102ea0c..ce610f28 100644 --- a/src/Common.h +++ b/src/Common.h @@ -18,6 +18,43 @@ #include +class ScopedCOMInit final // never use this in DllMain +{ +public: + ScopedCOMInit() { + HRESULT hr = ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); // attempt STA init 1st (older CoInitialize(NULL)) + + if (hr == RPC_E_CHANGED_MODE) + { + hr = ::CoInitializeEx(nullptr, COINIT_MULTITHREADED); // STA init failed, switch to MTA + } + + if (SUCCEEDED(hr)) + { + // S_OK or S_FALSE, both needs subsequent CoUninitialize() + _bInitialized = true; + } + } + + ~ScopedCOMInit() { + if (_bInitialized) + { + _bInitialized = false; + ::CoUninitialize(); + } + } + + bool isInitialized() const { + return _bInitialized; + } + +private: + bool _bInitialized = false; + + ScopedCOMInit(const ScopedCOMInit&) = delete; + ScopedCOMInit& operator=(const ScopedCOMInit&) = delete; +}; + void expandEnv(std::wstring& s); std::wstring getDateTimeStrFrom(const std::wstring& dateTimeFormat, const SYSTEMTIME& st); void writeLog(const wchar_t* logFileName, const wchar_t* logSuffix, const wchar_t* log2write); @@ -27,4 +64,4 @@ std::string getFileContentA(const char* file2read); std::wstring GetLastErrorAsString(DWORD errorCode); std::wstring stringToUpper(std::wstring strToConvert); std::wstring stringToLower(std::wstring strToConvert); -std::wstring stringReplace(std::wstring subject, const std::wstring& search, const std::wstring& replace); \ No newline at end of file +std::wstring stringReplace(std::wstring subject, const std::wstring& search, const std::wstring& replace); diff --git a/src/verifySignedfile.cpp b/src/verifySignedfile.cpp index 56c2d66a..5b4293fd 100644 --- a/src/verifySignedfile.cpp +++ b/src/verifySignedfile.cpp @@ -18,8 +18,9 @@ // VerifyDLL.cpp : Verification of an Authenticode signed DLL // -#include #include +#include +#include #include #include #include @@ -28,15 +29,556 @@ #include #include #include +#include #include "verifySignedfile.h" +#include +#include +#include +#include +#pragma comment(lib, "msxml6.lib") +#pragma comment(lib, "crypt32.lib") +#pragma comment(lib, "ncrypt.lib") +#pragma comment(lib, "bcrypt.lib") -using namespace std; +#import exclude("ISequentialStream", "_FILETIME", "IStream", "IErrorInfo") rename_namespace("MSXML6") +using namespace std; // Debug use bool doLogCertifError = false; +// +// XML Signature (XMLDsig) Verification functions BEGIN +// + +// Helper to decode Base64 +std::vector base64Decode(const std::wstring& base64String) +{ + std::wstring cleaned; + for (wchar_t c : base64String) + { + if (!iswspace(c)) + cleaned += c; + } + + if (cleaned.empty()) + return std::vector(); + + DWORD dwSize = 0; + if (!CryptStringToBinaryW(cleaned.c_str(), (DWORD)cleaned.length(), + CRYPT_STRING_BASE64, NULL, &dwSize, NULL, NULL)) + { + return std::vector(); + } + + std::vector result(dwSize); + if (!CryptStringToBinaryW(cleaned.c_str(), (DWORD)cleaned.length(), CRYPT_STRING_BASE64, result.data(), &dwSize, NULL, NULL)) + { + return std::vector(); + } + + return result; +} + +// Get node text content +std::wstring getNodeText(MSXML6::IXMLDOMNodePtr node) +{ + if (node == nullptr) + return L""; + + _bstr_t text = node->Gettext(); + return std::wstring((wchar_t*)text); +} + +// Compute SHA-256 hash using BCrypt +std::vector computeSHA256(const std::vector& data) +{ + BCRYPT_ALG_HANDLE hAlg = NULL; + BCRYPT_HASH_HANDLE hHash = NULL; + DWORD hashSize = 32; + std::vector hash(hashSize); + + if (BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM, NULL, 0) != 0) + return std::vector(); + + if (BCryptCreateHash(hAlg, &hHash, NULL, 0, NULL, 0, 0) != 0) + { + BCryptCloseAlgorithmProvider(hAlg, 0); + return std::vector(); + } + + BCryptHashData(hHash, (PUCHAR)data.data(), (ULONG)data.size(), 0); + BCryptFinishHash(hHash, hash.data(), hashSize, 0); + + BCryptDestroyHash(hHash); + BCryptCloseAlgorithmProvider(hAlg, 0); + + return hash; +} + +// Verify certificate chain and validity +bool SecurityGuard::verifyXmlCertificate(PCCERT_CONTEXT pCertContext) +{ + // Check certificate validity period + FILETIME currentTime; + GetSystemTimeAsFileTime(¤tTime); + + if (CompareFileTime(¤tTime, &pCertContext->pCertInfo->NotBefore) < 0) + { + writeSecurityError(L"XML Signature - Certificate Error: ", L"Certificate is not yet valid"); + return false; + } + + if (CompareFileTime(¤tTime, &pCertContext->pCertInfo->NotAfter) > 0) + { + writeSecurityError(L"XML Signature - Certificate Error: ", L"Certificate has expired"); + return false; + } + + // Verify certificate chain + CERT_CHAIN_PARA chainPara = { 0 }; + chainPara.cbSize = sizeof(CERT_CHAIN_PARA); + + PCCERT_CHAIN_CONTEXT pChainContext = NULL; + + if (!CertGetCertificateChain( + NULL, + pCertContext, + NULL, + NULL, + &chainPara, + CERT_CHAIN_REVOCATION_CHECK_CHAIN, + NULL, + &pChainContext)) + { + DWORD dwErr = GetLastError(); + std::wstringstream ss; + ss << L"Failed to get certificate chain, error: 0x" << std::hex << dwErr; + writeSecurityError(L"XML Signature - Certificate Chain Error: ", ss.str()); + return false; + } + + bool chainValid = false; + + // Check chain status + if (pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR) + { + chainValid = true; + } + else + { + std::wstringstream ss; + ss << L"Certificate chain validation failed. Error status: 0x" << std::hex << pChainContext->TrustStatus.dwErrorStatus; + + if (pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID) + ss << L" (NOT_TIME_VALID)"; + if (pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED) + ss << L" (REVOKED)"; + if (pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID) + ss << L" (SIGNATURE_INVALID)"; + if (pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT) + ss << L" (UNTRUSTED_ROOT)"; + if (pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) + ss << L" (PARTIAL_CHAIN)"; + + writeSecurityError(L"XML Signature - Certificate Chain Error: ", ss.str()); + } + + CertFreeCertificateChain(pChainContext); + + return chainValid; +} + +std::wstring rawDataToHexString(PCRYPT_DATA_BLOB pDataBlob) +{ + std::wstringstream ss; + + for (DWORD i = 0; i < pDataBlob->cbData; i++) + { + ss << std::hex << std::setw(2) << std::setfill(L'0') << (int)pDataBlob->pbData[i]; + } + + return ss.str(); +} + +// Verify that the certificate matches expected identity +// You can verify by thumbprint (SHA1 hash of entire cert) or Key ID (SHA1 hash of public key) +bool SecurityGuard::verifyXmlTrustedCertificate(PCCERT_CONTEXT pCertContext, const std::wstring& expectedThumbprint/* = L""*/) +{ + // Method 1: Verify by SHA1 thumbprint (most secure - identifies exact certificate) + if (!expectedThumbprint.empty()) + { + // Get certificate thumbprint + BYTE thumbprint[20] = { 0 }; + DWORD thumbprintSize = sizeof(thumbprint); + + if (!CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, thumbprint, &thumbprintSize)) + { + writeSecurityError(L"XML Signature - Certificate Error: ", L"Failed to get certificate thumbprint"); + return false; + } + + // Convert expected thumbprint from hex string to bytes + std::wstring cleanThumbprint; + for (wchar_t c : expectedThumbprint) + { + if (iswxdigit(c)) + cleanThumbprint += c; + } + + if (cleanThumbprint.length() != 40) // SHA1 = 20 bytes = 40 hex chars + { + writeSecurityError(L"XML Signature - Configuration Error: ", L"Invalid thumbprint format"); + return false; + } + + std::vector expectedThumbprintBytes; + for (size_t i = 0; i < cleanThumbprint.length(); i += 2) + { + std::wstring byteStr = cleanThumbprint.substr(i, 2); + BYTE byte = (BYTE)wcstol(byteStr.c_str(), nullptr, 16); + expectedThumbprintBytes.push_back(byte); + } + + // Compare thumbprints + if (memcmp(thumbprint, expectedThumbprintBytes.data(), 20) != 0) + { + // Get certificate subject for error message + DWORD dwSize = CertGetNameStringW(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE,0, NULL, NULL, 0); + std::wstring subjectName(dwSize - 1, 0); + CertGetNameStringW(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, &subjectName[0], dwSize); + + std::wstringstream ss; + ss << L"Document signed by: " << subjectName; + writeSecurityError(L"XML Signature - Certificate thumbprint mismatch Error: ", ss.str()); + return false; + } + + return true; // Thumbprint matches + } + + // Method 2: Verify by Subject Key Identifier (more flexible - identifies the key) + if (!_signer_key_id_xml.empty()) + { + // Get Subject Key Identifier extension + PCERT_EXTENSION pExt = CertFindExtension(szOID_SUBJECT_KEY_IDENTIFIER, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension); + + if (pExt == nullptr) + { + writeSecurityError(L"XML Signature - Certificate Error: ", L"Certificate has no Subject Key Identifier"); + return false; + } + + // Decode the extension + DWORD cbKeyID = 0; + PCRYPT_DATA_BLOB pKeyIDBlob = nullptr; + + if (!CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_SUBJECT_KEY_IDENTIFIER, pExt->Value.pbData, pExt->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, nullptr, &pKeyIDBlob, &cbKeyID)) + { + writeSecurityError(L"XML Signature - Certificate Error: ", L"Failed to decode Subject Key Identifier"); + return false; + } + + // Convert expected Key ID from hex string to bytes + std::wstring cleanKeyID; + for (wchar_t c : _signer_key_id_xml) + { + if (iswxdigit(c)) + cleanKeyID += c; + } + + if (cleanKeyID.length() != 40) // SHA1 = 20 bytes = 40 hex chars + { + LocalFree(pKeyIDBlob); + writeSecurityError(L"XML Signature - Configuration Error: ", L"Invalid Key ID format"); + return false; + } + + std::vector expectedKeyIDBytes; + for (size_t i = 0; i < cleanKeyID.length(); i += 2) + { + std::wstring byteStr = cleanKeyID.substr(i, 2); + BYTE byte = (BYTE)wcstol(byteStr.c_str(), nullptr, 16); + expectedKeyIDBytes.push_back(byte); + } + + // Compare Key IDs + bool match = (pKeyIDBlob->cbData == expectedKeyIDBytes.size()) && (memcmp(pKeyIDBlob->pbData, expectedKeyIDBytes.data(), pKeyIDBlob->cbData) == 0); + + if (!match) + { + DWORD dwSize = CertGetNameStringW(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0); + std::wstring subjectName(dwSize - 1, 0); + CertGetNameStringW(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, &subjectName[0], dwSize); + + wstring wrongKeyId = rawDataToHexString(pKeyIDBlob); + + std::wstringstream ss; + ss << L"expected Key ID " << _signer_key_id_xml << L" vs " << L"wrong Key ID " << wrongKeyId << ", document signed by : " << subjectName; + writeSecurityError(L"XML Signature - Certificate Key ID mismatch Error: ", ss.str()); + + LocalFree(pKeyIDBlob); + return false; + } + LocalFree(pKeyIDBlob); + return true; // Key ID matches + } + + return true; // No verification requested +} + +bool SecurityGuard::verifyXmlSignature(const std::string& xmlData, const std::wstring& trustedThumbprint) +{ + ScopedCOMInit com; + if (!com.isInitialized()) + return false; + + MSXML6::IXMLDOMDocument3Ptr pXMLDoc; + + try + { + // 1. Load XML document + HRESULT hr = pXMLDoc.CreateInstance(__uuidof(MSXML6::DOMDocument60)); + if (FAILED(hr)) + { + writeSecurityError(L"XML Signature - XML Error: ", L"Failed to create XML document"); + return false; + } + + pXMLDoc->put_preserveWhiteSpace(VARIANT_TRUE); + pXMLDoc->async = VARIANT_FALSE; + pXMLDoc->setProperty(_bstr_t("SelectionNamespaces"), + _bstr_t("xmlns:ds='http://www.w3.org/2000/09/xmldsig#'")); + + if (pXMLDoc->loadXML(_bstr_t(xmlData.c_str())) == VARIANT_FALSE) + { + writeSecurityError(L"XML Signature - XML Error: ", L"Failed to load XML"); + return false; + } + + // 2. Find Signature element + MSXML6::IXMLDOMNodePtr pSigNode = pXMLDoc->selectSingleNode(L"//ds:Signature"); + if (pSigNode == nullptr) + { + writeSecurityError(L"XML Signature Error: ", L"The document is not signed - No Signature element found"); + return false; + } + + // 3. Get SignedInfo element + MSXML6::IXMLDOMNodePtr pSignedInfo = pSigNode->selectSingleNode(L"ds:SignedInfo"); + if (pSignedInfo == nullptr) + { + writeSecurityError(L"XML Signature Error: ", L"No SignedInfo element found"); + return false; + } + + // 4. Get SignatureValue + MSXML6::IXMLDOMNodePtr pSigValue = pSigNode->selectSingleNode(L"ds:SignatureValue"); + if (pSigValue == nullptr) + { + writeSecurityError(L"XML Signature Error: ", L"No SignatureValue element found"); + return false; + } + + std::wstring sigValueB64 = getNodeText(pSigValue); + std::vector signatureBytes = base64Decode(sigValueB64); + + if (signatureBytes.empty()) + { + writeSecurityError(L"XML Signature Error: ", L"Failed to decode SignatureValue"); + return false; + } + + // 5. FIRST: Verify the document digest (to detect modifications) + // Get the DigestValue from SignedInfo + MSXML6::IXMLDOMNodePtr pReference = pSignedInfo->selectSingleNode(L"ds:Reference"); + if (pReference == nullptr) + { + writeSecurityError(L"XML Signature Error: ", L"No Reference element found"); + return false; + } + + MSXML6::IXMLDOMNodePtr pDigestValue = pReference->selectSingleNode(L"ds:DigestValue"); + if (pDigestValue == nullptr) + { + writeSecurityError(L"XML Signature Error: ", L"No DigestValue element found"); + return false; + } + + std::wstring expectedDigestB64 = getNodeText(pDigestValue); + std::vector expectedDigest = base64Decode(expectedDigestB64); + + if (expectedDigest.empty()) + { + writeSecurityError(L"XML Signature Error: ", L"Failed to decode DigestValue"); + return false; + } + + // Clone the document and remove the Signature element (enveloped signature transform) + MSXML6::IXMLDOMDocument3Ptr pDocClone; + hr = pDocClone.CreateInstance(__uuidof(MSXML6::DOMDocument60)); + if (FAILED(hr)) + { + writeSecurityError(L"XML Error: ", L"Failed to create document clone"); + return false; + } + + pDocClone->put_preserveWhiteSpace(VARIANT_TRUE); + pDocClone->async = VARIANT_FALSE; + + _bstr_t originalXml = pXMLDoc->Getxml(); + pDocClone->loadXML(originalXml); + + // Remove Signature node from clone + pDocClone->setProperty(_bstr_t("SelectionNamespaces"), + _bstr_t("xmlns:ds='http://www.w3.org/2000/09/xmldsig#'")); + + MSXML6::IXMLDOMNodePtr pSigNodeClone = pDocClone->selectSingleNode(L"//ds:Signature"); + if (pSigNodeClone != nullptr) + { + MSXML6::IXMLDOMNodePtr pParent = pSigNodeClone->GetparentNode(); + if (pParent != nullptr) + { + pParent->removeChild(pSigNodeClone); + } + } + + // Compute digest of the document without signature + _bstr_t docXml = pDocClone->Getxml(); + std::string docStr((char*)docXml); + std::vector docBytes(docStr.begin(), docStr.end()); + std::vector actualDigest = computeSHA256(docBytes); + + // Compare digests + if (actualDigest != expectedDigest) + { + writeSecurityError(L"XML Signature - Document Integrity Error: ", L"The document has been modified after signing. Digest verification failed."); + return false; + } + + // 6. Serialize SignedInfo to bytes (same way as signer) + _bstr_t signedInfoXml = pSignedInfo->Getxml(); + std::string signedInfoStr((char*)signedInfoXml); + std::vector signedInfoBytes(signedInfoStr.begin(), signedInfoStr.end()); + + // 7. Compute hash of SignedInfo + std::vector signedInfoHash = computeSHA256(signedInfoBytes); + + if (signedInfoHash.empty()) + { + writeSecurityError(L"XML Signature - Hash Error", L"Failed to compute hash of SignedInfo"); + return false; + } + + // 8. Get certificate from signature + MSXML6::IXMLDOMNodePtr pX509Cert = pSigNode->selectSingleNode(L"ds:KeyInfo/ds:X509Data/ds:X509Certificate"); + + if (pX509Cert == nullptr) + { + writeSecurityError(L"XML Signature - Certificate Error: ", L"No X509Certificate found in signature"); + return false; + } + + std::wstring certB64 = getNodeText(pX509Cert); + std::vector certBytes = base64Decode(certB64); + + if (certBytes.empty()) + { + writeSecurityError(L"XML Signature - Certificate Error: ", L"Failed to decode certificate"); + return false; + } + + // 9. Create certificate context + PCCERT_CONTEXT pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, certBytes.data(), (DWORD)certBytes.size()); + + if (!pCertContext) + { + DWORD dwErr = GetLastError(); + std::wstringstream ss; + ss << L"Failed to create certificate context. Error: 0x" << std::hex << dwErr; + writeSecurityError(L"XML Signature - Certificate Error: ", ss.str()); + return false; + } + + // 10. Verify certificate validity and chain + if (!verifyXmlCertificate(pCertContext)) + { + CertFreeCertificateContext(pCertContext); + return false; + } + + // 11. Verify this is YOUR trusted certificate (by thumbprint or key ID) + if (!verifyXmlTrustedCertificate(pCertContext, trustedThumbprint)) + { + CertFreeCertificateContext(pCertContext); + return false; + } + + // 12. Import public key and verify signature + BCRYPT_KEY_HANDLE hKey = NULL; + bool result = false; + + if (CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, &pCertContext->pCertInfo->SubjectPublicKeyInfo, 0, NULL, &hKey) != 0) + { + // Setup padding info for RSA-SHA256 + BCRYPT_PKCS1_PADDING_INFO paddingInfo = { 0 }; + paddingInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM; + + // Verify signature + NTSTATUS status = BCryptVerifySignature( + hKey, + &paddingInfo, + signedInfoHash.data(), + (ULONG)signedInfoHash.size(), + signatureBytes.data(), + (ULONG)signatureBytes.size(), + BCRYPT_PAD_PKCS1); + + result = (status == 0); + + if (!result) + { + std::wstringstream ss; + ss << L"BCrypt status: 0x" << std::hex << std::setw(8) << std::setfill(L'0') << status; + + if (status == 0xC000A000) + ss << L" (STATUS_INVALID_SIGNATURE)"; + + writeSecurityError(L"XML Signature Verification Failed: ", ss.str().c_str()); + } + + BCryptDestroyKey(hKey); + } + else + { + DWORD dwErr = GetLastError(); + std::wstringstream ss; + ss << L"Failed to import public key. Error: 0x" << std::hex << dwErr; + writeSecurityError(L"XML Signature - Key Import Error", ss.str().c_str()); + } + + CertFreeCertificateContext(pCertContext); + + return result; + } + catch (_com_error& e) + { + writeSecurityError(L"XML Signature - COM Error: ", e.ErrorMessage()); + return false; + } + catch (...) + { + writeSecurityError(L"XML Signature - Unknown Error: ", L"An unexpected error occurred"); + return false; + } +} + +// +// XML Signature (XMLDsig) Verification functions END +// + + void SecurityGuard::writeSecurityError(const std::wstring& prefix, const std::wstring& log2write) const { // Expand the environment variable @@ -213,8 +755,7 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) wstringstream ss; for (unsigned i = 0; i < key_id_sze; i++) { - ss << std::uppercase << std::setfill(wchar_t('0')) << std::setw(2) << std::hex - << key_id_buff[i]; + ss << std::uppercase << std::setfill(wchar_t('0')) << std::setw(2) << std::hex << key_id_buff[i]; } key_id_hex = ss.str(); diff --git a/src/verifySignedfile.h b/src/verifySignedfile.h index 3b7dc33b..b0ee6745 100644 --- a/src/verifySignedfile.h +++ b/src/verifySignedfile.h @@ -60,6 +60,7 @@ class SecurityGuard final void setDisplayName(const std::wstring& signer_display_name) { _signer_display_name = signer_display_name; } void setSubjectName(const std::wstring& signer_subject) { _signer_subject = signer_subject; } void setKeyId(const std::wstring& signer_key_id) { _signer_key_id = signer_key_id; } + void setKeyIdXml(const std::wstring& signer_key_id_xml) { _signer_key_id_xml = signer_key_id_xml; } void setAuthorityKeyId(const std::wstring& authority_key_id) { _authority_key_id = authority_key_id; } void setErrLogPath(std::wstring& errLogPath) { _errLogPath = errLogPath; } @@ -67,16 +68,23 @@ class SecurityGuard final void writeSecurityError(const std::wstring& prefix, const std::wstring& log2write) const; + bool verifyXmlSignature(const std::string& xmlData, const std::wstring& trustedThumbprint = L""); + private: // Code signing certificate std::wstring _signer_display_name; // = L"Notepad++" std::wstring _signer_subject; // = L"C=FR, S=Ile-de-France, L=Saint Cloud, O=\"Notepad++\", CN=\"Notepad++\", E=don.h@free.fr" std::wstring _signer_key_id; // = L"7B4D26B77F8269B987AC3E8EBC3899E1A4176DFA" => Should be UPPERCASE + std::wstring _signer_key_id_xml; // = L"7B4D26B77F8269B987AC3E8EBC3899E1A4176DFA" => Should be UPPERCASE std::wstring _authority_key_id; // = L"8BDE0FA542DB39D347AF06A83AC9D09D421D1366" => Should be UPPERCASE bool _doCheckRevocation = false; bool _doCheckChainOfTrust = false; + // For xml signature verification + bool verifyXmlTrustedCertificate(PCCERT_CONTEXT pCertContext, const std::wstring& expectedThumbprint = L""); + bool verifyXmlCertificate(PCCERT_CONTEXT pCertContext); + std::wstring _errLogPath = L"%LOCALAPPDATA%\\WinGUp\\log\\securityError.log"; // By default, but overrideable }; diff --git a/src/winmain.cpp b/src/winmain.cpp index 71fa17f4..5a8a8156 100644 --- a/src/winmain.cpp +++ b/src/winmain.cpp @@ -99,6 +99,8 @@ static constexpr wchar_t FLAG_CHKCERT_NAME[] = L"-chkCertName="; static constexpr wchar_t FLAG_CHKCERT_SUBJECT[] = L"-chkCertSubject="; static constexpr wchar_t FLAG_CHKCERT_KEYID[] = L"-chkCertKeyId="; static constexpr wchar_t FLAG_CHKCERT_AUTHORITYKEYID[] = L"-chkCertAuthorityKeyId="; +static constexpr wchar_t FLAG_CHKCERT_XML[] = L"-chkCert4InfoXML"; +static constexpr wchar_t FLAG_CHKCERT_KEYID_XML[] = L"-chkCertKeyId4XML="; static constexpr wchar_t FLAG_ERRLOGPATH[] = L"-errLogPath="; static constexpr wchar_t MSGID_HELP[] = @@ -150,6 +152,16 @@ gup [-vVERSION_VALUE] [-infoUrl=URL] [-chkCertSig=YES_NO] [-chkCertTrustChain]\r -errLogPath= : override the default error log path. The default value is:\r\n\ \"%LOCALAPPDATA%\\WinGUp\\log\\securityError.log\"\r\n\ \r\n\ +Update mode:\r\n\ +\r\n\ +gup [-vVERSION_VALUE] [-infoUrl=URL] [-chkCertKeyId=CERT_KEYID]\r\n\ +\r\n\ + -chkCert4InfoXML : Enable signature check for XML (XMLDsig) returned from server.\r\n\ + Enable this when the server signs the XML; it ensure the XML\r\n\ + has not been altered or hijacked.\r\n\ + -chkCertKeyId4XML= : Use the certificate Key ID for authentication. If ignored,\r\n\ + only XML integrity is checked; authentication is not verified.\r\n\ +\r\n\ Download & unzip mode:\r\n\ \r\n\ gup -clean FOLDER_TO_ACTION\r\n\ @@ -1376,6 +1388,18 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) securityGuard.setErrLogPath(errLogPath); } + bool updateInfoXmlMustBeSigned = false; + if (isInList(FLAG_CHKCERT_XML, params)) + { + updateInfoXmlMustBeSigned = true; + } + + wstring signer_key_id_xml; + if (getParamValFromString(FLAG_CHKCERT_KEYID_XML, params, signer_key_id_xml)) + { + securityGuard.setKeyIdXml(signer_key_id_xml); + } + // Object (gupParams) is moved here because we need app icon form configuration file GupParameters gupParams(L"gup.xml"); appIconFile = gupParams.getSoftwareIcon(); @@ -1644,6 +1668,11 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) return -1; } + if (updateInfoXmlMustBeSigned && !securityGuard.verifyXmlSignature(updateInfo)) + { + return -1; + } + bool isModal = gupParams.isMessageBoxModal(); GupDownloadInfo gupDlInfo(updateInfo.c_str());