C # - RSACryptoServiceProvider Расшифровать в SecureString вместо байтового массива - PullRequest
4 голосов
/ 21 декабря 2010

У меня есть метод, который в настоящее время возвращает строку, преобразованную из байтового массива:

public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding();
public static string Decrypt(string textToDecrypt, string privateKeyXml)
{
    if (string.IsNullOrEmpty(textToDecrypt))
    {
        throw new ArgumentException(
            "Cannot decrypt null or blank string"
        );
    }
    if (string.IsNullOrEmpty(privateKeyXml))
    {
        throw new ArgumentException("Invalid private key XML given");
    }
    byte[] bytesToDecrypt = Convert.FromBase64String(textToDecrypt);
    byte[] decryptedBytes;
    using (var rsa = new RSACryptoServiceProvider())
    {
        rsa.FromXmlString(privateKeyXml);
        decryptedBytes = rsa.Decrypt(bytesToDecrypt, FOAEP);
    }
    return ByteConverter.GetString(decryptedBytes);
}

Я пытаюсь обновить этот метод, чтобы вместо него возвращать SecureString, но у меня возникают проблемы с преобразованиемвозвращаемое значение RSACryptoServiceProvider.Decrypt от byte[] до SecureString.Я попробовал следующее:

var secStr = new SecureString();
foreach (byte b in decryptedBytes)
{
    char[] chars = ByteConverter.GetChars(new[] { b });
    if (chars.Length != 1)
    {
        throw new Exception(
            "Could not convert a single byte into a single char"
        );
    }
    secStr.AppendChar(chars[0]);
}
return secStr;

Однако, используя этот тестер равенства SecureString , полученный SecureString не был равен SecureString, сконструированному из исходного незашифрованного текста.Мои методы Encrypt и Decrypt работали раньше, когда я просто использовал string везде, и я также протестировал код равенства SecureString, так что я почти уверен, что проблема в том, как я пытаюсь конвертировать byte[] в SecureString.Есть ли другой путь, который я должен выбрать для использования шифрования RSA, который позволил бы мне получить SecureString при расшифровке?

Редактировать: Я не хотел преобразовывать байтовый массивв обычную строку, а затем поместите эту строку в SecureString, потому что это, кажется, побеждает смысл использования SecureString в первую очередь.Тем не менее, также плохо, что Decrypt возвращает byte[], и я пытаюсь вставить этот байтовый массив в SecureString?Я полагаю, что если Decrypt вернет byte[], то это безопасный способ передачи конфиденциальной информации, поэтому преобразование одного безопасного представления данных в другое безопасное представление кажется нормальным.

Ответы [ 5 ]

3 голосов
/ 10 июля 2015

Я думаю, что проблема может быть в вашем ByteConvert.GetChars методе. Я не могу найти этот класс или метод в документах MSDN. Я не уверен, что это опечатка или доморощенная функция. Независимо от этого, скорее всего, это неправильно интерпретирует кодирование байтов. Вместо этого используйте метод GetChars UTF8Encoding. Он будет правильно преобразовывать байты обратно в строку .NET, если они изначально были зашифрованы из строкового объекта .NET. (Если нет, вы захотите использовать метод GetChars в кодировке , соответствующей исходной строке.)

Вы правы, что использование массивов - самый безопасный подход. Поскольку расшифрованные представления вашего секрета хранятся в байтовых или символьных массивах, вы можете легко очистить их после завершения, чтобы ваш открытый текст не остался в памяти. Это не совсем безопасно, но более безопасно, чем преобразование в строку. Строки не могут быть изменены, и они остаются в памяти, пока не будут собраны в мусор в неопределенное время в будущем.

var secStr = new SecureString();
var chars = System.Text.Encoding.UTF8.GetChars(decryptedBytes);
for( int idx = 0; idx < chars.Length; ++idx )
{
    secStr.AppendChar(chars[idx]);
    # Clear out the chars as you go.
    chars[idx] = 0
}

# Clear the decrypted bytes from memory, too.
Array.Clear(decryptedBytes, 0, decryptedBytes.Length);

return secStr;
3 голосов
/ 21 декабря 2010

Символ и байт могут использоваться взаимозаменяемо с приведением, поэтому измените ваш второй кусок кода следующим образом:

