К сожалению, без каких-либо подробностей было бы трудно понять, какой лучший ответ. Одна конкретная деталь, которая отсутствует здесь, - это то, откуда приходит объект SecureString
. Вы создаете его для выполнения этого хэша? Или пароль уже представлен объектом SecureString
, который вы передаете другим API?
Если первое, то это предполагает, что у вас уже есть незашифрованная строка без детерминированного жизненного цикла в вашем процессе, содержащая пароль. Если последнее, то, хотя время жизни незашифрованной версии (ов) пароля может быть детерминированным, обратите внимание, что пароль все еще заканчивается расшифровкой в разных точках исполнения.
Тем не менее, с точки зрения ваших конкретных вопросов:
Как работать количество байтов, выделенных SecureStringToGlobalAllocUnicode
Мне кажется, что вы должны быть в состоянии доверять, что удвоение длина исходного текста была бы надежной. Свойство SecureString.Length
возвращает количество объектов char
, составляющих строку, то есть число 16-разрядных значений UTF16, поэтому байты в два раза больше. Свойство Length
не учитывает кодовые точки Юникода, которые принимают два 16-битных значения (т. Е. Низкий и высокий суррогат), поэтому он должен быть точным для вычислений байтовой длины.
Это, если вы не доверяете этому & hellip; выделенная строка должна быть завершена нулем, поэтому вы можете просто выполнить обычное сканирование строки.Обратите внимание: если вы используете метод BSTR для строки, строка имеет префикс 32-разрядного байт счетчик (не число символов), представляющий строку, не считая ее нулевой ограничитель; вы можете получить это путем вычитания 4 из возвращаемого , получения четырех байтов и преобразования его обратно в значение int
.
Соответствующая функция для использования, когда мне нужен п байт после IntPtr и не хочу, чтобы выделить управляемые байты [] и использовать Marshal.Copy
Есть много способов сделать это , Я думаю, что один из самых простых подходов к р/вызова функции Windows, CopyMemory()
:
[DllImport("kernel32.dll")]
unsafe extern static void CopyMemory(void* destination, void* source, IntPtr size_t);
Просто передайте соответствующие IntPtr
значения метода, используя либо метод IntPtr.ToPointer()
или явное преобразование в void*
, что доступно. Используется так:
unsafe
{
CopyMemory(IntPtr.Add(unsafeBuffer, usernamePart.Length).ToPointer(),
unmanagedPwd.ToPointer(), new IntPtr(lenPasswordArray));
}
В .NET 4.6 (по MSDN ... Я не использовал это сам ... все еще застряли на 4.5), вы можете (смогут) использовать метод Buffer.MemoryCopy()
, Например:.
Buffer.MemoryCopy(unmanagedPwd.ToPointer(),
IntPtr.Add(unsafeBuffer, usernamePart.Length).ToPointer(),
lenPasswordArray,
lenPasswordArray);
(Обратите внимание, что я думаю, что вы имели тип в исходном примере, вы добавляете lenPasswordArray
в unsafeBuffer
указатель для определения местоположения, к которому для копирования данных паролей я исправил, что в выше примеров, используя вместо этого длину имени пользователя, поскольку вы, похоже, хотите скопировать данные пароля сразу после данных для имени пользователя, которое уже было скопировано).
Как зашифровать эти байты
Что вы имеете в виду под этим? Вы спрашиваете, как hash байты? То есть запустить алгоритм хеширования MD5 на них? Обратите внимание, что это не шифрование; нет никакого практического способа дешифрования значения (несмотря на недостатки безопасности MD5).
Если вы просто хотите хэш-байты, вам понадобится реализация MD5, которая может работать в неуправляемой памяти. Я не уверен, что Windows имеет неуправляемый MD5 API, но у него вообще есть криптография. Таким образом, вы можете использовать p/invoke для доступа к этим функциям. См. Cryptographic Service Providers для более подробной информации.
Замечу, что в данный момент, теперь у вас есть расшифрованные данные в памяти, в двух разных местах: первоначально расшифрованный блок памяти от вызова к SecureStringToGlobalAllocUnicode()
, и, конечно, новая копия вы сделали копирование на unsafeBuffer
. Вы можете контролировать время жизни этих буферов более близко, чем объект System.String
, но кроме этого у вас одинаковый риск в течение этого срока действия вредоносного кода, проверяющего ваш процесс и восстановления открытого текста.
Если вы имеете в виду что-то иное, кроме хэширования, пожалуйста, уточните, как и почему вы хотите «зашифровать эти байты».
Как надежно обнулить и бесплатно ничего я выделил (я новичок в небезопасном коде)
Я не знаю, что unsafe
имеет отношение к вопросу. Действительно, за исключением тех мест, где вам нужно использовать void*
, вашему примеру кода самому не нужно unsafe
.
Что касается обнуления буферов памяти, то код, который у вас, кажется, в порядке. Если вы хотите что-то немного более эффективное, чем выделение всего нового буфера byte[]
только для того, чтобы установить другое место памяти для всех нулей, вы можете p/вызвать функцию Windows SecureZeroMemory()
(аналогично приведенному выше примеру CopyMemory()
).
Теперь все сказанное выше, как я упоминал в комментариях, мне кажется, что есть способы сделать это в управляемом безопасном коде просто путем непосредственного контроля жизни промежуточных объектов. Например:
static string SecureComputeHash(string username, SecureString password)
{
byte[] textBytes = null;
IntPtr textChars = IntPtr.Zero;
try
{
byte[] userNameBytes = Encoding.Unicode.GetBytes(username);
textChars = Marshal.SecureStringToGlobalAllocUnicode(password);
int passwordByteLength = password.Length * 2;
textBytes = new byte[userNameBytes.Length + passwordByteLength];
userNameBytes.CopyTo(textBytes, 0);
Marshal.Copy(textChars, textBytes, userNameBytes.Length, passwordByteLength);
using (MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider())
{
return Convert.ToBase64String(provider.ComputeHash(textBytes));
}
}
finally
{
// Clean up temporary buffers
if (textChars != IntPtr.Zero)
{
Marshal.ZeroFreeGlobalAllocUnicode(textChars);
}
if (textBytes != null)
{
for (int i = 0; i < textBytes.Length; i++)
{
textBytes[i] = 0;
}
}
}
}
(я использовал кодировку base64 для преобразования хэша byte[]
результата в строку Простого вызов ToString()
вы показали в вашем примере не будет ничего делать полезно, так как он просто возвращает имя типа для. a byte[]
. Я думаю, что base64 - это самый эффективный и полезный способ хранения хешированных данных, но вы можете, конечно, использовать любое представление, которое вы считаете полезным).
Вышеупомянутый предполагает, что ваш пароль уже находится в объекте SecureString
. Конечно, если вы просто инициализируете объект SecureString
из какого-либо другого нешифрованного объекта, вы можете сделать это по-другому, например, создать char[]
непосредственно из незашифрованного объекта (который может быть, например, string
или StringBuilder
).
Я не вижу, как ваш неуправляемый подход был бы заметно лучше, чем указано выше.
Единственное исключение, которое я вижу, - это если вы обеспокоены тем, что класс MD5CryptoServiceProvider
может оставить некоторую копию ваших данных в своих собственных структурах данных. Это может быть серьезной проблемой, но тогда у вас также будет такая озабоченность по поводу вашего неуправляемого подхода, поскольку вы не показали, какую реализацию MD5 вы бы фактически использовали там (вам нужно было бы убедиться, что всякая реализация, которую вы используете, оставляя копии ваших данных).
Лично я подозреваю (но не знаю точно), что, учитывая слово «крипто» в имени класса MD5CryptoServiceProvider
, этот класс тщательно очищает временные буферы в памяти.
За исключением этого Возможно концерн, полностью управляемый подход выполняет то же самое, с ИМХО меньше суеты.
Простите мое невежество, почему вы делаете это в «небезопасном» контексте? – NWard
Почему вы используете небезопасный код в первую очередь? Похоже, все это можно было бы сделать с помощью безопасных типов ('string',' byte [] ') –
Копирование защищенной строки в' byte [] 'отбрасывает всю« безопасность »« SecureString ». Вероятно, вам придется использовать некоторую неуправляемую библиотеку для MD5. – Luaan