telandor
telandor

Reputation: 869

Convert SecureString with ANSI-character to unmanaged string

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.

Upvotes: 0

Views: 284

Answers (1)

GSerg
GSerg

Reputation: 78185

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

Upvotes: 1

Related Questions