1

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.

David I. McIntosh
  • 2,038
  • 4
  • 23
  • 45
  • Possible duplicate of [How can I use ConvertTo-SecureString](http://stackoverflow.com/questions/13633826/how-can-i-use-convertto-securestring) – Mathias R. Jessen Nov 23 '15 at 21:26
  • 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 Nov 23 '15 at 23:33
  • 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 Nov 25 '15 at 03:00
  • I know this is old, but I just want to say thank you for your code! It helped me tremendously :) – Indigo Mar 12 '19 at 11:01
  • @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 Mar 12 '19 at 14:02
  • Thanks for the feedback, but actually I was more interested in the "how to decrypt" part than the "secure string in memory" part. However, I stumble upon this Microsoft advisory that you may be interested in : https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md – Indigo Mar 12 '19 at 21:49
  • @Indigo: code above was modified today - it's now secure. If you're using the code I had before, grab the new code above, its simpler and safer. – David I. McIntosh Mar 28 '19 at 03:31
  • The new secure code should be posted as an answer instead of an edit. – Scott Chamberlain Mar 28 '19 at 03:46
  • @ScottChamberlain Point taken. (In particular, difference between old, incorrect code and new code would be illustrative of security pitfalls). When I have the time, I will do that. But, I am no security expert, and I only _think_ I have a good solution. I'd still like to see some comment from someone who at least claims to be an expert... – David I. McIntosh Mar 28 '19 at 03:49
  • @DavidI.McIntosh thanks for letting me know :) this is an interesting new take. However, unsafe code may be frowned upon in some environment due to coding rules and compliance. – Indigo Mar 28 '19 at 08:00
  • @indigo: Ah, your comment made me notice a mistake - the main bit of code above actually does not need to be qualified as "unsafe", only the alternate code below. I hadn't noticed the "unsafe" qualification that was there before. I have removed it. – David I. McIntosh Mar 29 '19 at 17:20
  • Probably don't need unsafe, if you can use stackalloc https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/stackalloc. – Jeremy Lakeman May 04 '20 at 03:59
  • @JeremyLakeman From a quick read of the article you reference: "As the preceding example shows, you must use an unsafe context when you work with pointer types", and since we are using char* pChars in the call to SecureString(char* pChars,...), it would still have to be run as unsafe. If I am wrong (I may be...), do say. – David I. McIntosh Mar 25 '21 at 14:21

1 Answers1

0

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.

briantist
  • 45,546
  • 6
  • 82
  • 127