为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
狐的传说 2021-07-07 17:57:12
我正在尝试进入 aSecureString的形式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[] 步骤,这可以提高内存效率吗?
查看完整描述

2 回答

?
波斯汪

TA贡献1811条经验 获得超4个赞

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


[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)用于将转换保留在非托管内存中。


查看完整回答
反对 回复 2021-07-10
?
ibeautiful

TA贡献1993条经验 获得超5个赞

有没有考虑GC.Collect()在获得hash后调用?

根据GC.Collect上的MSDN

强制立即对所有代进行垃圾收集。使用此方法尝试回收所有无法访问的内存。它执行所有代的阻塞垃圾收集。

所有对象,无论它们在内存中的时间有多长,都被考虑收集;但是,不会收集托管代码中引用的对象。使用此方法强制系统尝试回收最大量的可用内存。

从我在您的代码中看到的,它不应该保留对转换中使用的对象的任何引用。这一切都应该由 GC 收集和处理。


查看完整回答
反对 回复 2021-07-10
  • 2 回答
  • 0 关注
  • 258 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信