From 4ac895ffa2669aa8509b62e411e7c48422469612 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Thu, 19 May 2016 12:19:31 +0200 Subject: [PATCH] Additional asymmetric encryption introduced when storing passwords to Windows Credential Manager --- CredWrite/CredWrite.rc | Bin 4872 -> 5792 bytes CredWrite/Main.cpp | 94 +++++++++++++++++++++++++++++------------ CredWrite/StdAfx.h | 1 + Makefile | 16 +++++++ include/KeyPrivate.bin | Bin 0 -> 1191 bytes include/KeyPublic.bin | Bin 0 -> 294 bytes lib/WinStd | 2 +- 7 files changed, 85 insertions(+), 28 deletions(-) create mode 100644 include/KeyPrivate.bin create mode 100644 include/KeyPublic.bin diff --git a/CredWrite/CredWrite.rc b/CredWrite/CredWrite.rc index c4aed2aeb642a4387c443bb8397b12a4a2207836..ea38b9fdbbb1db3ea530edbf9734612f16d69f96 100644 GIT binary patch delta 206 zcmeBBTcEqaj-A(!A(f$&p@gA`A(0_xa-fjpj(zhru2b9qAQ1%F diff --git a/CredWrite/Main.cpp b/CredWrite/Main.cpp index 6c30217..57387ee 100644 --- a/CredWrite/Main.cpp +++ b/CredWrite/Main.cpp @@ -20,6 +20,8 @@ #include "StdAfx.h" +#pragma comment(lib, "Crypt32.lib") + using namespace std; using namespace winstd; @@ -51,45 +53,83 @@ static int CredWrite() assert(target_name.length() < CRED_MAX_GENERIC_TARGET_NAME_LENGTH); // Prepare password. - string password_enc_utf8; + string password_enc; { // Convert Base64 >> UTF-8. - sanitizing_vector password_utf8; + sanitizing_vector password; base64_dec dec; bool is_last; - dec.decode(password_utf8, is_last, pwcArglist[2], (size_t)-1); + dec.decode(password, is_last, pwcArglist[2], (size_t)-1); - // Convert UTF-8 >> UTF-16. - sanitizing_wstring password; - MultiByteToWideChar(CP_UTF8, 0, password_utf8.data(), (int)password_utf8.size(), password); - - // Encrypt the password. - wstring password_enc; - CRED_PROTECTION_TYPE cpt; - if (!CredProtect(TRUE, password.data(), (DWORD)password.size(), password_enc, &cpt)) { - OutputDebugStr(_T("CredProtect failed (error %u).\n"), GetLastError()); + // Prepare cryptographics provider. + crypt_prov cp; + if (!cp.create(NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { + OutputDebugStr(_T("CryptAcquireContext failed (error %u).\n"), GetLastError()); return 2; } - // Convert UTF-16 >> UTF-8. - WideCharToMultiByte(CP_UTF8, 0, password_enc.data(), (int)password_enc.size(), password_enc_utf8, NULL, NULL); + // Import the public key. + HRSRC res = FindResource(NULL, MAKEINTRESOURCE(1), RT_RCDATA); + assert(res); + HGLOBAL res_handle = LoadResource(NULL, res); + assert(res_handle); + crypt_key key; + unique_ptr > keyinfo_data; + DWORD keyinfo_size = 0; + if (!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) || + !key.import_public(cp, X509_ASN_ENCODING, keyinfo_data.get())) + { + OutputDebugStr(_T("Public key import failed (error %u).\n"), GetLastError()); + return 2; + } + + // Pre-allocate memory to allow space, as encryption will grow the data, and we need additional 16B at the end for MD5 hash. + DWORD dwBlockLen; + if (!CryptGetKeyParam(key, KP_BLOCKLEN, dwBlockLen, 0)) dwBlockLen = 0; + password.reserve((password.size() + dwBlockLen - 1) / dwBlockLen * dwBlockLen + 16); + + // Encrypt the password using our public key. Calculate MD5 hash and append it. + crypt_hash hash; + if (!hash.create(cp, CALG_MD5)) { + OutputDebugStr(_T("Creating MD5 hash failed (error %u).\n"), GetLastError()); + return 2; + } + if (!CryptEncrypt(key, hash, TRUE, 0, password)) { + OutputDebugStr(_T("Encrypting password failed (error %u).\n"), GetLastError()); + return 2; + } + vector hash_bin; + CryptGetHashParam(hash, HP_HASHVAL, hash_bin, 0); + password.insert(password.end(), hash_bin.begin(), hash_bin.end()); + + // Convert encrypted password to Base64, since CredProtectA() fail for binary strings. + string password_base64; + base64_enc enc; + enc.encode(password_base64, password.data(), password.size()); + + // Encrypt the password using user's key. + CRED_PROTECTION_TYPE cpt; + if (!CredProtectA(TRUE, password_base64.c_str(), (DWORD)password_base64.length(), password_enc, &cpt)) { + OutputDebugStr(_T("CredProtect failed (error %u).\n"), GetLastError()); + return 2; + } } - assert(password_enc_utf8.size()*sizeof(char) < CRED_MAX_CREDENTIAL_BLOB_SIZE); + assert(password_enc.size()*sizeof(char) < CRED_MAX_CREDENTIAL_BLOB_SIZE); // Write credentials. CREDENTIAL cred = { - 0, // Flags - CRED_TYPE_GENERIC, // Type - (LPWSTR)target_name.c_str(), // TargetName - _T(""), // Comment - { 0, 0 }, // LastWritten - (DWORD)password_enc_utf8.size()*sizeof(char), // CredentialBlobSize - (LPBYTE)password_enc_utf8.data(), // CredentialBlob - CRED_PERSIST_ENTERPRISE, // Persist - 0, // AttributeCount - NULL, // Attributes - NULL, // TargetAlias - pwcArglist[1] // UserName + 0, // Flags + CRED_TYPE_GENERIC, // Type + (LPWSTR)target_name.c_str(), // TargetName + _T(""), // Comment + { 0, 0 }, // LastWritten + (DWORD)password_enc.size()*sizeof(char), // CredentialBlobSize + (LPBYTE)password_enc.data(), // CredentialBlob + CRED_PERSIST_ENTERPRISE, // Persist + 0, // AttributeCount + NULL, // Attributes + NULL, // TargetAlias + pwcArglist[1] // UserName }; if (!CredWrite(&cred, 0)) { OutputDebugStr(_T("CredWrite failed (error %u).\n"), GetLastError()); diff --git a/CredWrite/StdAfx.h b/CredWrite/StdAfx.h index a98e00e..027561c 100644 --- a/CredWrite/StdAfx.h +++ b/CredWrite/StdAfx.h @@ -24,6 +24,7 @@ #include #include +#include #include #include diff --git a/Makefile b/Makefile index 7e555e6..a5226c4 100644 --- a/Makefile +++ b/Makefile @@ -81,6 +81,22 @@ Publish :: "MSI\MSIBuild\Version\Version.mak" $(MAKE) /f "Makefile" /$(MAKEFLAGS) Version cd "$(MAKEDIR)" +GenRSAKeypair :: \ + "include\KeyPrivate.bin" \ + "include\KeyPublic.bin" + +"include\KeyPrivate.bin" : + if exist $@ del /f /q $@ + if exist "$(@:"=).tmp" del /f /q "$(@:"=).tmp" + openssl.exe genrsa 2048 | openssl.exe rsa -inform PEM -outform DER -out "$(@:"=).tmp" + move /y "$(@:"=).tmp" $@ > NUL + +"include\KeyPublic.bin" : "include\KeyPrivate.bin" + if exist $@ del /f /q $@ + if exist "$(@:"=).tmp" del /f /q "$(@:"=).tmp" + openssl.exe rsa -in $** -inform DER -outform DER -out "$(@:"=).tmp" -pubout + move /y "$(@:"=).tmp" $@ > NUL + !ELSE ###################################################################### diff --git a/include/KeyPrivate.bin b/include/KeyPrivate.bin new file mode 100644 index 0000000000000000000000000000000000000000..445532b46c5f52f139a9201e28620c4c45e86bd9 GIT binary patch literal 1191 zcmV;Y1X%kpf&`-i0RRGm0RaHDa6=j$E1{lh9ISv#6|qamo5l1ft!y1*u2MK`MM|adxCiz6W(;BOW z7oaMhRn6WHOiR>u6GyiK0|5X50)hbn0EG|Qwn-FOhx05dIzU#PTLrmr7@Ejzl(-#e zwJ*EI+;X6|l`DTJ?Af?FJiDoKBKt@Pg}OM78bNla?_|V-PPZBlRHCQh8EJ&XpdNzb z{$3^4W(~lskYaGS9tygC0NHu<{*G$Zwjy;wQ-juMzbEMespZ42iti=2 zXF|$wXVY}lNi5VkuHu|7*ny)fn|il*xtBgDBi6;{Y@sZ(RC~f!HZ^n^flU#RP4ac* z-DesL@4gxgV*ArM%muV}MpO?Cl=taNYpG>plC&WXifg8--?GFYXktHY`1{NMPXd8~ z0M(QckDQ%%v>6Jm!Ce**Xkbse&Vc7_g0>m{M+4*nNs~8oT6KYb-OktZ(y3=i6-{=) zb`Q&K>S63x?*8ECAY1NK7$`O~nedSmJ^(Y!^vIO>3OKxBY>Y4aMG+0`(jpB zTIUv@_(%3wOg*bd^V0q{>N*b|bLoWF83KWT17lbQz&AhN7l>9JGxwS(C^`fcn}fmY zUNCDs3z=2O_)TD?M>2yqi$*=5m)vjYdoS{cjHjkPaIqqm+^6!NMN&||g>$Nw4jNGM zHSNkn{J=ILWe3RHo$9-S$1nMhx$TAy1XzaD~o9 zt?Jf_gwp#oAk=D#L_6|F5tUhJL?WOEjTTt~fq+lw$C~acf%Y!}ak|^^k!uLqD=Qse z*a?hrMBcBiC9Kk+#c$a5n#V_L)u0h%@w+8E18Z;Bcm0uZ;OWnK3l3M9*Fuj|QFqxX zp6y92H`U5p#Z-Sf#} FYYG62Nc#W) literal 0 HcmV?d00001 diff --git a/include/KeyPublic.bin b/include/KeyPublic.bin new file mode 100644 index 0000000000000000000000000000000000000000..a0f9dec4c413fbdf6e5edf0ec5f754a08f9eb36c GIT binary patch literal 294 zcmV+>0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>lwQxfk9V?-pY886QQkB9LC@?pQ;+vR91^v6Tqw*W$VdAf=j96buDp3p1RMp%k*!k-4 z#0I}qg{vYGFf9K@@SOZKux6i3oHZE*ukWC9EDplQ3tkaGti&g4!%m(;R|5O+VBVkE z+ToOcw63h3`w4j@4zMDul&vz6tuXIxCa8K!P{bgk)BoB2yjqCBZR4S>B_hw6U(m#% s=Bj`(F$W2N+DCWPQYQICyVDx0h8LhJo>k4>4@^tcb`wXp0s{d60UkSuO8@`> literal 0 HcmV?d00001 diff --git a/lib/WinStd b/lib/WinStd index f915078..03cb7ee 160000 --- a/lib/WinStd +++ b/lib/WinStd @@ -1 +1 @@ -Subproject commit f91507801b9bb0a51f70806478d16b133eec261d +Subproject commit 03cb7ee822826b30f18d9e0fc0a71583999e6b12