我正试图将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字节[]步骤可以提高内存效率吗?
答案 0 :(得分:1)
我找到了按照我想要的方式做到这一点的方法。我在这里的代码还没有完成(在失败的情况下需要更好的异常处理和内存管理),但这里是:
[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)
将转换保持在非托管内存中。
答案 1 :(得分:0)
您是否考虑过在获取哈希后调用UserFormViewComponent extends from EntityFormViewComponent
?
强制所有世代立即进行垃圾收集。 使用此方法尝试回收所有无法访问的内存。它执行所有代的阻塞垃圾收集。
所有物品,无论它们在记忆中存在多长时间,都被考虑收集;但是,不会收集托管代码中引用的对象。使用此方法强制系统尝试回收最大可用内存量。
从我在代码中看到的内容来看,它不应该保留对转换中使用的对象的任何引用。这一切都应由GC收集和处理。