2013-08-22 100 views
5

我該如何得到一個byte[]的等價物SecureString(我從PasswordBox得到)?SecureString to Byte [] C#

我的目標是使用CryptoStream到一個文件中寫入這些字節,而類Write方法採用byte[]輸入,所以我要轉換的SecureStringbyte[]這樣我就可以使用與CryptoStream

編輯:我不想用string,因爲它違背了一個SecureString

回答

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); 
} 
+1

與ANSI C字符串不同,BSTR可以包含空字符,因此您的空值掃描無效。只需使用源SecureString的Length成員(乘以2得到字節數)。 –

-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.ReadByteMarshal.ReadInt16IntPtr得到你所需要的。

+2

儘管這確實違背了「SecureString」的目的,即允許安全地在內存中存儲敏感字符串數據(如密碼)。 – Adrian

+0

我不想使用'string',那麼有沒有其他的方法,這將仍然保持密碼安全並完成相同的事情? – inixsoftware

5

假設你要使用的字節數組,一旦大功告成擺脫它,你應該封裝整個操作,這樣可以清理達後本身:

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渲染,這對大多數加密應用程序來說不應該是個問題。

+2

埃裏克 - 我認爲你的工作陣列有一個小問題。 很好,你正在調零它,但如果垃圾收集器將決定移動它呢?那麼你的暴露數據就是內存中的「垃圾」。 在將敏感數據放入 – ArielB

+1

之前,您需要將字節數組固定到內存中。好的,@ArielB。我已經添加了一個GCHandle.Alloc()和handle.Free()來關閉workArray直到它被清除。遲到總比不到好。謝謝! –