SecureString到Byte [] C#

时间:2013-08-22 23:49:10

标签: c# wpf encryption securestring

我如何得到byte[]相当于SecureString(我从PasswordBox得到的)?

我的目标是使用CryptoStream将这些字节写入文件,并且该类的Write方法接受byte[]输入,因此我想转换{{1 {}为} SecureString所以我可以使用byte[]

编辑:我不想使用CryptoStream,因为它违背了string

的要点

5 个答案:

答案 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.ReadInt16IntPtr来获取所需内容。