我如何得到byte[]
相当于SecureString
(我从PasswordBox
得到的)?
我的目标是使用CryptoStream
将这些字节写入文件,并且该类的Write
方法接受byte[]
输入,因此我想转换{{1 {}为} SecureString
所以我可以使用byte[]
。
编辑:我不想使用CryptoStream
,因为它违背了string
答案 0 :(得分:7)
假设您想要使用字节数组并在完成后立即删除它,您应该封装整个操作,以便它自行清理:
public static T Process<T>(this SecureString src, Func<byte[], T> func)
{
IntPtr bstr = IntPtr.Zero;
byte[] workArray = null;
GCHandle handle = GCHandle.Alloc(workArray, GCHandleType.Pinned);
try
{
/*** PLAINTEXT EXPOSURE BEGINS HERE ***/
bstr = Marshal.SecureStringToBSTR(src);
unsafe
{
byte* bstrBytes = (byte*)bstr;
workArray = new byte[src.Length * 2];
for (int i = 0; i < workArray.Length; i++)
workArray[i] = *bstrBytes++;
}
return func(workArray);
}
finally
{
if (workArray != null)
for (int i = 0; i < workArray.Length; i++)
workArray[i] = 0;
handle.Free();
if (bstr != IntPtr.Zero)
Marshal.ZeroFreeBSTR(bstr);
/*** PLAINTEXT EXPOSURE ENDS HERE ***/
}
}
以下是用例的外观:
private byte[] GetHash(SecureString password)
{
using (var h = new SHA256Cng()) // or your hash of choice
{
return password.Process(h.ComputeHash);
}
}
没有麻烦,没有大惊小怪,没有明文留在记忆中。
请记住,传递给func()
的字节数组包含明文的原始Unicode呈现,对于大多数加密应用程序来说,这不应该是一个问题。
答案 1 :(得分:2)
我从original answer修改为处理unicode
IntPtr unmanagedBytes = Marshal.SecureStringToGlobalAllocUnicode(password);
byte[] bValue = null;
try
{
byte* byteArray = (byte*)unmanagedBytes.GetPointer();
// Find the end of the string
byte* pEnd = byteArray;
char c='\0';
do
{
byte b1=*pEnd++;
byte b2=*pEnd++;
c = '\0';
c= (char)(b1 << 8);
c += (char)b2;
}while (c != '\0');
// Length is effectively the difference here (note we're 2 past end)
int length = (int)((pEnd - byteArray) - 2);
bValue = new byte[length];
for (int i=0;i<length;++i)
{
// Work with data in byte array as necessary, via pointers, here
bValue[i] = *(byteArray + i);
}
}
finally
{
// This will completely remove the data from memory
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedBytes);
}
答案 2 :(得分:0)
由于我的信誉点不足以评论Eric的回答,因此我必须发布此帖子。
我认为Eric的代码有问题,因为GCHandle.Alloc(workArray, ...)
的处理不正确。它不应固定null
的{{1}}值,而是固定实际的数组,该数组将在更远的几行处创建。
此外,workArray
可以抛出handle.Free()
,因此我建议将其放置在InvalidOperationException
之后,以使至少二进制字符串Marshal.ZeroFreeBSTR(...)
指向零。
修改后的代码如下:
bstr
这些修改可确保将正确的public static T Process<T>(this SecureString src, Func<byte[], T> func)
{
IntPtr bstr = IntPtr.Zero;
byte[] workArray = null;
GCHandle? handle = null; // Change no. 1
try
{
/*** PLAINTEXT EXPOSURE BEGINS HERE ***/
bstr = Marshal.SecureStringToBSTR(src);
unsafe
{
byte* bstrBytes = (byte*)bstr;
workArray = new byte[src.Length * 2];
handle = GCHandle.Alloc(workArray, GCHandleType.Pinned); // Change no. 2
for (int i = 0; i < workArray.Length; i++)
workArray[i] = *bstrBytes++;
}
return func(workArray);
}
finally
{
if (workArray != null)
for (int i = 0; i < workArray.Length; i++)
workArray[i] = 0;
if (bstr != IntPtr.Zero)
Marshal.ZeroFreeBSTR(bstr);
handle?.Free(); // Change no. 3 (Edit: no try-catch but after Marshal.ZeroFreeBSTR)
/*** PLAINTEXT EXPOSURE ENDS HERE ***/
}
}
数组固定在内存中(更改1和2)。此外,它们避免在byte
引发异常(更改3)的情况下,仍将未加密的二进制字符串加载到内存中。
答案 3 :(得分:0)
这个 100% 托管代码似乎对我有用:
var pUnicodeBytes = Marshal.SecureStringToGlobalAllocUnicode(secureString);
try
{
byte[] unicodeBytes = new byte[secureString.Length * 2];
for( var idx = 0; idx < unicodeBytes.Length; ++idx )
{
bytes[idx] = Marshal.ReadByte(pUnicodeBytes, idx);
}
return bytes;
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(pUnicodeBytes);
}
答案 4 :(得分:-2)
根据这个http://www.microsoft.com/indonesia/msdn/credmgmt.aspx,您可以将其编组为库存C#字符串,然后将其转换为字节数组:
static string SecureStringToString( SecureString value )
{
string s ;
IntPtr p = Marshal.SecureStringToBSTR( value );
try
{
s = Marshal.PtrToStringBSTR( p ) ;
}
finally
{
Marshal.FreeBSTR( p ) ;
}
return s ;
}
或根据此回答How to convert SecureString to System.String?,您可以使用Marshal.ReadByte
上的Marshal.ReadInt16
和IntPtr
来获取所需内容。