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