簡體   English   中英

SecureString 到 Byte[] C#

[英]SecureString to Byte[] C#

我如何獲得與SecureString等效的byte[] (我從PasswordBox獲得)?

我的目標是使用CryptoStream將這些字節寫入文件,並且該類的Write方法采用byte[]輸入,因此我想將SecureString轉換為byte[]以便我可以與CryptoStream一起使用。

編輯:我不想使用string因為它破壞了擁有SecureString

假設您想使用字節數組並在完成后立即刪除它,您應該封裝整個操作,以便它自己清理:

public static T Process<T>(this SecureString src, Func<byte[], T> func)
{
    IntPtr bstr = IntPtr.Zero;
    byte[] workArray = null;
    GCHandle? handle = null; // Hats off to Tobias Bauer
    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); // Hats off to Tobias Bauer
            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 呈現,這對於大多數加密應用程序來說應該不是問題。

我修改了原始答案以處理 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);
}

由於我沒有足夠的聲望點來評論 Eric 的回答,所以我必須發表這篇文章。

在我看來,Eric 的代碼存在問題,因為GCHandle.Alloc(workArray, ...)沒有正確完成。 它不應該固定workArraynull值,而是固定實際的數組,該數組將在更遠的幾行處創建。

此外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 ***/
    }
}

這些修改確保將正確的byte數組固定在內存中(更改編號 1 和 2)。 此外,他們避免將未加密的二進制字符串仍然加載到內存中,以防handle?.Free()拋出異常(第 3 項更改)。

這個 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);
}

根據這一點, 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 ;
}

或根據此答案, 如何將 SecureString 轉換為 System.String? ,您可以在IntPtr上使用Marshal.ReadByteMarshal.ReadInt16來獲取所需內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM