Безопасно конвертировать зашифрованную стандартную строку в 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 и просто использовать командлет.
Если вы оставите пространство выполнения открытым, повторное выполнение будет довольно быстрым.