Reputation: 2352
I have two apps:
The WPF app is able to decrypt the string just fine, while the ASP.NET app isn't. I've made sure that plaintexts and salts used in encryption/decryption in both apps are the same.
Here's the relevant code, which is shared by both apps:
string Encrypt(string value, SecureString password, string salt)
{
var temp = Marshal.SecureStringToBSTR(password);
var lengthInBytes = Marshal.SizeOf(temp);
// The rest of the encryption code...
}
string Decrypt(string value, SecureString password, string salt)
{
var temp = Marshal.SecureStringToBSTR(password);
var lengthInBytes = Marshal.SizeOf(temp);
// The rest of the decryption code...
}
There's no mistake here: the shown lines in both functions are indeed the same, as they, IMHO, should be.
And yet, the lengthInBytes
variable inside the Decrypt
differs between apps. It equals 8 under ASP.NET (where it fails to decrypt) and 4 under WPF (where both functions work correctly). The test password plaintext was "test", if that matters.
The reason I'm not showing the rest of the code is because it doesn't really matter. It's obvious to me it can't possibly work while this difference exists, so this is what I'm focusing on.
The WPF app captures the SecureString
directly from the PasswordBox.SecurePassword
property. OTOH, the ASP.NET builds it by calling AppendChar
in a loop on a fresh SecureString
instance. I'm aware this isn't as secure, but there wasn't any viable non-interactive alternative for this scenario. While this is beside the point, I suspect it may be a part of the problem. I just don't see how, yet.
What have I missed?
FWIW, the WPF app is currently employing the Obfuscar
tool on its entry executable. I didn't yet try to disable this to see if it made any difference.
UPDATE: Here's the code I'm using on the ASP.NET side to create a SecureString
from a plaintext System.String
:
using (var secure = new SecureString())
{
for (var i = 0; i < plaintext.Length; i++)
{
secure.AppendChar(plaintext[i]);
}
return Decrypt(encrypted, secure, salt);
}
Upvotes: 0
Views: 429
Reputation: 15161
You are incorrectly using Marshal.SizeOf
to try to get the string length but that will return the IntPtr
structure size. Also, I would recommend to use unicode encoding to avoid any change on the data because of the system's encoding:
string Encrypt(string value, SecureString password, string salt)
{
try
{
var temp = Marshal.SecureStringToGlobalAllocUnicode(password);
var lengthInBytes = sizeof(char) * password.Length;
// The rest of the encription code...
}
finally
{
//Cleanup
Marshal.ZeroFreeGlobalAllocUnicode(temp);
}
}
string Decrypt(string value, SecureString password, string salt)
{
try
{
var temp = Marshal.SecureStringToGlobalAllocUnicode(password);
var lengthInBytes = sizeof(char) * password.Length;
// The rest of the decryption code...
}
finally
{
//Cleanup
Marshal.ZeroFreeGlobalAllocUnicode(temp);
}
}
Always remember to zero the memory and free it or the SecureString purpose will be defeated.
Upvotes: 3