diff --git a/build/wxExtend.vcxproj b/build/wxExtend.vcxproj
index 8359984..5e70355 100644
--- a/build/wxExtend.vcxproj
+++ b/build/wxExtend.vcxproj
@@ -21,6 +21,7 @@
+
Create
Create
@@ -33,6 +34,7 @@
+
diff --git a/build/wxExtend.vcxproj.filters b/build/wxExtend.vcxproj.filters
index 68925a3..6cca51e 100644
--- a/build/wxExtend.vcxproj.filters
+++ b/build/wxExtend.vcxproj.filters
@@ -31,6 +31,9 @@
Source Files
+
+ Source Files
+
@@ -48,6 +51,9 @@
Header Files
+
+ Header Files
+
diff --git a/include/wxex/crypto.h b/include/wxex/crypto.h
new file mode 100644
index 0000000..a4f1f12
--- /dev/null
+++ b/include/wxex/crypto.h
@@ -0,0 +1,260 @@
+/*
+ Copyright 2016 Amebis
+
+ This file is part of wxExtend.
+
+ wxExtend is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ wxExtend is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with wxExtend. If not, see .
+*/
+
+#pragma once
+
+#include "common.h"
+
+#include
+#include
+
+#include
+
+
+///
+/// Cryptographics Session Base Class
+///
+class WXEXTEND_API wxCryptoSession
+{
+protected:
+ HCRYPTPROV m_h; ///< Session Handle
+
+public:
+ wxCryptoSession();
+ virtual ~wxCryptoSession();
+
+
+ ///
+ /// Has the session creation been successful?
+ ///
+ /// \returns
+ /// - true if creation succeeded
+ /// - false otherwise
+ ///
+ inline bool IsOk() const
+ {
+ return m_h != NULL;
+ }
+
+
+ ///
+ /// \returns Session handle to be used in native API calls.
+ ///
+ inline operator HCRYPTPROV() const
+ {
+ return m_h;
+ }
+};
+
+
+///
+/// RSA AES Cryptographics Session
+///
+class WXEXTEND_API wxCryptoSessionRSAAES : public wxCryptoSession
+{
+public:
+ wxCryptoSessionRSAAES();
+};
+
+
+///
+/// Cryptographics Hash Base
+///
+class WXEXTEND_API wxCryptoHash
+{
+protected:
+ HCRYPTHASH m_h; ///< Hash Handle
+
+public:
+ wxCryptoHash();
+ virtual ~wxCryptoHash();
+
+
+ ///
+ /// Has the hash creation been successful?
+ ///
+ /// \returns
+ /// - true if creation succeeded
+ /// - false otherwise
+ ///
+ inline bool IsOk() const
+ {
+ return m_h != NULL;
+ }
+
+
+ ///
+ /// \returns Hash handle to be used in native API calls.
+ ///
+ inline operator HCRYPTHASH() const
+ {
+ return m_h;
+ }
+
+
+ ///
+ /// Hashes given block of data
+ ///
+ /// \param[in] data Pointer to memory block
+ /// \param[in] size Size of memory block in bytes
+ ///
+ /// \returns
+ /// - true if hashing succeeded
+ /// - false otherwise
+ ///
+ bool Hash(const void *data, size_t size);
+
+
+ ///
+ /// Hashes given block of data
+ ///
+ /// \param[in] data Memory block
+ ///
+ /// \returns
+ /// - true if hashing succeeded
+ /// - false otherwise
+ ///
+ inline bool Hash(const wxMemoryBuffer &data)
+ {
+ return Hash(data.GetData(), data.GetDataLen());
+ }
+
+
+ ///
+ /// Converts string to UTF-8 and hashes it.
+ ///
+ /// \param[in] str String to hash
+ ///
+ /// \returns
+ /// - true if hashing succeeded
+ /// - false otherwise
+ ///
+ inline bool HashAsUTF8(const wxString &str)
+ {
+ const wxScopedCharBuffer buf(str.ToUTF8());
+ return Hash((const char*)buf.data(), buf.length());
+ }
+
+
+ ///
+ /// Signs the hash using session key
+ ///
+ /// \param[out] signature Digital signature
+ ///
+ /// \returns
+ /// - true if signing succeeded
+ /// - false otherwise
+ ///
+ bool Sign(wxMemoryBuffer &signature);
+
+
+ ///
+ /// Signs the hash using session key
+ ///
+ /// \returns Digital Signature
+ ///
+ inline wxMemoryBuffer Sign()
+ {
+ wxMemoryBuffer signature;
+ wxVERIFY(Sign(signature));
+ return signature;
+ }
+};
+
+
+///
+/// SHA-1 Cryptographics Hash
+///
+class WXEXTEND_API wxCryptoHashSHA1 : public wxCryptoHash
+{
+public:
+ wxCryptoHashSHA1(wxCryptoSession &session);
+};
+
+
+///
+/// Cryptographics Key Base
+///
+class WXEXTEND_API wxCryptoKey
+{
+public:
+ wxCryptoKey();
+ virtual ~wxCryptoKey();
+
+
+ ///
+ /// Has the key creation been successful?
+ ///
+ /// \returns
+ /// - true if creation succeeded
+ /// - false otherwise
+ ///
+ inline bool IsOk() const
+ {
+ return m_h != NULL;
+ }
+
+
+ ///
+ /// \returns Key handle to be used in native API calls.
+ ///
+ inline operator HCRYPTKEY() const
+ {
+ return m_h;
+ }
+
+
+ bool ImportPrivate(wxCryptoSession &session, const void *data, size_t size);
+ bool ImportPublic(wxCryptoSession &session, const void *data, size_t size);
+
+protected:
+ HCRYPTKEY m_h;
+};
+
+
+///
+/// Verifies if the hash matches signature and the public key
+///
+/// \param[in] hash Hash
+/// \param[in] signature_data Pointer to signature data
+/// \param[in] signature_size Signature data size in bytes
+/// \param[in] key Public key
+///
+/// \returns
+/// - true if verification succeeded and the hash matches
+/// - false otherwise
+///
+bool WXEXTEND_API wxCryptoVerifySignature(const wxCryptoHash &hash, const void *signature_data, size_t signature_size, const wxCryptoKey &key);
+
+
+///
+/// Verifies if the hash matches signature and the public key
+///
+/// \param[in] hash Hash
+/// \param[in] signature Signature data
+/// \param[in] key Public key
+///
+/// \returns
+/// - true if verification succeeded and the hash matches
+/// - false otherwise
+///
+inline bool wxCryptoVerifySignature(const wxCryptoHash &hash, const wxMemoryBuffer &signature, const wxCryptoKey &key)
+{
+ return ::wxCryptoVerifySignature(hash, signature.GetData(), signature.GetDataLen(), key);
+}
diff --git a/include/wxex/xml.h b/include/wxex/xml.h
index 0dba1c4..10d03f4 100644
--- a/include/wxex/xml.h
+++ b/include/wxex/xml.h
@@ -19,6 +19,7 @@
#pragma once
+#include "crypto.h"
#include "common.h"
#include
@@ -113,4 +114,4 @@ inline wxString wxXmlEscapeAttr(_In_ const wxString& str)
/// \param[in] node Root node
///
///
-void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node);
+bool WXEXTEND_API wxXmlHashNode(_In_ wxCryptoHash &hash, const wxXmlNode *node);
diff --git a/src/crypto.cpp b/src/crypto.cpp
new file mode 100644
index 0000000..66a78d2
--- /dev/null
+++ b/src/crypto.cpp
@@ -0,0 +1,188 @@
+/*
+ Copyright 2015-2016 Amebis
+
+ This file is part of wxExtend.
+
+ wxExtend is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ wxExtend is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with wxExtend. If not, see .
+*/
+
+#include "stdafx.h"
+#pragma comment(lib, "Crypt32.lib")
+
+
+//////////////////////////////////////////////////////////////////////////
+// wxCryptoSession
+//////////////////////////////////////////////////////////////////////////
+
+wxCryptoSession::wxCryptoSession() : m_h(NULL)
+{
+}
+
+
+wxCryptoSession::~wxCryptoSession()
+{
+ if (m_h)
+ ::CryptReleaseContext(m_h, 0);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// wxCryptoSessionRSAAES
+//////////////////////////////////////////////////////////////////////////
+
+wxCryptoSessionRSAAES::wxCryptoSessionRSAAES()
+{
+ ::CryptAcquireContext(&m_h, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// wxCryptoHash
+//////////////////////////////////////////////////////////////////////////
+
+wxCryptoHash::wxCryptoHash() : m_h(NULL)
+{
+}
+
+
+wxCryptoHash::~wxCryptoHash()
+{
+ if (m_h)
+ ::CryptDestroyHash(m_h);
+}
+
+
+bool wxCryptoHash::Hash(const void *data, size_t size)
+{
+ wxASSERT_MSG(m_h, wxT("object uninitialized"));
+ wxASSERT_MSG(data || !size, wxT("invalid parameter"));
+
+ return ::CryptHashData(m_h, (const BYTE*)data, size, 0) ? true : false;
+}
+
+
+bool wxCryptoHash::Sign(wxMemoryBuffer &signature)
+{
+ // Try with the current buffer size first.
+ DWORD size = (DWORD)signature.GetBufSize();
+ BYTE *data = (BYTE*)signature.GetWriteBuf(size);
+ if (::CryptSignHash(m_h, AT_SIGNATURE, NULL, 0, data, &size)) {
+ // The buffer was large enough. Set the actual size.
+ signature.SetDataLen(size);
+ } else if (::GetLastError() == ERROR_MORE_DATA) {
+ // The buffer was not big enough. Variable size contains the actual required size. Realloc and retry one more time.
+ wxCHECK(::CryptSignHash(m_h, AT_SIGNATURE, NULL, 0, data = (BYTE*)signature.GetWriteBuf(size), &size), false);
+ signature.SetDataLen(size);
+ } else {
+ signature.Clear();
+ return false;
+ }
+
+ // Reverse byte order, for consistent OpenSSL experience.
+ for (DWORD i = 0, j = size - 1; i < j; i++, j--)
+ wxSwap(data[i], data[j]);
+
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// wxCryptoHashSHA1
+//////////////////////////////////////////////////////////////////////////
+
+wxCryptoHashSHA1::wxCryptoHashSHA1(wxCryptoSession &session)
+{
+ ::CryptCreateHash(session, CALG_SHA1, 0, 0, &m_h);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// wxCryptoKey
+//////////////////////////////////////////////////////////////////////////
+
+wxCryptoKey::wxCryptoKey() : m_h(NULL)
+{
+}
+
+
+wxCryptoKey::~wxCryptoKey()
+{
+ if (m_h)
+ ::CryptDestroyKey(m_h);
+}
+
+
+bool wxCryptoKey::ImportPrivate(wxCryptoSession &session, const void *data, size_t size)
+{
+ wxASSERT_MSG(!m_h, wxT("object initialized"));
+ wxASSERT_MSG(session.IsOk(), wxT("invalid session"));
+ wxASSERT_MSG(data || !size, wxT("invalid parameter"));
+
+ PUBLICKEYSTRUC *key_data = NULL;
+ DWORD key_size = 0;
+ if (!::CryptDecodeObjectEx(X509_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, (const BYTE*)data, size, CRYPT_DECODE_ALLOC_FLAG, NULL, &key_data, &key_size))
+ return false;
+
+ // 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;
+
+ if (!::CryptImportKey(session, (const BYTE*)key_data, key_size, NULL, 0, &m_h)) {
+ ::LocalFree(key_data);
+ return false;
+ }
+ ::LocalFree(key_data);
+
+ return true;
+}
+
+
+bool wxCryptoKey::ImportPublic(wxCryptoSession &session, const void *data, size_t size)
+{
+ wxASSERT_MSG(!m_h, wxT("object initialized"));
+ wxASSERT_MSG(session.IsOk(), wxT("invalid session"));
+ wxASSERT_MSG(data || !size, wxT("invalid parameter"));
+
+ CERT_PUBLIC_KEY_INFO *keyinfo_data = NULL;
+ DWORD keyinfo_size = 0;
+ if (!::CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, (const BYTE*)data, size, CRYPT_DECODE_ALLOC_FLAG, NULL, &keyinfo_data, &keyinfo_size))
+ return false;
+
+ if (!::CryptImportPublicKeyInfo(session, X509_ASN_ENCODING, keyinfo_data, &m_h)) {
+ ::LocalFree(keyinfo_data);
+ return false;
+ }
+ ::LocalFree(keyinfo_data);
+
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// wxCryptoVerifySignature
+//////////////////////////////////////////////////////////////////////////
+
+bool WXEXTEND_API wxCryptoVerifySignature(const wxCryptoHash &hash, const void *signature_data, size_t signature_size, const wxCryptoKey &key)
+{
+ wxASSERT_MSG(hash.IsOk() , wxT("invalid hash"));
+ wxASSERT_MSG(signature_data || !signature_size, wxT("invalid parameter"));
+ wxASSERT_MSG(key.IsOk() , wxT("invalid key"));
+
+ // Reverse byte order, for consistent OpenSSL experience.
+ wxMemoryBuffer signature(signature_size);
+ BYTE *data = (BYTE*)signature.GetData();
+ for (size_t i = 0, j = signature_size - 1; i < signature_size; i++, j--)
+ data[i] = ((const BYTE*)signature_data)[j];
+
+ return ::CryptVerifySignature(hash, data, signature_size, key, NULL, 0) ? true : false;
+}
diff --git a/src/stdafx.h b/src/stdafx.h
index 29d9677..97c9249 100644
--- a/src/stdafx.h
+++ b/src/stdafx.h
@@ -25,6 +25,7 @@
#include "../include/wxex/appbar.h"
#include "../include/wxex/comutils.h"
+#include "../include/wxex/crypto.h"
#include "../include/wxex/xml.h"
#include "../include/wxex/common.h"
diff --git a/src/xml.cpp b/src/xml.cpp
index aed3d2b..26b039f 100644
--- a/src/xml.cpp
+++ b/src/xml.cpp
@@ -20,14 +20,7 @@
#include "stdafx.h"
-static inline BOOL CryptHashData(__in HCRYPTHASH hHash, __in const wxString &str, __in DWORD dwFlags)
-{
- const wxScopedCharBuffer buf(str.ToUTF8());
- return ::CryptHashData(hHash, (const BYTE*)buf.data(), buf.length(), dwFlags);
-}
-
-
-void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node)
+bool WXEXTEND_API wxXmlHashNode(_In_ wxCryptoHash &hash, const wxXmlNode *node)
{
wxASSERT_MSG(node, wxT("invalid parameter"));
@@ -38,22 +31,22 @@ void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node)
static const BYTE element_in[] = "<";
// Hash the open tag.
- wxVERIFY(::CryptHashData(hash, element_in, _countof(element_in) - 1, 0));
- wxVERIFY(::CryptHashData(hash, node->GetName(), 0));
+ wxCHECK(hash.Hash(element_in, _countof(element_in) - 1), false);
+ wxCHECK(hash.HashAsUTF8(node->GetName()), false);
for (wxXmlAttribute *attr = node->GetAttributes(); attr; attr = attr->GetNext()) {
static const BYTE attrib_sep[] = " ";
- wxVERIFY(::CryptHashData(hash, attrib_sep, _countof(attrib_sep) - 1, 0));
- wxVERIFY(::CryptHashData(hash, attr->GetName(), 0));
+ wxCHECK(hash.Hash(attrib_sep, _countof(attrib_sep) - 1), false);
+ wxCHECK(hash.HashAsUTF8(attr->GetName()), false);
wxString value = attr->GetValue();
if (!value.IsEmpty()) {
static const BYTE
attrval_in [] = "=\"",
attrval_out[] = "\"";
- wxVERIFY(::CryptHashData(hash, attrval_in, _countof(attrval_in) - 1, 0));
- wxVERIFY(::CryptHashData(hash, wxXmlEscapeAttr(value), 0));
- wxVERIFY(::CryptHashData(hash, attrval_out, _countof(attrval_out) - 1, 0));
+ wxCHECK(hash.Hash(attrval_in, _countof(attrval_in) - 1), false);
+ wxCHECK(hash.HashAsUTF8(wxXmlEscapeAttr(value)), false);
+ wxCHECK(hash.Hash(attrval_out, _countof(attrval_out) - 1), false);
}
}
}
@@ -65,21 +58,21 @@ void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node)
elemclose_in[] = "";
// Hash the open tag closing.
- wxVERIFY(::CryptHashData(hash, element_out, _countof(element_out) - 1, 0));
+ wxCHECK(hash.Hash(element_out, _countof(element_out) - 1), false);
// Hash the children.
for (; child; child = child->GetNext())
- wxXmlHashNode(hash, child);
+ wxCHECK(wxXmlHashNode(hash, child), false);
// Hash the closing tag.
- wxVERIFY(::CryptHashData(hash, elemclose_in, _countof(elemclose_in) - 1, 0));
- wxVERIFY(::CryptHashData(hash, node->GetName(), 0));
- wxVERIFY(::CryptHashData(hash, element_out, _countof(element_out) - 1, 0));
+ wxCHECK(hash.Hash(elemclose_in, _countof(elemclose_in) - 1), false);
+ wxCHECK(hash.HashAsUTF8(node->GetName()), false);
+ wxCHECK(hash.Hash(element_out, _countof(element_out) - 1), false);
} else {
static const BYTE element_out [] = "/>";
// Hash the childless element tag closing.
- wxVERIFY(::CryptHashData(hash, element_out, _countof(element_out) - 1, 0));
+ wxCHECK(hash.Hash(element_out, _countof(element_out) - 1), false);
}
break;
@@ -87,7 +80,7 @@ void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node)
case wxXML_TEXT_NODE:
{
- wxVERIFY(::CryptHashData(hash, wxXmlEscapeText(node->GetContent()), 0));
+ wxCHECK(hash.HashAsUTF8(wxXmlEscapeText(node->GetContent())), false);
break;
}
@@ -97,16 +90,16 @@ void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node)
cdata_in [] = "";
- wxVERIFY(::CryptHashData(hash, cdata_in, _countof(cdata_in) - 1, 0));
- wxVERIFY(::CryptHashData(hash, node->GetContent(), 0));
- wxVERIFY(::CryptHashData(hash, cdata_out, _countof(cdata_out) - 1, 0));
+ wxCHECK(hash.Hash(cdata_in, _countof(cdata_in) - 1), false);
+ wxCHECK(hash.HashAsUTF8(node->GetContent()), false);
+ wxCHECK(hash.Hash(cdata_out, _countof(cdata_out) - 1), false);
break;
}
case wxXML_COMMENT_NODE:
{
- wxVERIFY(::CryptHashData(hash, node->GetContent(), 0));
+ wxCHECK(hash.HashAsUTF8(node->GetContent()), false);
break;
}
@@ -114,12 +107,15 @@ void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node)
{
// Hash the children.
for (wxXmlNode *child = node->GetChildren(); child; child = child->GetNext())
- wxXmlHashNode(hash, child);
+ wxCHECK(wxXmlHashNode(hash, child), false);
break;
}
default:
wxFAIL_MSG(wxT("unsupported XML node type"));
+ return false;
}
+
+ return true;
}