В C# - rsacryptoserviceprovider будет расшифровать в объект SecureString, а не байтовый массив
У меня есть метод, который в настоящее время возвращает строку, преобразованную из массива байтов:
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
, построенному из оригинального, незашифрованного текста. Мои методы шифрования и дешифрования работали и раньше, когда я просто использовал string
везде, и я также протестировал код равенства SecureString
, поэтому я уверен, что проблема здесь заключается в том, как я пытаюсь преобразовать byte[]
в SecureString
. Существует ли другой маршрут, который я должен использовать для шифрования RSA, который позволит мне получить обратно SecureString
при расшифровке?
Edit: я не хотел преобразовывать массив байтов в обычную строку, а затем набивать эту строку в SecureString
, потому что это, кажется, побеждает смысл использования SecureString
В городе первое место. Однако плохо ли также, что Decrypt
возвращает byte[]
, и я затем пытаюсь запихнуть этот массив байтов в SecureString
? Я думаю, что если Decrypt
возвращает byte[]
, то это безопасный способ передачи конфиденциальной информации, поэтому преобразование одного безопасного представления данных в другое безопасное представление кажется нормальным.
5 ответов:
Символ и Байт могут использоваться взаимозаменяемо с приведением, поэтому измените свой второй фрагмент кода следующим образом:
Это должно работать должным образом, но имейте в виду, что вы все еще приносите незашифрованную информацию в "чистую" память, поэтому есть точка, в которой она может быть скомпрометирована (что в некотором роде нарушает цельvar secStr = new SecureString(); foreach (byte b in decryptedBytes) { secStr.AppendChar((char)b); } return secStr;
SecureString
).** обновление **
A
byte[]
вашей конфиденциальной информации не является безопасным. Вы можете посмотреть на него в памяти и увидеть информация (особенно если это просто строка). Отдельные байты будут находиться в точном порядке строки, поэтому "читать" ее довольно прямолинейно.Я сам (на самом деле около часа назад) просто боролся с этим же вопросом, и, насколько я знаю, нет хорошего способа перейти прямо от дешифратора к
SecureString
, если только дешифратор не запрограммирован специально для поддержки этой стратегии.
Я думаю, что проблема может быть в вашем методе
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;
Основываясь на кодировании ответа гориллы , я попробовал следующий метод
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
для прямого возврата ASecureString
?Edit: Да, это работает:
var secStr = new SecureString(); foreach (byte b in decryptedBytes) { var c = (char)b; if ('\0' == c) { continue; } secStr.AppendChar(c); } return secStr;
Edit: исправление: это работает с простыми старыми английскими строками. Шифрование и последующая попытка расшифровать строку
"標準語 明治維新 english やった"
не работает должным образом, потому что полученная расшифрованная строка, используя этот методforeach (byte b in decryptedBytes)
, не соответствует исходной незашифрованной строке.Edit: используя следующие работы для обоих:
var secStr = new SecureString(); foreach (char c in ByteConverter.GetChars(decryptedBytes)) { secStr.AppendChar(c); } return secStr;
Это все еще оставляет a массив байтов и массив символов пароля в памяти, который отстой. Может быть, мне следует найти другой класс RSA, который возвращает A
SecureString
. : /
Использовать Систему.Кодирование.По умолчанию.Метода getString GetString MSDN
Что делать, если вы придерживаетесь UTF-16?
Внутри .NET (и, следовательно, SecureString) использует UTF-16 (двойной байт) для хранения содержимого строки. Вы можете воспользоваться этим и перевести ваши защищенные данные на два байта (т. е. 1 символ) за один раз...
Когда вы шифруете, снимите символ и используйте кодировку.UTF16.GetBytes (), чтобы получить ваши два байта,и вставьте эти два байта в поток шифрования. В обратном случае, когда вы читаете из зашифрованного потока, считывайте по два байта за раз, и UTF16.GetString (), чтобы получить свой символ.
Это, вероятно, звучит ужасно, но это удерживает все символы вашей секретной строки от того, чтобы быть все в одном месте, и это дает вам надежность символа "size" (вам не придется угадывать, является ли следующий одиночный байт символом или маркером UTF для двойного символа). Наблюдатель не может знать, какие символы идут рядом с какими и в каком порядке, поэтому угадать секрет почти невозможно.
Честно говоря, это просто предположение идея... Я собираюсь попробовать его сам и посмотреть, насколько он жизнеспособен. Моя цель-создать методы расширения (SecureString.Шифрование и ICrypto.ToSecureString, или что-то в этом роде).