0

I an working with API calls to advapi32.dll for managing Windows Saved Credentials in order to automate certain windows applications which can use saved credentials, which is working fine.

I am trying to update my code to use SecureString for password throughout, as I have no need to interact with the text contained in passwords at any point so it should be more secure if my application never holds the password in plain text.

I am able to marshal a SecureString to COM task allocator memory to pass to the API calls with:

var unmanagedPassword = Marshal.SecureStringToCoTaskMemUnicode(userCredential.Password);

However, when it comes to reading that information back into the application, I cannot find a way to marshal such an unmanaged string back into a SecureString without copying the string into managed memory, be it as a string or byte array.

Is there a safe way to do this that I am overlooking?

Ashigore
  • 4,618
  • 1
  • 19
  • 39
  • Would the downvoter care to explain how this question is a "why isn't it working" question without desired outcome? I'm asking how to do something that I cannot find a way to do. I think I have specified clearly what I need. To create a new SecureString from unmanaged memory. – Ashigore Sep 05 '17 at 20:10
  • It's a curious oversight. I suppose the simplest way (without unmanaged code) is to call `.AppendChar()` in a loop over `Marshal.ReadInt16()` (assuming a Unicode string). There's also an unsafe constructor that takes a `char*` and a length. Note that the unmanaged memory should be zeroed and deallocated, otherwise there's not much point to you using a `SecureString` while all the data is still floating around in plain text. – Jeroen Mostert Sep 05 '17 at 20:16
  • Thanks @JeroenMostert, that did the trick! – Ashigore Sep 05 '17 at 20:36

1 Answers1

0

Big thanks to Jeroen Mostert for his comments which lead to this solution, which should be about as safe as is possible.

As Jeroen described, each character is read as a short and appended to a new SecureString one at a time.

Unmanaged strings in Task Allocator memory are null terminated, hence reading characters until getting 0. Unmanaged binary strings are length prefixed and so would require a slight modification of the code below.

    var outString = new SecureString();
    outString.AppendChar('p');
    outString.AppendChar('a');
    outString.AppendChar('s');
    outString.AppendChar('s');
    outString.AppendChar('w');
    outString.AppendChar('o');
    outString.AppendChar('r');
    outString.AppendChar('d');
    var ptr = Marshal.SecureStringToCoTaskMemUnicode(outString);

    var inString = new SecureString();
    var i = 0;
    short c;

    while ((c = Marshal.ReadInt16(ptr, i)) != 0)
    {
        inString.AppendChar((char)c);
        i += 2;
    }

    Marshal.ZeroFreeCoTaskMemUnicode(ptr);
Ashigore
  • 4,618
  • 1
  • 19
  • 39