Reputation: 2272
I have a c++ application (32 bit) that will take an entered string, encrypt it using a code block named WriteProtectedStringValueToRegistry
LONG WriteProtectedStringValueToRegistry(LPCTSTR subKey, LPCTSTR valueName, LPCTSTR value)
{size_t len = strlen( value );
if (!subKey || !valueName)
return ERROR_INVALID_DATA;
LONG result = 0;
DWORD keyCreationResult = 0;
HKEY newKey;
// Create a new key or open existing key.
result = RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
subKey,
0,
NULL,
0,
KEY_ALL_ACCESS,
NULL,
&newKey,
&keyCreationResult);
if (ERROR_SUCCESS != result)
{
return result;
}
if (keyCreationResult == REG_OPENED_EXISTING_KEY)
{
WriteLog("Opened existing key '%s'\n", subKey);
}
else
{
WriteLog("Created new key '%s'\n", subKey);
}
DATA_BLOB unencryptedData, encryptedData;
unencryptedData.pbData = (BYTE *)value;
// Save the NULL character in the data
// We need to multiply the length of the string by the
// size of the data contained therein to support multi-
// byte character sets.
unencryptedData.cbData = (len + 1) * sizeof(*value);
if (!CryptProtectData(
&unencryptedData,
L"My Encrypted Data",
NULL,
NULL,
NULL,
0,
&encryptedData))
{
RegCloseKey(newKey);
return GetLastError();
}
// OK, so now we can save the data to the registry.
result = RegSetValueEx(
newKey,
valueName,
0,
REG_BINARY,
encryptedData.pbData,
encryptedData.cbData);
// Free the encrypted data buffer
LocalFree(encryptedData.pbData);
RegCloseKey(newKey);
return result;
}
Now - in another application (c#, built for any CPU) i am using the DPAPI class from Microsoft to decrypt the binary string read from the registry.
private void btnRead_Click(object sender, EventArgs e)
{
try
{
RegistryKey rKey1 = Registry.LocalMachine;
rKey1 = rKey1.OpenSubKey(@"SOFTWARE\XXX\XXX\Credentials", true);
var value = (byte[])rKey1.GetValue("UserName");
var valueAsString = BitConverter.ToString(value);
string decrypted = DPAPI.Decrypt(EncodeTo64(valueAsString));
}
catch (Exception ex)
{
while (ex != null)
{
Console.WriteLine(ex.Message);
ex = ex.InnerException;
}
}
}
Using a test rig, i can confirm the data written to the registry can be encrypted and unencrypted in the c++ program, and using a different test rig, i can confirm the encrypt and decrypt is working in the c# application.
string xx = DPAPI.Encrypt("Administrator");
string yy = DPAPI.Decrypt(xx);
// works encrypt and decrypt
byte[] data;
data = Convert.FromBase64String(xx);
rKey2.SetValue("UserNamecsharp", data, RegistryValueKind.Binary);
byte[] value = (byte[])rKey1.GetValue("UserName");
var valueAsString = Convert.ToBase64String(value);
string decrypted = DPAPI.Decrypt(valueAsString);
It appears the starting values in the registry are the same, and running through the debugger, they appear to be very close - up until the four A's
valueAsString
"AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAesG+C/0ymUSov+q7G6U0rAAAAAAkAAAATQB5ACAARQBuAGMAcgB5AHAAdABlAGQAIABEAGEAdABhAAAAA2YAAKgAAAAQAAAAhMQNYP/ECV0uWNQJNwR0DQAAAAAEgAAAoAAAABAAAAAusIzWvKtWfIE25su1nBkWEAAAACWxuZ2lz12ON/uOafeqdfcUAAAAs7rYAvpeXoSH191clwcFXxmIA9M=" string
xx "AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA3lfKy0QLx0KeGBAy9xuuoAAAAAACAAAAAAADZgAAwAAAABAAAAA7y9SEsVpdsVoCO78Vlq+3AAAAAASAAACgAAAAEAAAAG1Ssj5xjVqBhCm2rK9oUtIQAAAA07Fyyoq6vK2OHJ1ygG4t8RQAAACZp6TmW2EBsu7kPVlf05D+jkVC7w==" string
Here is the registry contents for them...
"UserName"=hex:01,00,00,00,d0,8c,9d,df,01,15,d1,11,8c,7a,00,c0,4f,c2,97,eb,01,\ 00,00,00,7a,c1,be,0b,fd,32,99,44,a8,bf,ea,bb,1b,a5,34,ac,00,00,00,00,24,00,\ 00,00,4d,00,79,00,20,00,45,00,6e,00,63,00,72,00,79,00,70,00,74,00,65,00,64,\ 00,20,00,44,00,61,00,74,00,61,00,00,00,03,66,00,00,a8,00,00,00,10,00,00,00,\ 84,c4,0d,60,ff,c4,09,5d,2e,58,d4,09,37,04,74,0d,00,00,00,00,04,80,00,00,a0,\ 00,00,00,10,00,00,00,2e,b0,8c,d6,bc,ab,56,7c,81,36,e6,cb,b5,9c,19,16,10,00,\ 00,00,25,b1,b9,9d,a5,cf,5d,8e,37,fb,8e,69,f7,aa,75,f7,14,00,00,00,b3,ba,d8,\ 02,fa,5e,5e,84,87,d7,dd,5c,97,07,05,5f,19,88,03,d3
"UserNamecsharp"=hex:01,00,00,00,d0,8c,9d,df,01,15,d1,11,8c,7a,00,c0,4f,c2,97,eb,01,\ 00,00,00,de,57,ca,cb,44,0b,c7,42,9e,18,10,32,f7,1b,ae,a0,00,00,00,00,02,00,\ 00,00,00,00,03,66,00,00,c0,00,00,00,10,00,00,00,3b,cb,d4,84,b1,5a,5d,b1,5a,\ 02,3b,bf,15,96,af,b7,00,00,00,00,04,80,00,00,a0,00,00,00,10,00,00,00,6d,52,\ b2,3e,71,8d,5a,81,84,29,b6,ac,af,68,52,d2,10,00,00,00,d3,b1,72,ca,8a,ba,bc,\ ad,8e,1c,9d,72,80,6e,2d,f1,14,00,00,00,99,a7,a4,e6,5b,61,01,b2,ee,e4,3d,59,\ 5f,d3,90,fe,8e,45,42,ef
I must be missing something, using the same cypt32.dll routines, it appears the data should be read and written correctly, but the decryption is failing.
thanks for your help
Upvotes: 2
Views: 721
Reputation: 998
At first glance, it appears that you are using 8-bit char strings ("string") instead of unicode strings (L"blah" or wchar_t*) in your C++ code. C# uses Unicode for strings. That may be why you are getting different results. My first check would be to make sure that I'm passing the C++ strings as unicode.
Good luck, hope this helps. :)
Upvotes: 2