将带有 ANSI 字符的 SecureString 转换为非托管字符串

Convert SecureString with ANSI-character to unmanaged string

我正在尝试将 SecureString 的内容直接复制到非托管内存中。 Microsoft 建议使用 Marshal.SecureStringToBSTR()Marshal.SecureStringToGlobalAllocAnsi()Marshal.SecureStringToGlobalAllocUnicode() 以及相应的 COM 使用方法。

我有一个 SecureString,其中包含字符“127”和“128”。因为 ANSI 使用 8 位编码,所以我希望以下转换为非托管字符串会起作用:

  var secureString = new SecureString();
  secureString.AppendChar((char)127);
  secureString.AppendChar((char)128);

  IntPtr unmanagedString = Marshal.SecureStringToGlobalAllocAnsi(secureString);
  var b1 = Marshal.ReadByte(unmanagedString, 0); // 127
  var b2 = Marshal.ReadByte(unmanagedString, 1); // 63

但是“128”的结果是“63”。 我可以使用 SecureStringToBSTR()SecureStringToGlobalAllocUnicode() 并省略所有其他字节。这行得通。但我想了解为什么 ANSI 方法 returns '63' 而不是 '128'.

我使用 C# 和 .Net Framework 4.8。

字符串是 char 的序列,而不是字节。 (char)128 表示 Unicode codepoint U+0080,它在您机器的 ANSI 代码页的任何代码页中都没有表示,因此您得到的是问号 (ANSI 63)。
这与 SecureStrings 无关。使用 Encoding.GetBytes/Encoding.GetString.

会得到相同的结果

请确保您在继续之前访问过 this


如果您确定您的安全字符串将只包含低于 256 的代码点,并且您希望这些代码点在 ANSI 中的往返表示,那么您可以安排 using codepage 28591:

public static byte[] SecureStringToRoundtripAnsiByteArray(SecureString secureString)
{
    var buffer_ptr = Marshal.SecureStringToCoTaskMemUnicode(secureString);

    try
    {
        byte[] buffer = new byte[secureString.Length * sizeof(char)];
        Marshal.Copy(buffer_ptr, buffer, 0, buffer.Length);

        try
        {
            return System.Text.Encoding.Convert(System.Text.Encoding.Unicode, System.Text.Encoding.GetEncoding(28591), buffer);
        }
        finally
        {
            Array.Clear(buffer, 0, buffer.Length);
        }
    }
    finally
    {
        Marshal.ZeroFreeCoTaskMemUnicode(buffer_ptr);
    }
}
using (var secureString = new SecureString())
{
    for (int i = 0; i < 256; i++)
        secureString.AppendChar((char)i);

    secureString.MakeReadOnly();

    byte[] b = SecureStringToRoundtripAnsiByteArray(secureString);

    for (int i = 0; i < 256; i++)
        Console.WriteLine(b[i]);
}