0

I am trying to copy the content of a SecureString directly into unmanaged memory. Microsoft recommends using Marshal.SecureStringToBSTR(), Marshal.SecureStringToGlobalAllocAnsi(), Marshal.SecureStringToGlobalAllocUnicode() and the corresponding methods for COM usage.

I have a SecureString with the chars '127' and '128'. Because ANSI uses 8 bit encoding I would expect that the following conversion into an unmanaged string would work:

  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

But the '128' results in '63'. I could use SecureStringToBSTR() or SecureStringToGlobalAllocUnicode() and omit every other byte. This would work. But I would like to understand why the ANSI method returns '63' instead of '128'.

I use C# with .Net Framework 4.8.

telandor
  • 869
  • 9
  • 28
  • You are converting Unicode [codepoint U+0080](https://www.fileformat.info/info/unicode/char/0080/index.htm) to ANSI. It does not exist there, so you get the question mark which is the substitute for unknown characters. This has nothing to do with secure strings, you would get the same result with `GetBytes`/`GetString`. – GSerg Sep 02 '20 at 12:04
  • [SecureString shouldn't be used](https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md) – Pavel Anikhouski Sep 02 '20 at 12:04

1 Answers1

1

Strings are sequences of chars, not bytes. (char)128 represents the Unicode codepoint U+0080 which has no representation in whatever code page is your machine's ANSI code page, so you get the question mark (ANSI 63) instead.
This has nothing to do with SecureStrings. You would get the same result with Encoding.GetBytes/Encoding.GetString.


Please make sure you have visited this before you proceed.


If you are certain that your secure string will only contain codepoints below 256, and you want roundtrip representation of such codepoints in ANSI, then you can arrange that 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]);
}
GSerg
  • 76,472
  • 17
  • 159
  • 346