C# 将 SecureString 安全地转换为 UTF-8 byte[]

标签 c# character-encoding marshalling securestring

我正在尝试将 SecureString 转换为 byte[] 的形式,我可以保持 GC 固定,并以 UTF-8 格式编码。我已经成功地做到了这一点,但使用的是 UTF-16 (默认编码),但我不知道如何进行编码转换,而 GC 没有机会在某处创建数据的托管副本(数据需要确保安全)。

这是我到目前为止所掌握的内容(上下文:计算 SecureString 哈希值的算法)

public static byte[] Hash(this SecureString secureString, HashAlgorithm hashAlgorithm)
{
  IntPtr bstr = Marshal.SecureStringToBSTR(secureString);
  int length = Marshal.ReadInt32(bstr, -4);
  var utf16Bytes = new byte[length];
  GCHandle utf16BytesPin = GCHandle.Alloc(utf16Bytes, GCHandleType.Pinned);
  byte[] utf8Bytes = null;

  try
  {
    Marshal.Copy(bstr, utf16Bytes, 0, length);
    Marshal.ZeroFreeBSTR(bstr);
    // At this point I have the UTF-16 byte[] perfectly.
    // The next line works at converting the encoding, but it does nothing
    // to protect the data from being spread throughout memory.
    utf8Bytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, utf16Bytes);
    return hashAlgorithm.ComputeHash(utf8Bytes);
  }
  finally
  {
    if (utf8Bytes != null)
    {
      for (var i = 0; i < utf8Bytes.Length; i++)
      { 
        utf8Bytes[i] = 0;
      }
    }
    for (var i = 0; i < utf16Bytes.Length; i++)
    { 
      utf16Bytes[i] = 0;
    }
    utf16BytesPin.Free();
  }
}

进行此转换的最佳方法是什么?我是否尝试在正确的位置进行转换,或者我应该以某种方式提前进行转换?通过完全跳过 UTF-16 byte[] 步骤可以提高内存效率吗?

最佳答案

我找到了一种按照我想要的方式做到这一点的方法。我这里的代码尚未完成(在失败的情况下需要更好的异常处理和内存管理),但它是:

[DllImport("kernel32.dll")]
static extern void RtlZeroMemory(IntPtr dst, int length);

public unsafe static byte[] HashNew(this SecureString secureString, HashAlgorithm hashAlgorithm)
{
  IntPtr bstr = Marshal.SecureStringToBSTR(secureString);
  int maxUtf8BytesCount = Encoding.UTF8.GetMaxByteCount(secureString.Length);
  IntPtr utf8Buffer = Marshal.AllocHGlobal(maxUtf8BytesCount);

  // Here's the magic:
  char* utf16CharsPtr = (char*)bstr.ToPointer();
  byte* utf8BytesPtr  = (byte*)utf8Buffer.ToPointer();
  int utf8BytesCount = Encoding.UTF8.GetBytes(utf16CharsPtr, secureString.Length, utf8BytesPtr, maxUtf8BytesCount);

  Marshal.ZeroFreeBSTR(bstr);
  var utf8Bytes = new byte[utf8BytesCount];
  GCHandle utf8BytesPin = GCHandle.Alloc(utf8Bytes, GCHandleType.Pinned);
  Marshal.Copy(utf8Buffer, utf8Bytes, 0, utf8BytesCount);
  RtlZeroMemory(utf8Buffer, utf8BytesCount);
  Marshal.FreeHGlobal(utf8Buffer);
  try
  {
    return hashAlgorithm.ComputeHash(utf8Bytes);
  }
  finally
  {
    for (int i = 0; i < utf8Bytes.Length; i++)
    {
      utf8Bytes[i] = 0;
    }
    utf8BytesPin.Free();
  }
}

它依赖于获取指向原始 UTF-16 字符串和 UTF-8 缓冲区的指针,然后使用 Encoding.UTF8.GetBytes(Char*, Int32, Byte*, Int32) 来保持非托管内存中的转换。

关于C# 将 SecureString 安全地转换为 UTF-8 byte[],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50589721/

相关文章:

c# - 当另一个属性更改时更新实体属性(实体初始化后)

c# - 如何在 ActiveMQ 中使用 MaxReconnectAttemps

c# - 语法错误 : Missing operand before '=' operator

java - 将 latin1 编码为 UTF8 失败

html - 当找不到元标记时,我可以给 jsoup 一个后备字符编码吗?

c# - 修改传递给C dll的char数组c#

C# MVP 的模型是什么?

php - 如何从文件开头删除?

java - UTF-8 JAXB 编码后 XML 内容仍为 ISO 8859-1

c# - 在 C# 中编码(marshal) IntPtr 数组