Rfc2898DeriveBytes + PBKDF2 + SecureString can I use a secure string instead of a string?

I have a GetPassword function that returns a type of SecureString .

When I pass this safe line to Rfc2898DeriveBytes to generate a key, Visual Studio shows an error. My limited knowledge tells me that this is because Rfc2898DeriveBytes only accepts a string, not a protected string. Is there any way around this?

 //read the password from terminal Console.Write("Insert password"); securePwd = myCryptography.GetPassword(); //dont know why the salt is initialized like this byte[] salt = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xF1, 0xF0, 0xEE, 0x21, 0x22, 0x45 }; try { //PBKDF2 standard Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(securePwd, salt, iterationsPwd); 
+4
source share
3 answers

After some research and review of previous stackoverflow answers mentioning SecureString , this answer is almost certain: No. Only API creators can accept SecureString and process it correctly internally. And they can only do this with the platform.

If you, as a user, can get plain String text, you would first of all give up most of the benefits of using SecureString . It would be even a little dangerous, since you would create safe code that would not actually be safe.

+4
source

Apparently, you can violate the protection provided by SecureString and set your internal state using the Marshal.SecureStringToBSTR() function.

Instead of creating a String from the result, copy the contents into Byte[] to go to Rfc2898DeriveBytes . Creating a String prevent you from destroying password information, allowing it to hang indefinitely on the heap or offload to disk, which in turn increases the likelihood that an attacker can find it. Instead, you should destroy the password as soon as you finish using it, filling the array with zeros. For the same reason, you must also set a zero for each BSTR element when copying it to Byte[] .

The salt should be randomly selected for each hashed password, and not a fixed predicted value, otherwise a preliminary calculated dictionary attack is possible. You must repeat many tens of thousands of times to prevent brute force attacks.

+6
source

I was curious that the Rfc2898DeriveBytes class Rfc2898DeriveBytes not support SecureString overloading to pass the password used in key derivation.

WPF allows you to process passwords as SecureString objects using PasswordBox . It seemed like such a loss, that the added security that this control offers was lost due to the fact that we could not pass the SecureString construct to the constructor. However, erickson has shown an excellent point of using byte[] instead of overloading string , since it is relatively easy to manage the contents of byte[] in memory than string .

Using erickson's suggestion as inspiration, I came up with the following shell, which should allow you to use a SecureString protected password value with a minimal exposure of the plaintext value in memory.

 private byte[] DeriveKey(SecureString password, byte[] salt, int iterations, int keyByteLength) { IntPtr ptr = Marshal.SecureStringToBSTR(password); byte[] passwordByteArray = null; try { int length = Marshal.ReadInt32(ptr, -4); passwordByteArray = new byte[length]; GCHandle handle = GCHandle.Alloc(passwordByteArray, GCHandleType.Pinned); try { for (int i = 0; i < length; i++) { passwordByteArray[i] = Marshal.ReadByte(ptr, i); } using (var rfc2898 = new Rfc2898DeriveBytes(passwordByteArray, salt, iterations)) { return rfc2898.GetBytes(keyByteLength); } } finally { Array.Clear(passwordByteArray, 0, passwordByteArray.Length); handle.Free(); } } finally { Marshal.ZeroFreeBSTR(ptr); } } 

This approach takes advantage of the fact that BSTR is a pointer pointing to the first character of a data string with a four-byte prefix.

Important points:

  • By wrapping Rfc2898DeriveBytes in a using statement, it ensures that it is deterministic. This is important because it has an internal HMACSHA1 object, which is KeyedHashAlgorithm , and needs to have a copy of the key (password) that it has in order to be reset to zero in the Dispose call. See the help source for more information.
  • As soon as we finish with BSTR , we zero it and release it through ZeroFreeBSTR .
  • Finally, we clear (clear) our copy of the password.
  • Update: Added byte[] pinning. As discussed in the comments of this answer, if byte[] not fixed, the garbage collector can move the object during the collection, and we would have no way to reset the original copy.

This should keep the plaintext password in memory for the shortest possible time and not weaken the benefits of using SecureString too much. Although, if an attacker has access to RAM, you probably have more problems. Another point is that we can only manage our own copies of the password, the API that we use can very poorly sort (not reset / clear) our copies. As far as I know, this does not apply to Rfc2898DeriveBytes , although their copy of the byte[] key (password) is not fixed and, therefore, traces of the array may hang if they were moved to the heap before resetting to zero. The message here is that the code may look safe, but problems may lie beneath it.

If anyone finds any serious flaws in this implementation, let me know.

+2
source

Source: https://habr.com/ru/post/1401756/


All Articles