Безопасно конвертировать зашифрованную стандартную строку в securestring

У меня есть зашифрованная стандартная строка, созданная в powershell с помощью "convertfrom-securestring" и сохраненная в файл. Позже я прочитал эту зашифрованную строку в приложении C# (под тем же идентификатором пользователя и на той же машине). Я хочу преобразовать эту зашифрованную строку в объект securestring. В powershell это можно сделать с помощью "convertto-securestring". Как можно сделать это безопасно в C#?

Мне известен этот ответ ( как я могу использовать ConvertTo-SecureString), но он предполагает, что зашифрованная строка была создана с известным ключом. Это отличается от случая, когда зашифрованная строка была создана без ключа (и, таким образом, привязана к идентификатору пользователя и машине, на которой она была построена).

Также обратите внимание, что в комментариях к вышеупомянутому вопросу цитировалась документация от Microsoft, в которой указывалось, что в случае отсутствия ключа используется API защиты данных Windows (DPAPI). В этой статье ( https://msdn.microsoft.com/en-us/library/ms229741(v=vs.110).aspx) показано, как использовать DPAPI, но, к сожалению, использование его в порядке, указанном в статье Microsoft, результат снова приводит к тому, что строка находится в виде открытого текста в строковом объекте (над которым мы не имеем никакого контроля, в отношении удаления), и поэтому использование ее указанным способом будет небезопасным.

Фактически, это фактически комбинация этого ( Как расшифровать строку в C#, которая зашифрована с помощью PowerShell) и вопроса в Как я могу использовать ConvertTo-SecureString.

Для записи, этот код работает, но я не уверен, какие проблемы могут быть с сохранением расшифрованной строки в байтовом массиве, даже если я делаю это как можно быстрее:

public static unsafe 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), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
    }

    byte[] decrypted = ProtectedData.Unprotect(encrypted, (byte[])null, DataProtectionScope.CurrentUser);
    lngth = decrypted.Length - 1;
    byte[] chr = new byte[2];
    SecureString secureString = new SecureString();
    for (int i = 0; i < lngth; )
    {
        chr[0] = decrypted[i]; decrypted[i++] = 0;
        chr[1] = decrypted[i]; decrypted[i++] = 0;
        string passwd = UnicodeEncoding.Unicode.GetString(chr);
        secureString.AppendChar(passwd[0]);
    }

    return secureString;
}

Обратите внимание на очевидную глупость копирования пар байтов из decrypted в chrи затем применяя UnicodeEncoding.Unicode.GetString по одному персонажу за раз. Если бы мы просто сделали это

string passwd = UnicodeEncoding.Unicode.GetString(decrypted);
for (int i = 0; i < passwd.Length; ++i)
{
    secureString.AppendChar(passwd[i]);
}

затем мы поместили "расшифрованную" строку в строковый объект, и она может зависать в памяти - у нас нет контроля. Код выше избегает этого. Мне не ясно, что если оставить его байтовым массивом и обнулить его, это гарантирует, что в памяти не будет копий. Кто-то может прокомментировать это?

1 ответ

Я не знаю, есть ли собственный метод C# для этого, но если нет, вы всегда можете вернуться к использованию пространства выполнения для выполнения конвейера powershell и просто использовать командлет.

Если вы оставите пространство выполнения открытым, повторное выполнение будет довольно быстрым.

Другие вопросы по тегам