SecureString to Byte[] C#

匆匆过客 提交于 2019-12-18 04:09:59

问题


How would I get a byte[] equivalent of a SecureString (which I get from a PasswordBox)?

My objective is to write these bytes using a CryptoStream to a file, and the Write method of that class takes a byte[] input, so I want to convert the SecureString to the byte[] so I can use in with a CryptoStream.

EDIT: I don't want to use string as it defeats the point of having a SecureString


回答1:


I modified from the original answer to handle 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:


Assuming you want to use the byte array and get rid of it as soon as you're done, you should encapsulate the entire operation so that it cleans up after itself:

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

And here's how a use case looks:

private byte[] GetHash(SecureString password)
{
    using (var h = new SHA256Cng()) // or your hash of choice
    {
        return password.Process(h.ComputeHash);
    }
}

No muss, no fuss, no plaintext left floating in memory.

Keep in mind that the byte array passed to func() contains the raw Unicode rendering of the plaintext, which shouldn't be an issue for most cryptographic applications.




回答3:


Per this, http://www.microsoft.com/indonesia/msdn/credmgmt.aspx, you can marshal it into a stock C# string and then convert that into an array of bytes:

static string SecureStringToString( SecureString value )
{
  string s ;
  IntPtr p = Marshal.SecureStringToBSTR( value );
  try
  {
    s = Marshal.PtrToStringBSTR( p ) ;
  }
  finally
  {
    Marshal.FreeBSTR( p ) ;
  }
  return s ;
}

or per this answer, How to convert SecureString to System.String?, you can use Marshal.ReadByte and Marshal.ReadInt16 on the IntPtr to get what you need.



来源:https://stackoverflow.com/questions/18392538/securestring-to-byte-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!