CryptoAPI functions replaced with wxExtend classes, logging improved
This commit is contained in:
parent
ca8d110de5
commit
bd1652bd5f
@ -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<BYTE> 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<BYTE>::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<BYTE> 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;
|
||||
}
|
||||
|
@ -27,10 +27,12 @@
|
||||
#include <wx/app.h>
|
||||
#include <wx/base64.h>
|
||||
#include <wx/datetime.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/protocol/http.h>
|
||||
#include <wx/xml/xml.h>
|
||||
|
||||
#include <wxex/common.h>
|
||||
#include <wxex/crypto.h>
|
||||
#include <wxex/xml.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -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<BYTE> 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<BYTE>::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;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <wx/xml/xml.h>
|
||||
|
||||
#include <wxex/common.h>
|
||||
#include <wxex/crypto.h>
|
||||
#include <wxex/xml.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
Loading…
x
Reference in New Issue
Block a user