var secStr = new SecureString();
foreach (byte b in decryptedBytes)
{
   secStr.AppendChar((char)b);
}

return secStr;

Это должно работать правильно, но имейте в виду, что вы все еще приноситеНезашифрованная информация в «чистом» виде в памяти, поэтому есть момент, когда она может быть скомпрометирована (что побуждает цель к SecureString).

** Обновление **

A byte[] вашей конфиденциальной информации не защищено.Вы можете посмотреть на это в памяти и увидеть информацию (особенно, если это просто строка).Отдельные байты будут в точном порядке строки, поэтому «читать» это довольно просто.

Я (на самом деле около часа назад) сам боролся с этой же проблемой, и какНасколько я знаю, нет хорошего способа перейти прямо от расшифровщика к SecureString, если дешифратор не запрограммирован специально для поддержки этой стратегии.

0 голосов
/ 03 ноября 2017

Что делать, если вы придерживаетесь UTF-16?

Внутренне .NET (и, следовательно, SecureString) использует UTF-16 (двойной байт) для хранения содержимого строки. Вы можете воспользоваться этим и перевести ваши защищенные данные по два байта (то есть 1 символ) за раз ...

Когда вы шифруете, снимите Char и используйте Encoding.UTF16.GetBytes (), чтобы получить ваши два байта, и вставьте эти два байта в ваш поток шифрования. И наоборот, когда вы читаете из своего зашифрованного потока, читайте два байта за раз и UTF16.GetString (), чтобы получить свой символ.

Возможно, это звучит ужасно, но не позволяет всем символам вашей секретной строки быть в одном месте, И это дает вам достоверность "размера" символа (вам не придется угадывать, является ли следующий единственный байт символ или UTF-маркер для символа двойной ширины). У наблюдателя нет возможности узнать, с какими персонажами и в каком порядке идти, поэтому угадать секрет почти невозможно.

Честно говоря, это всего лишь предложенная идея ... Я собираюсь попробовать это сам и посмотреть, насколько она жизнеспособна. Моя цель - создать методы расширения (SecureString.Encrypt и ICrypto.ToSecureString или что-то в этом роде).

0 голосов
/ 21 декабря 2010

Основываясь на Кодируя ответ Гориллы , я попробовал следующее в моем Decrypt методе:

string decryptedString1 = string.Empty;
foreach (byte b in decryptedBytes)
{
    decryptedString1 += (char)b;
}
string decryptedString2 = ByteConverter.GetString(decryptedBytes);

При отладке decryptedString1 и decryptedString2 не были равны:

decryptedString1    "m\0y\0V\0e\0r\0y\0L\0o\0n\0g\0V\03\0r\0y\05\03\0c\0r\03\07\0p\04\0s\0s\0w\00\0r\0d\0!\0!\0!\0"
decryptedString2    "myVeryLongV3ry53cr37p4ssw0rd!!!"

Похоже, я могу просто пройти через массив byte[], выполнить прямое приведение к char и пропустить \0 символов.Как сказал Горилла по кодированию, это, похоже, опять отчасти лишает смысла SecureString, потому что конфиденциальные данные плавают в памяти небольшими порциями byte.Любые предложения, чтобы заставить RSACryptoServiceProvider.Decrypt вернуть SecureString напрямую?

Редактировать: Да, это работает:

var secStr = new SecureString();
foreach (byte b in decryptedBytes)
{
    var c = (char)b;
    if ('\0' == c)
    {
        continue;
    }
    secStr.AppendChar(c);
}
return secStr;

Редактировать: исправление: это работает с простыми старыми английскими строками.Шифрование, а затем попытка расшифровать строку "標準語 明治維新 english やった" не работает должным образом, поскольку полученная расшифрованная строка, использующая эту технику foreach (byte b in decryptedBytes), не соответствует исходной незашифрованной строке.

Редактировать: , используя следующее, работает для обоих:

var secStr = new SecureString();
foreach (char c in ByteConverter.GetChars(decryptedBytes))
{
    secStr.AppendChar(c);
}
return secStr;

Это все еще оставляет байтовый массив и массив символов пароля в памяти, что отстой.Может быть, я должен найти другой класс RSA, который возвращает SecureString.: /

0 голосов
/ 21 декабря 2010

Использовать System.Encoding.Default.GetString GetString MSDN

...