From bd1652bd5f723591be19be447789bfcc86726cd2 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Fri, 18 Mar 2016 12:19:28 +0100 Subject: [PATCH] CryptoAPI functions replaced with wxExtend classes, logging improved --- UpdCheck/main.cpp | 210 ++++++++++++++++++++++++++++---------------- UpdCheck/stdafx.h | 2 + UpdSignXML/main.cpp | 87 ++++++++---------- UpdSignXML/stdafx.h | 1 + 4 files changed, 172 insertions(+), 128 deletions(-) diff --git a/UpdCheck/main.cpp b/UpdCheck/main.cpp index f9abb6f..15ec45f 100644 --- a/UpdCheck/main.cpp +++ b/UpdCheck/main.cpp @@ -18,7 +18,45 @@ */ #include "stdafx.h" -#pragma comment(lib, "Crypt32.lib") + + +/// +/// Module initializer +/// +class wxUpdCheckInitializer : public wxInitializer +{ +protected: + FILE *m_pLogFile; + +public: + wxUpdCheckInitializer(); + virtual ~wxUpdCheckInitializer(); +}; + + +wxUpdCheckInitializer::wxUpdCheckInitializer() : + m_pLogFile(NULL), + wxInitializer() +{ + if (wxInitializer::IsOk()) { + wxString str(wxFileName::GetTempDir()); + str += wxFILE_SEP_PATH; + str += wxT(UPDATER_LOG_FILE); + m_pLogFile = fopen(str, "w+"); + if (m_pLogFile) + delete wxLog::SetActiveTarget(new wxLogStderr(m_pLogFile)); + } else + m_pLogFile = NULL; +} + + +wxUpdCheckInitializer::~wxUpdCheckInitializer() +{ + if (m_pLogFile != NULL) { + delete wxLog::SetActiveTarget(NULL); + fclose(m_pLogFile); + } +} /// @@ -28,97 +66,113 @@ int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In { wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program"); - // Inizialize wxWidgets. - wxInitializer initializer; - if (!initializer) + // Initialize wxWidgets. + wxUpdCheckInitializer initializer; + if (!initializer.IsOk()) return -1; - wxXmlDocument doc; - - // Load repository database. - wxHTTP http; - //wxDateTime timestamp_last_checked; - //http.SetHeader(wxS("If-Modified-Since"), timestamp_last_checked.Format(wxS("%a, %d %b %y %T %z"))); - if (!http.Connect(wxS(UPDATER_HTTP_SERVER), UPDATER_HTTP_PORT)) { - wxFAIL_MSG(wxT("Error resolving server name.")); - return 1; + // Create RSA AES cryptographic context. + wxCryptoSessionRSAAES cs; + if (!cs.IsOk()) { + wxLogError(wxT("Failed to create cryptographics session.")); + return -1; } - wxInputStream *httpStream = http.GetInputStream(wxS(UPDATER_HTTP_PATH)); - if (!doc.Load(*httpStream, "UTF-8", wxXMLDOC_KEEP_WHITESPACE_NODES)) { - wxFAIL_MSG(wxT("Error reading data file.")); - return 1; - } - wxDELETE(httpStream); - http.Close(); - - // Find the (first) signature. - wxXmlNode *document = doc.GetDocumentNode(); - std::vector sig; - for (wxXmlNode *prolog = document->GetChildren(); prolog; prolog = prolog->GetNext()) { - if (prolog->GetType() == wxXML_COMMENT_NODE) { - wxString content = prolog->GetContent(); - size_t content_len = content.length(); - if (content_len >= _countof(wxS(UPDATER_SIGNATURE_MARK)) - 1 && - memcmp((const wxStringCharType*)content, wxS(UPDATER_SIGNATURE_MARK), sizeof(wxStringCharType)*(_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1)) == 0) - { - // Read the signature. - size_t signature_len = content_len - (_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1); - sig.resize(wxBase64DecodedSize(signature_len)); - size_t res = wxBase64Decode(sig.data(), sig.capacity(), content.Right(signature_len), wxBase64DecodeMode_SkipWS); - if (res != wxCONV_FAILED) { - sig.resize(res); - - // Remove the signature as it is not a part of the validation check. - document->RemoveChild(prolog); - wxDELETE(prolog); - break; - } else - sig.clear(); - } - } - } - - if (sig.empty()) - wxFAIL_MSG(wxT("Signature not found in the Updater file.")); - - // Reverse byte order, for consistent OpenSSL experience. - for (std::vector::size_type i = 0, j = sig.size() - 1; i < j; i++, j--) - std::swap(sig[i], sig[j]); - - // Create cryptographic context. - HCRYPTPROV cp = NULL; - wxVERIFY(::CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)); - - // Hash the content. - HCRYPTHASH ch = NULL; - wxVERIFY(::CryptCreateHash(cp, CALG_SHA1, 0, 0, &ch)); - ::wxXmlHashNode(ch, document); // Import public key. - HCRYPTKEY ck = NULL; + wxCryptoKey ck; { HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(IDR_KEY_PUBLIC), RT_RCDATA); wxASSERT_MSG(res, wxT("public key not found")); HGLOBAL res_handle = ::LoadResource(NULL, res); wxASSERT_MSG(res_handle, wxT("loading resource failed")); - CERT_PUBLIC_KEY_INFO *keyinfo_data = NULL; - DWORD keyinfo_size = 0; - wxVERIFY(::CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, (const BYTE*)::LockResource(res_handle), ::SizeofResource(NULL, res), CRYPT_DECODE_ALLOC_FLAG, NULL, &keyinfo_data, &keyinfo_size)); - - wxVERIFY(::CryptImportPublicKeyInfo(cp, X509_ASN_ENCODING, keyinfo_data, &ck)); - ::LocalFree(keyinfo_data); + if (!ck.ImportPublic(cs, ::LockResource(res_handle), ::SizeofResource(NULL, res))) { + wxLogError(wxT("Failed to import public key.")); + return -1; + } } - // We have the hash, we have the signature, we have the public key. Now verify. - if (::CryptVerifySignature(ch, sig.data(), sig.size(), ck, NULL, 0)) { + for (const wxChar *server = wxT(UPDATER_HTTP_SERVER); server[0]; server += wcslen(server) + 1) { + wxXmlDocument doc; + + wxLogStatus(wxT("Contacting repository at %s..."), server); + + // Load repository database. + wxHTTP http; + //wxDateTime timestamp_last_checked; + //http.SetHeader(wxS("If-Modified-Since"), timestamp_last_checked.Format(wxS("%a, %d %b %y %T %z"))); + if (!http.Connect(server, UPDATER_HTTP_PORT)) { + wxLogWarning(wxT("Error resolving %s server name."), server); + continue; + } + wxInputStream *httpStream = http.GetInputStream(wxS(UPDATER_HTTP_PATH)); + if (!httpStream) { + wxLogWarning(wxT("Error response received from server %s (port %u) requesting %s."), server, UPDATER_HTTP_PORT, UPDATER_HTTP_PATH); + continue; + } + wxLogStatus(wxT("Loading repository catalogue...")); + if (!doc.Load(*httpStream, "UTF-8", wxXMLDOC_KEEP_WHITESPACE_NODES)) { + wxLogWarning(wxT("Error loading repository catalogue.")); + wxDELETE(httpStream); + http.Close(); + continue; + } + wxDELETE(httpStream); + http.Close(); + + wxLogStatus(wxT("Verifying repository catalogue signature...")); + + // Find the (first) signature. + wxXmlNode *document = doc.GetDocumentNode(); + std::vector sig; + for (wxXmlNode *prolog = document->GetChildren(); prolog; prolog = prolog->GetNext()) { + if (prolog->GetType() == wxXML_COMMENT_NODE) { + wxString content = prolog->GetContent(); + size_t content_len = content.length(); + if (content_len >= _countof(wxS(UPDATER_SIGNATURE_MARK)) - 1 && + memcmp((const wxStringCharType*)content, wxS(UPDATER_SIGNATURE_MARK), sizeof(wxStringCharType)*(_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1)) == 0) + { + // Read the signature. + size_t signature_len = content_len - (_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1); + sig.resize(wxBase64DecodedSize(signature_len)); + size_t res = wxBase64Decode(sig.data(), sig.capacity(), content.Right(signature_len), wxBase64DecodeMode_SkipWS); + if (res != wxCONV_FAILED) { + sig.resize(res); + + // Remove the signature as it is not a part of the validation check. + document->RemoveChild(prolog); + wxDELETE(prolog); + break; + } else + sig.clear(); + } + } + } + + if (sig.empty()) { + wxLogWarning(wxT("Signature not found in the repository catalogue.")); + continue; + } + + // Hash the content. + wxCryptoHashSHA1 ch(cs); + if (!wxXmlHashNode(ch, document)) { + wxLogWarning(wxT("Failed to hash the repository catalogue.")); + continue; + } + + // We have the hash, we have the signature, we have the public key. Now verify. + if (!wxCryptoVerifySignature(ch, sig.data(), sig.size(), ck)) { + wxLogWarning(wxT("Repository catalogue signature does not match its content, or signature verification failed.")); + continue; + } + // The signature is correct. Now parse the file. - } else - wxFAIL_MSG(wxT("Updater file signature does not match file content.")); + wxLogStatus(wxT("Parsing repository catalogue...")); - wxVERIFY(::CryptDestroyKey(ck)); - wxVERIFY(::CryptDestroyHash(ch)); - wxVERIFY(::CryptReleaseContext(cp, 0)); + return 0; + } - return 0; + // No success. + return 1; } diff --git a/UpdCheck/stdafx.h b/UpdCheck/stdafx.h index 74ce2ba..cf5e6be 100644 --- a/UpdCheck/stdafx.h +++ b/UpdCheck/stdafx.h @@ -27,10 +27,12 @@ #include #include #include +#include #include #include #include +#include #include #include diff --git a/UpdSignXML/main.cpp b/UpdSignXML/main.cpp index 1245c21..b72ebf9 100644 --- a/UpdSignXML/main.cpp +++ b/UpdSignXML/main.cpp @@ -18,7 +18,6 @@ */ #include "stdafx.h" -#pragma comment(lib, "Crypt32.lib") /// @@ -28,9 +27,9 @@ int _tmain(int argc, _TCHAR *argv[]) { wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program"); - // Inizialize wxWidgets. + // Initialize wxWidgets. wxInitializer initializer; - if (!initializer) { + if (!initializer.IsOk()) { _ftprintf(stderr, wxT("Failed to initialize the wxWidgets library, aborting.\n")); return -1; } @@ -55,22 +54,20 @@ int _tmain(int argc, _TCHAR *argv[]) break; default: - wxLogMessage(wxT("Syntax error detected, aborting.")); + wxLogWarning(wxT("Syntax error detected, aborting.")); return -1; } + // Load input XML document. const wxString& filenameIn = parser.GetParam(0); wxXmlDocument doc; if (!doc.Load(filenameIn, "UTF-8", wxXMLDOC_KEEP_WHITESPACE_NODES)) { - _ftprintf(stderr, wxT("%s: error UMD0001: Error opening input file.\n"), filenameIn.fn_str()); + _ftprintf(stderr, wxT("%s: error USX0001: Error opening input file.\n"), filenameIn.fn_str()); return 1; } - bool has_errors = false; - + // Examine prologue if the document is already signed and remove all signatures found. wxXmlNode *document = doc.GetDocumentNode(); - - // Examine prologue if the document is already signed and remove all signatures. for (wxXmlNode *prolog = document->GetChildren(); prolog;) { if (prolog->GetType() == wxXML_COMMENT_NODE) { wxString content = prolog->GetContent(); @@ -89,67 +86,57 @@ int _tmain(int argc, _TCHAR *argv[]) prolog = prolog->GetNext(); } - // Create cryptographic context. - HCRYPTPROV cp = NULL; - wxVERIFY(::CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)); + // Create RSA AES cryptographic session. + wxCryptoSessionRSAAES cs; + if (!cs.IsOk()) { + _ftprintf(stderr, wxT("Failed to create cryptographics session.\n")); + return -1; + } - // Import private key. + // Import the private key into the session from resources. { HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(IDR_KEY_PRIVATE), RT_RCDATA); wxASSERT_MSG(res, wxT("private key not found")); HGLOBAL res_handle = ::LoadResource(NULL, res); wxASSERT_MSG(res_handle, wxT("loading resource failed")); - PUBLICKEYSTRUC *key_data = NULL; - DWORD key_size = 0; - wxVERIFY(::CryptDecodeObjectEx(X509_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, (const BYTE*)::LockResource(res_handle), ::SizeofResource(NULL, res), CRYPT_DECODE_ALLOC_FLAG, NULL, &key_data, &key_size)); - // See: http://pumka.net/2009/12/16/rsa-encryption-cplusplus-delphi-cryptoapi-php-openssl-2/comment-page-1/#comment-429 - key_data->aiKeyAlg = CALG_RSA_SIGN; - - HCRYPTKEY ck = NULL; - wxVERIFY(::CryptImportKey(cp, (const BYTE*)key_data, key_size, NULL, 0, &ck)); - wxVERIFY(::CryptDestroyKey(ck)); - ::LocalFree(key_data); + wxCryptoKey ck; + if (!ck.ImportPrivate(cs, ::LockResource(res_handle), ::SizeofResource(NULL, res))) { + _ftprintf(stderr, wxT("Failed to import private key.\n")); + return -1; + } } - // Hash the content. - HCRYPTHASH ch = NULL; - wxVERIFY(::CryptCreateHash(cp, CALG_SHA1, 0, 0, &ch)); - ::wxXmlHashNode(ch, document); + // Hash the XML content. + wxCryptoHashSHA1 ch(cs); + if (!wxXmlHashNode(ch, document)) { + _ftprintf(stderr, wxT("%s: error USX0002: Error hashing input file.\n"), filenameIn.fn_str()); + return 2; + } // Sign the hash. - DWORD sig_size = 0; - if (!::CryptSignHash(ch, AT_SIGNATURE, NULL, 0, NULL, &sig_size)) { - _ftprintf(stderr, wxT("%s: error UMD0003: Signing input file failed (%i).\n"), filenameIn.fn_str(), ::GetLastError()); - has_errors = true; + wxMemoryBuffer sig; + if (!ch.Sign(sig)) { + _ftprintf(stderr, wxT("%s: error USX0003: Error signing hash.\n"), filenameIn.fn_str()); + return 3; } - std::vector sig(sig_size); - if (!::CryptSignHash(ch, AT_SIGNATURE, NULL, 0, sig.data(), &sig_size)) { - _ftprintf(stderr, wxT("%s: error UMD0003: Signing input file failed (%i).\n"), filenameIn.fn_str(), ::GetLastError()); - has_errors = true; - } - wxVERIFY(::CryptDestroyHash(ch)); - wxVERIFY(::CryptReleaseContext(cp, 0)); - - // Reverse byte order, for consistent OpenSSL experience. - for (std::vector::size_type i = 0, j = sig.size() - 1; i < j; i++, j--) - std::swap(sig[i], sig[j]); // Encode signature (Base64) and append to the document prolog. wxString signature; signature += wxS(UPDATER_SIGNATURE_MARK); - signature += wxBase64Encode(sig.data(), sig.size()); + signature += wxBase64Encode(sig); document->AddChild(new wxXmlNode(wxXML_COMMENT_NODE, wxS(""), signature)); + // Write output XML document. const wxString& filenameOut = parser.GetParam(1); if (!doc.Save(filenameOut, wxXML_NO_INDENTATION)) { - _ftprintf(stderr, wxT("%s: error UMD0001: Error writing output file.\n"), filenameOut.fn_str()); - has_errors = true; + _ftprintf(stderr, wxT("%s: error USX0004: Error writing output file.\n"), filenameOut.fn_str()); + + // Remove the output file (if exists). + wxRemoveFile(filenameOut); + + return 4; } - if (has_errors) { - wxRemoveFile(filenameOut); - return 1; - } else - return 0; + return 0; } diff --git a/UpdSignXML/stdafx.h b/UpdSignXML/stdafx.h index 02d521e..e95dd05 100644 --- a/UpdSignXML/stdafx.h +++ b/UpdSignXML/stdafx.h @@ -28,6 +28,7 @@ #include #include +#include #include #include