猿问

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

我正在尝试进入 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[] 步骤,这可以提高内存效率吗?


狐的传说
浏览 259回答 2
2回答

波斯汪

我找到了一种方法来按照我想要的方式做到这一点。我这里的代码还没有完成(在失败的情况下需要更好的异常处理和内存管理),但这里是:[DllImport("kernel32.dll")]static extern void RtlZeroMemory(IntPtr dst, int length);public unsafe static byte[] HashNew(this SecureString secureString, HashAlgorithm hashAlgorithm){&nbsp; IntPtr bstr = Marshal.SecureStringToBSTR(secureString);&nbsp; int maxUtf8BytesCount = Encoding.UTF8.GetMaxByteCount(secureString.Length);&nbsp; IntPtr utf8Buffer = Marshal.AllocHGlobal(maxUtf8BytesCount);&nbsp; // Here's the magic:&nbsp; char* utf16CharsPtr = (char*)bstr.ToPointer();&nbsp; byte* utf8BytesPtr&nbsp; = (byte*)utf8Buffer.ToPointer();&nbsp; int utf8BytesCount = Encoding.UTF8.GetBytes(utf16CharsPtr, secureString.Length, utf8BytesPtr, maxUtf8BytesCount);&nbsp; Marshal.ZeroFreeBSTR(bstr);&nbsp; var utf8Bytes = new byte[utf8BytesCount];&nbsp; GCHandle utf8BytesPin = GCHandle.Alloc(utf8Bytes, GCHandleType.Pinned);&nbsp; Marshal.Copy(utf8Buffer, utf8Bytes, 0, utf8BytesCount);&nbsp; RtlZeroMemory(utf8Buffer, utf8BytesCount);&nbsp; Marshal.FreeHGlobal(utf8Buffer);&nbsp; try&nbsp; {&nbsp; &nbsp; return hashAlgorithm.ComputeHash(utf8Bytes);&nbsp; }&nbsp; finally&nbsp; {&nbsp; &nbsp; for (int i = 0; i < utf8Bytes.Length; i++)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; utf8Bytes[i] = 0;&nbsp; &nbsp; }&nbsp; &nbsp; utf8BytesPin.Free();&nbsp; }}它依赖于获取指向原始 UTF-16 字符串和 UTF-8 缓冲区的指针,然后Encoding.UTF8.GetBytes(Char*, Int32, Byte*, Int32)用于将转换保留在非托管内存中。

ibeautiful

有没有考虑GC.Collect()在获得hash后调用?根据GC.Collect上的MSDN:强制立即对所有代进行垃圾收集。使用此方法尝试回收所有无法访问的内存。它执行所有代的阻塞垃圾收集。所有对象,无论它们在内存中的时间有多长,都被考虑收集;但是,不会收集托管代码中引用的对象。使用此方法强制系统尝试回收最大量的可用内存。从我在您的代码中看到的,它不应该保留对转换中使用的对象的任何引用。这一切都应该由 GC 收集和处理。
随时随地看视频慕课网APP
我要回答