0
votes

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?

1
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
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
Thanks @JeroenMostert, that did the trick!Ashigore

1 Answers

0
votes

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);