1
votes

I have an encrypted standard string, created in powershell via "convertfrom-securestring" and saved to a file. Later, I read this encrypted string with a C# app (under same user ID and on same machine). I want to convert this encrypted string into a securestring object. In powershell, one can do this using "convertto-securestring". How can one do this securely in c#?

I am aware of this answer (How can I use ConvertTo-SecureString), but it assumes the encrypted string has been created with a known key. This is different from the case where the encrypted string has been created with no key (and is thus tied to the userID and machine on which it was constructed.)

Note also that in the comments in the above-referenced question, documentation from Microsoft was quoted that indicated the Windows Data Protection API (DPAPI) is used in the case where no key is provided. This article (https://msdn.microsoft.com/en-us/library/ms229741(v=vs.110).aspx) shows how to use the DPAPI, but unfortunately using it in the manner specified in the Microsoft article would result again result in the string being in clear-text in a string object (over which we have no control, vis-a-vis disposal), and so using it in the manner indicated would be insecure.

Actually, this is effectively a combination of this (How to decrypt a string in C# which is encrypted via PowerShell) and the question in How can I use ConvertTo-SecureString.

For the record, this code works, but I am not sure what the issues may be with storing the decrypted string in byte and char arrays, even though I do clobber it as quickly as possible:

public static System.Security.SecureString MakeSecurityString(string pwd)
{
    int lngth = pwd.Length / 2;
    byte[] encrypted = new byte[lngth];
    for (int i = 0; i < lngth; ++i)
    {
        encrypted[i] = byte.Parse(pwd.Substring(2 * i, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture);
    }

    byte[] decrypted = System.Security.Cryptography.ProtectedData.Unprotect(encrypted, (byte[])null, System.Security.Cryptography.DataProtectionScope.CurrentUser);
    var chars = System.Text.UnicodeEncoding.Unicode.GetChars(decrypted);
    for(int i=0; i<decrypted.Length; ++i)
    {
        decrypted[i]=0;
    }
    var secureString = new System.Security.SecureString();
    for (int i = 0; i < chars.Length; ++i)
    {
        secureString.AppendChar(chars[i]);
        chars[i] = '\0';
    }

    return secureString;
}

I think the above code is secure, except for the brief moment that the password is in clear text in the byte-array decrypted and then the char-array chars, though we zero these out as quickly as possible so that at least the password cannot hang around in memory after exiting the routine. We call secureString.AppendChar multiple times, which as Oguz Ozgu pointed out below, means secureString must be decrypting and then re-encrypting the string internally - one would presume this is done safely. Can someone comment on security issues of the above code?

Edit: note that the code below would avoid the multiple calls to secureString.AppendChar, but it must be run as "unsafe":

        SecureString secureString;
        fixed (char* pChars = chars)
        {
            secureString = new SecureString(pChars, chars.Length);
        }
        for (int i = 0; i < chars.Length; ++i)
        {
            chars[i] = '\0';
        }
        return secureString;

Note also that the link in Indigo's comment below makes it clear that this is ONLY SECURE IN A .NET FRAMEWORK ON WINDOWS.

1
How secure is a SecureString? It unprotects (decrypts) the buffer with each AppendChar() call, or InsertAt() call, or SetAt() call, and does the work, and encrypts the buffer again. The memory block will contain unencrypted data for a very very short while. I think this is what you should also implement; keep the unencrypted data in memory as short as possible. - Oguz Ozgul
Yes, the individual bytes will appear fleetingly in clear text one-by-one. But the issue is this: when using a single char to store the characters, that single char is not something on the heap where one needs to worry about its life-after-death, i.e. about it hanging around in heap memory undisposed. The second we put something like a password into a string, in C# we loose control of the memory containing that string: we have no way to guarantee destruction (over-writing) of the byte string. It can hang around in memory for a very long time, and find it way into hibernate images, etc. - David I. McIntosh
I know this is old, but I just want to say thank you for your code! It helped me tremendously :) - Indigo
@Indigo Note that the code above may be even more flawed that I originally realized, if you are really really concerned about security: it puts the characters of the password, one character at a time albeit, into a series of strings passwd, which may persist in memory after death. I am sure there are API's that would allow one to do this properly without using strings, I just don't know them, and was hoping some well-informed programmer might comment with a safer/superior way of doing things. - David I. McIntosh

1 Answers

0
votes

I don't know if there is a native C# method to do this, but if not you can always fall back to using a runspace to execute a powershell pipeline and just use the cmdlet.

If you keep the runspace open, repeated executions will be pretty fast.