David I. McIntosh
David I. McIntosh

Reputation: 2130

securely convert encrypted standard string to securestring

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.

Upvotes: 1

Views: 1639

Answers (1)

briantist
briantist

Reputation: 47872

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.

Upvotes: 0

Related Questions