diff --git a/lib/TLS/include/Credentials.h b/lib/TLS/include/Credentials.h index 1c49d96..2979361 100644 --- a/lib/TLS/include/Credentials.h +++ b/lib/TLS/include/Credentials.h @@ -59,6 +59,9 @@ namespace eapserial #pragma once #include "../../EAPBase/include/Credentials.h" +#include "../../EAPBase/include/EAPSerial.h" + +#include #include #include @@ -190,7 +193,7 @@ namespace eap /// @} public: - std::vector m_cert_hash; ///< Client certificate hash (certificates are kept in Personal Certificate Storage) + winstd::cert_context m_cert; ///< Client certificate }; } @@ -199,22 +202,30 @@ namespace eapserial { inline void pack(_Inout_ unsigned char *&cursor, _In_ const eap::credentials_tls &val) { - pack(cursor, (const eap::credentials&)val); - pack(cursor, val.m_cert_hash ); + // Don't save m_identity. We rebuild it on every load. + //pack(cursor, (const eap::credentials&)val); + pack(cursor, val.m_cert ); } inline size_t get_pk_size(const eap::credentials_tls &val) { return - get_pk_size((const eap::credentials&)val) + - get_pk_size(val.m_cert_hash ); + // Don't save m_identity. We rebuild it on every load. + //get_pk_size((const eap::credentials&)val) + + get_pk_size(val.m_cert ); } inline void unpack(_Inout_ const unsigned char *&cursor, _Out_ eap::credentials_tls &val) { - unpack(cursor, (eap::credentials&)val); - unpack(cursor, val.m_cert_hash ); + // Don't load m_identity. We rebuild it on load. + //unpack(cursor, (eap::credentials&)val); + unpack(cursor, val.m_cert ); + + if (val.m_cert) { + // Generate identity. TODO: Find which CERT_NAME_... constant returns valid identity (username@domain or DOMAIN\Username). + CertGetNameString(val.m_cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, val.m_identity); + } } } diff --git a/lib/TLS/src/Config.cpp b/lib/TLS/src/Config.cpp index e342f2b..0396a1b 100644 --- a/lib/TLS/src/Config.cpp +++ b/lib/TLS/src/Config.cpp @@ -156,13 +156,23 @@ bool eap::config_tls::load(_In_ IXMLDOMNode *pConfigRoot, _Out_ EAP_ERROR **ppEa com_obj pXmlElCA; pXmlListCAs->get_item(j, &pXmlElCA); bstr bstrFormat; - if (eapxml::get_element_value(pXmlElCA, bstr(L"eap-metadata:format"), &bstrFormat) == ERROR_SUCCESS) { - if (CompareStringEx(LOCALE_NAME_INVARIANT, NORM_IGNORECASE, bstrFormat, bstrFormat.length(), L"PEM", -1, NULL, NULL, 0) == CSTR_EQUAL) { - vector aData; - if (eapxml::get_element_base64(pXmlElCA, bstr(L"eap-metadata:cert-data"), aData) == ERROR_SUCCESS) - add_trusted_ca(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, aData.data(), (DWORD)aData.size()); - } + if (eapxml::get_element_value(pXmlElCA, bstr(L"eap-metadata:format"), &bstrFormat) != ERROR_SUCCESS) { + // not specified. + continue; } + + if (CompareStringEx(LOCALE_NAME_INVARIANT, NORM_IGNORECASE, bstrFormat, bstrFormat.length(), L"PEM", -1, NULL, NULL, 0) != CSTR_EQUAL) { + // Certificate must be PEM encoded. + continue; + } + + vector aData; + if (eapxml::get_element_base64(pXmlElCA, bstr(L"eap-metadata:cert-data"), aData) != ERROR_SUCCESS) { + // Error reading element. + continue; + } + + add_trusted_ca(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, aData.data(), (DWORD)aData.size()); } } diff --git a/lib/TLS/src/Credentials.cpp b/lib/TLS/src/Credentials.cpp index 572fbbd..8baf826 100644 --- a/lib/TLS/src/Credentials.cpp +++ b/lib/TLS/src/Credentials.cpp @@ -34,14 +34,14 @@ eap::credentials_tls::credentials_tls(_In_ module &mod) : credentials(mod) eap::credentials_tls::credentials_tls(_In_ const credentials_tls &other) : - m_cert_hash(other.m_cert_hash), + m_cert(other.m_cert), credentials(other) { } eap::credentials_tls::credentials_tls(_Inout_ credentials_tls &&other) : - m_cert_hash(std::move(other.m_cert_hash)), + m_cert(std::move(other.m_cert)), credentials(std::move(other)) { } @@ -51,7 +51,7 @@ eap::credentials_tls& eap::credentials_tls::operator=(_In_ const credentials_tls { if (this != &other) { (credentials&)*this = other; - m_cert_hash = other.m_cert_hash; + m_cert = other.m_cert; } return *this; @@ -62,7 +62,7 @@ eap::credentials_tls& eap::credentials_tls::operator=(_Inout_ credentials_tls && { if (this != &other) { (credentials&)*this = std::move(other); - m_cert_hash = std::move(other.m_cert_hash); + m_cert = std::move(other.m_cert); } return *this; @@ -78,13 +78,13 @@ eap::config* eap::credentials_tls::clone() const void eap::credentials_tls::clear() { credentials::clear(); - m_cert_hash.clear(); + m_cert.free(); } bool eap::credentials_tls::empty() const { - return credentials::empty() && m_cert_hash.empty(); + return credentials::empty() && !m_cert; } @@ -92,13 +92,35 @@ bool eap::credentials_tls::save(_In_ IXMLDOMDocument *pDoc, _In_ IXMLDOMNode *pC { const bstr bstrNamespace(L"urn:ietf:params:xml:ns:yang:ietf-eap-metadata"); DWORD dwResult; + HRESULT hr; - if (!credentials::save(pDoc, pConfigRoot, ppEapError)) + // Don't save m_identity. We rebuild it on every load. + //if (!credentials::save(pDoc, pConfigRoot, ppEapError)) + // return false; + + // + com_obj pXmlElClientCertificate; + if ((dwResult = eapxml::create_element(pDoc, bstr(L"ClientCertificate"), bstrNamespace, &pXmlElClientCertificate))) { + *ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating element."), NULL); return false; + } - // - if ((dwResult = eapxml::put_element_hex(pDoc, pConfigRoot, bstr(L"CertHash"), bstrNamespace, m_cert_hash.data(), m_cert_hash.size())) != ERROR_SUCCESS) { - *ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating element."), NULL); + if (m_cert) { + // / + if ((dwResult = eapxml::put_element_value(pDoc, pXmlElClientCertificate, bstr(L"format"), bstrNamespace, bstr(L"PEM"))) != ERROR_SUCCESS) { + *ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating element."), NULL); + return false; + } + + // / + if ((dwResult = eapxml::put_element_base64(pDoc, pXmlElClientCertificate, bstr(L"cert-data"), bstrNamespace, m_cert->pbCertEncoded, m_cert->cbCertEncoded)) != ERROR_SUCCESS) { + *ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating element."), NULL); + return false; + } + } + + if (FAILED(hr = pConfigRoot->appendChild(pXmlElClientCertificate, NULL))) { + *ppEapError = m_module.make_error(HRESULT_CODE(hr), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error appending element."), NULL); return false; } @@ -111,15 +133,35 @@ bool eap::credentials_tls::load(_In_ IXMLDOMNode *pConfigRoot, _Out_ EAP_ERROR * assert(pConfigRoot); DWORD dwResult; - if (!credentials::load(pConfigRoot, ppEapError)) - return false; + // Don't load m_identity. We rebuild it on load. + //if (!credentials::load(pConfigRoot, ppEapError)) + // return false; - // - if ((dwResult = eapxml::get_element_hex(pConfigRoot, bstr(L"eap-metadata:CertHash"), m_cert_hash)) != ERROR_SUCCESS) { - *ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error reading element."), NULL); + m_identity.clear(); + m_cert.free(); + + // + com_obj pXmlElClientCertificate; + if ((dwResult = eapxml::select_element(pConfigRoot, bstr(L"eap-metadata:ClientCertificate"), &pXmlElClientCertificate)) != ERROR_SUCCESS) { + *ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error reading element."), NULL); return false; } + // / + bstr bstrFormat; + if ((dwResult = eapxml::get_element_value(pXmlElClientCertificate, bstr(L"eap-metadata:format"), &bstrFormat)) == ERROR_SUCCESS) { + if (CompareStringEx(LOCALE_NAME_INVARIANT, NORM_IGNORECASE, bstrFormat, bstrFormat.length(), L"PEM", -1, NULL, NULL, 0) == CSTR_EQUAL) { + // / + vector aData; + if ((dwResult = eapxml::get_element_base64(pXmlElClientCertificate, bstr(L"eap-metadata:cert-data"), aData)) == ERROR_SUCCESS) { + if (m_cert.create(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, aData.data(), (DWORD)aData.size())) { + // Generate identity. TODO: Find which CERT_NAME_... constant returns valid identity (username@domain or DOMAIN\Username). + CertGetNameString(m_cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, m_identity); + } + } + } + } + return true; } @@ -128,11 +170,36 @@ bool eap::credentials_tls::store(_In_ LPCTSTR pszTargetName, _Out_ EAP_ERROR **p { assert(pszTargetName); assert(ppEapError); + string cert_enc; + + // Prepare cryptographics provider. + crypt_prov cp; + if (!cp.create(NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { + *ppEapError = m_module.make_error(GetLastError(), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" CryptAcquireContext failed."), NULL); + return false; + } + + // Encrypt certificate. + vector cert; + if (!m_module.encrypt_md5(cp, m_cert->pbCertEncoded, m_cert->cbCertEncoded, cert, ppEapError)) + return false; + + // Convert encrypted certificate to Base64, since CredProtectA() fail for binary strings. + string cert_base64; + base64_enc enc; + enc.encode(cert_base64, cert.data(), cert.size()); + + // Encrypt the certificate using user's key. + CRED_PROTECTION_TYPE cpt; + if (!CredProtectA(TRUE, cert_base64.c_str(), (DWORD)cert_base64.length(), cert_enc, &cpt)) { + *ppEapError = m_module.make_error(GetLastError(), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" CredProtect failed."), NULL); + return false; + } tstring target(target_name(pszTargetName)); // Write credentials. - assert(m_cert_hash.size() < CRED_MAX_CREDENTIAL_BLOB_SIZE); + assert(cert_enc.size() < CRED_MAX_CREDENTIAL_BLOB_SIZE); assert(m_identity.length() < CRED_MAX_USERNAME_LENGTH ); CREDENTIAL cred = { 0, // Flags @@ -140,8 +207,8 @@ bool eap::credentials_tls::store(_In_ LPCTSTR pszTargetName, _Out_ EAP_ERROR **p (LPTSTR)target.c_str(), // TargetName _T(""), // Comment { 0, 0 }, // LastWritten - (DWORD)m_cert_hash.size(), // CredentialBlobSize - (LPBYTE)m_cert_hash.data(), // CredentialBlob + (DWORD)cert_enc.size(), // CredentialBlobSize + (LPBYTE)cert_enc.data(), // CredentialBlob CRED_PERSIST_ENTERPRISE, // Persist 0, // AttributeCount NULL, // Attributes @@ -159,7 +226,7 @@ bool eap::credentials_tls::store(_In_ LPCTSTR pszTargetName, _Out_ EAP_ERROR **p bool eap::credentials_tls::retrieve(_In_ LPCTSTR pszTargetName, _Out_ EAP_ERROR **ppEapError) { - assert(pszTargetName && _tcslen(pszTargetName) < CRED_MAX_GENERIC_TARGET_NAME_LENGTH); + assert(pszTargetName); // Read credentials. unique_ptr > cred; @@ -168,12 +235,38 @@ bool eap::credentials_tls::retrieve(_In_ LPCTSTR pszTargetName, _Out_ EAP_ERROR return false; } - if (cred->UserName) - m_identity = cred->UserName; - else - m_identity.clear(); + // Decrypt the certificate using user's key. + string cert_base64; + if (!CredUnprotectA(TRUE, (LPCSTR)(cred->CredentialBlob), cred->CredentialBlobSize, cert_base64)) { + *ppEapError = m_module.make_error(GetLastError(), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" CredUnprotect failed."), NULL); + return false; + } - m_cert_hash.assign(cred->CredentialBlob, cred->CredentialBlob + cred->CredentialBlobSize); + // Convert Base64 to binary encrypted certificate, since CredProtectA() fail for binary strings. + vector cert; + base64_dec dec; + bool is_last; + dec.decode(cert, is_last, cert_base64.c_str(), cert_base64.length()); + + // Prepare cryptographics provider. + crypt_prov cp; + if (!cp.create(NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { + *ppEapError = m_module.make_error(GetLastError(), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" CryptAcquireContext failed."), NULL); + return false; + } + + // Decrypt certificate. + vector > _cert; + if (!m_module.decrypt_md5(cp, cert.data(), cert.size(), _cert, ppEapError)) + return false; + + if (!m_cert.create(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, _cert.data(), (DWORD)_cert.size())) { + *ppEapError = m_module.make_error(GetLastError(), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error loading certificate."), NULL); + return false; + } + + // Generate identity. TODO: Find which CERT_NAME_... constant returns valid identity (username@domain or DOMAIN\Username). + CertGetNameString(m_cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, m_identity); return true; } diff --git a/lib/TLS_UI/include/TLS_UI.h b/lib/TLS_UI/include/TLS_UI.h index 8407018..d1c8c4a 100644 --- a/lib/TLS_UI/include/TLS_UI.h +++ b/lib/TLS_UI/include/TLS_UI.h @@ -33,11 +33,6 @@ /// class wxCertificateClientData; -/// -/// Helper class for auto-destroyable certificates used in wxWidget's item containers -/// -class wxCertificateSelectionClientData; - /// /// Validator for host name /// @@ -116,45 +111,6 @@ public: }; -class wxCertificateSelectionClientData : public wxClientData -{ -public: - /// - /// Default constructor - /// - wxCertificateSelectionClientData(); - - /// - /// Constructs client data object - /// - wxCertificateSelectionClientData(const wchar_t *identity, unsigned char *hash, size_t hash_size); - - /// - /// Constructs client data object with copy - /// - wxCertificateSelectionClientData(const std::wstring &identity, const std::vector &hash); - - /// - /// Constructs client data object with move - /// - wxCertificateSelectionClientData(std::wstring &&identity, std::vector &&hash); - - /// - /// Constructs client data object with copy - /// - wxCertificateSelectionClientData(const wxCertificateSelectionClientData &other); - - /// - /// Constructs client data object with move - /// - wxCertificateSelectionClientData(wxCertificateSelectionClientData &&other); - -public: - std::wstring m_identity; ///< Client identity - std::vector m_hash; ///< Client certificate hash (certificates are kept in Personal Certificate Storage) -}; - - class wxHostNameValidator : public wxValidator { wxDECLARE_DYNAMIC_CLASS(wxHostNameValidator); diff --git a/lib/TLS_UI/src/TLS_UI.cpp b/lib/TLS_UI/src/TLS_UI.cpp index 6711b43..b82a9f2 100644 --- a/lib/TLS_UI/src/TLS_UI.cpp +++ b/lib/TLS_UI/src/TLS_UI.cpp @@ -76,50 +76,6 @@ wxCertificateClientData::~wxCertificateClientData() } -////////////////////////////////////////////////////////////////////// -// wxCertificateSelectionClientData -////////////////////////////////////////////////////////////////////// - -wxCertificateSelectionClientData::wxCertificateSelectionClientData() -{ -} - - -wxCertificateSelectionClientData::wxCertificateSelectionClientData(const wchar_t *identity, unsigned char *hash, size_t hash_size) : - m_identity(identity), - m_hash(hash, hash + hash_size) -{ -} - - -wxCertificateSelectionClientData::wxCertificateSelectionClientData(const std::wstring &identity, const std::vector &hash) : - m_identity(identity), - m_hash(hash) -{ -} - - -wxCertificateSelectionClientData::wxCertificateSelectionClientData(std::wstring &&identity, std::vector &&hash) : - m_identity(std::move(identity)), - m_hash(std::move(hash)) -{ -} - - -wxCertificateSelectionClientData::wxCertificateSelectionClientData(const wxCertificateSelectionClientData &other) : - m_identity(other.m_identity), - m_hash(other.m_hash) -{ -} - - -wxCertificateSelectionClientData::wxCertificateSelectionClientData(wxCertificateSelectionClientData &&other) : - m_identity(std::move(other.m_identity)), - m_hash(std::move(other.m_hash)) -{ -} - - ////////////////////////////////////////////////////////////////////// // wxHostNameValidator ////////////////////////////////////////////////////////////////////// @@ -417,13 +373,16 @@ bool wxEAPTLSCredentialsPanel::TransferDataToWindow() } // Prepare certificate information. - std::unique_ptr data(new wxCertificateSelectionClientData); - eap::get_cert_title(cert, data->m_identity); + std::unique_ptr data(new wxCertificateClientData(CertDuplicateCertificateContext(cert))); // Add to list. - CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID, data->m_hash); - bool is_selected = data->m_hash == m_cred.m_cert_hash; - int i = m_cert_select_val->Append(data->m_identity, data.release()); + bool is_selected = + m_cred.m_cert && + m_cred.m_cert->cbCertEncoded == data->m_cert->cbCertEncoded && + memcmp(m_cred.m_cert->pbCertEncoded, data->m_cert->pbCertEncoded, m_cred.m_cert->cbCertEncoded) == 0; + winstd::tstring name; + eap::get_cert_title(cert, name); + int i = m_cert_select_val->Append(name, data.release()); if (is_selected) { m_cert_select_val->SetSelection(i); is_found = true; @@ -450,10 +409,12 @@ bool wxEAPTLSCredentialsPanel::TransferDataFromWindow() if (m_cert_none->GetValue()) m_cred.clear(); else { - const wxCertificateSelectionClientData *data = dynamic_cast(m_cert_select_val->GetClientObject(m_cert_select_val->GetSelection())); + const wxCertificateClientData *data = dynamic_cast(m_cert_select_val->GetClientObject(m_cert_select_val->GetSelection())); if (data) { - m_cred.m_identity = data->m_identity; - m_cred.m_cert_hash = data->m_hash; + m_cred.m_cert.attach_duplicated(data->m_cert); + + // Generate identity. TODO: Find which CERT_NAME_... constant returns valid identity (username@domain or DOMAIN\Username). + CertGetNameString(m_cred.m_cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, m_cred.m_identity); } else m_cred.clear(); }