0

I have strings of sensitive information that I need to collect from my users. I am using a WPF PasswordBox to request this information. For the uninitiated, the PasswordBox control provides a SecurePassword property which is a SecureString object rather than an insecure string object. Within my application, the data from the PasswordBox gets passed as a SecureString to an encryption method.

What I need to be able to do is marshal the data to a byte array that essentially represents a .Net string value without first converting the data to a .Net string. More specifically, given a SecureString with a value such as...

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`~!@#$%^&*()_-+={[}]|:;"'<,>.?/ ≈篭母

...how can I convert it to a byte array that is the equivalent a .Net string that's been serialized and written to a stream with a StreamWriter?

By using Marshal.SecureStringToCoTaskMemUnicode(...) I am able to do this with more traditional, western text. However, when I created the above text string using additional, not-typical characters and a string of Japanese text (see the last few bolded characters) my method of getting a Unicode byte array assigned to the IntPtr position doesn't seem to properly work anymore.

How can I emit the data of a SecureString in a secure way such that the returned byte data is structured the same as the byte data of a standard .Net string, serialized to binary output?

NOTE

Please ignore all security concerns at the moment. I am working on making various security upgrades to my application. For now, I need to use a SecureString for getting the sensitive data to the encryptor. The decryptor (for now) will still need to decrypt this data to string values, which is why I need to some how serialize the data in the the SecureString to a binary format similar to the binary format of the string object.

I agree that this approach is a bit unfortunate, however, I'm having to make incremental improvements on an existing application, and the first phase is locking down the data in SecureString objects from the user to the encryptor.

RLH
  • 15,230
  • 22
  • 98
  • 182
  • https://codereview.stackexchange.com/a/107864/5297 ? – stuartd Dec 01 '17 at 16:11
  • this sounds a bit suspect, why do you need there password information if you are locking down certain things, there are other methods / approaches one could take to prevent unauthorized access to things such as network shares, folders, database, etc.. can you explain what it is you are truly trying to lock down..? – MethodMan Dec 01 '17 at 16:16
  • @MethodMan See my notes. Our application has to collect sensitive information, encrypt it using secured key, distribute the encrypted data that is then decrypted at a different locale. Eventually, I will update the code in the second-point to decrypt the data and keep the details in SecureStrings. However, as I stated, I need to do this in a multi-step process. For now, the application responsible for the encryption needs to emit the data in such a way that the decrypted details can be easily re-converted into a .Net string. – RLH Dec 01 '17 at 17:06

1 Answers1

1

If you need to write secure string to stream, I'd suggest to create method like this:

public static class Extensions {
    public static void WriteSecure(this StreamWriter writer, SecureString sec) {
        int length = sec.Length;
        if (length == 0)
            return;
        IntPtr ptr = Marshal.SecureStringToBSTR(sec);
        try {
            // each char in that string is 2 bytes, not one (it's UTF-16 string)
            for (int i = 0; i < length * 2; i += 2) {
                // so use ReadInt16 and convert resulting "short" to char
                var ch = Convert.ToChar(Marshal.ReadInt16(ptr + i));
                // write
                writer.Write(ch);
            }
        }
        finally {
            // don't forget to zero memory
            Marshal.ZeroFreeBSTR(ptr);
        }
    }
}

If you really need byte array - you can reuse this method too:

byte[] result;
using (var ms = new MemoryStream()) {
    using (var writer = new StreamWriter(ms)) {
        writer.WriteSecure(secureString);
    }
    result = ms.ToArray();
}

Though method from first comment might be a bit more pefomant (not sure if that's important for you).

Evk
  • 98,527
  • 8
  • 141
  • 191