WPF的PasswordBox中的SecureString与手动构建的SecureString不同

时间:2018-05-06 18:22:24

标签: c# wpf encryption

我有两个应用程序:

  1. 一个WPF应用程序,它通过PasswordBox元素捕获密码并使用它加密C#字符串,
  2. 一个ASP.NET应用程序,它通过明文配置接收相同的密码,并用它来解密来自#1的字符串。
  3. WPF应用程序能够很好地解密字符串,而ASP.NET应用程序则不能。我已经确保两个应用程序中加密/解密中使用的明文和盐是相同的。

    以下是两个应用共享的相关代码:

    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...
    }
    

    这里没有错误:两个函数中显示的行确实相同,因为它们应该是恕我直言。

    然而,lengthInBytes内的Decrypt变量因应用而异。它在ASP.NET(它无法解密)下等于8,在WPF下等于4(两个函数都正常工作)。测试密码明文是“测试”,如果重要的话。

    我没有展示其余代码的原因是因为它并不重要。很明显,当存在这种差异时,它不可能正常工作,所以这就是我所关注的。

    WPF应用程序直接从SecureString属性捕获PasswordBox.SecurePassword。 OTOH,ASP.NET通过在新的AppendChar实例的循环中调用SecureString来构建它。我知道这不是那么安全,但是这种情况没有任何可行的非交互式替代方案。虽然这不是重点,但我怀疑它可能是问题的一部分。我只是不知道如何。

    我错过了什么?

    FWIW,WPF应用程序目前正在其入口可执行文件中使用Obfuscar工具。我还没有尝试禁用它,看它是否有任何区别。

    更新:以下是我在ASP.NET端使用的代码,用于从明文SecureString创建System.String

    using (var secure = new SecureString())
    {
        for (var i = 0; i < plaintext.Length; i++)
        {
            secure.AppendChar(plaintext[i]);
        }
    
        return Decrypt(encrypted, secure, salt);
    }
    

1 个答案:

答案 0 :(得分:2)

您错误地使用Marshal.SizeOf尝试获取字符串长度,但这将返回IntPtr结构大小。此外,我建议使用unicode编码,以避免因系统编码而对数据进行任何更改:

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);
    }
}

永远记住将内存归零并释放它,否则SecureString的目的就会失败。