我有一个方法,当前返回从字节数组转换的字符串:
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;
但是,使用this SecureString equality tester时,生成的SecureString
不等于原始未加密文本构造的SecureString
。我的加密和解密方法之前有效,当我在任何地方使用string
时,我也测试了SecureString
等式代码,所以我很确定这里的问题是我在尝试将byte[]
转换为SecureString
。我应该采用另一种方法来使用RSA加密,这样我可以在解密时取回SecureString
吗?
编辑:我不想将字节数组转换为常规字符串,然后将该字符串填充到SecureString
中,因为这似乎打败了the point of using a SecureString
第一名。但是,Decrypt
返回byte[]
是否也很糟糕,然后我尝试将该字节数组填充到SecureString
中?我的猜测是,如果Decrypt
返回byte[]
,那么这是传递敏感信息的安全方式,因此将数据的一个安全表示转换为另一个安全表示似乎没问题。
答案 0 :(得分:3)
char和byte可以与cast一起使用,所以修改你的第二块代码:
var secStr = new SecureString();
foreach (byte b in decryptedBytes)
{
secStr.AppendChar((char)b);
}
return secStr;
这应该可以正常工作,但请记住,你仍然将未加密的信息带入内存中的“清晰”信息,因此有一点可以让它受到损害(哪种方式会使{{{ 1}})。
** 更新 **
SecureString
您的敏感信息不安全。您可以在内存中查看它并查看信息(特别是如果它只是一个字符串)。单个字节将按照字符串的确切顺序排列,因此“读取”非常简单。
我(实际上大约一个小时前)我自己正在努力解决这个问题,据我所知,除非解密器是专门编程的,否则没有好办法直接从解密器转到byte[]
支持这一战略。
答案 1 :(得分:3)
我认为问题可能是您的ByteConvert.GetChars
方法。我在MSDN文档中找不到该类或方法。我不确定这是错字还是本土功能。无论如何,它很可能无法正确解释字节的编码。相反,请使用UTF8Encoding's GetChars method。它将正确地将字节转换回.NET字符串,假设它们最初是从.NET字符串对象加密的。 (如果没有,您会希望在encoding上使用与原始字符串匹配的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;
答案 2 :(得分:0)
基于Coding Gorilla's answer,我在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;
这仍然会在内存中留下一个字节数组和一个密码的char数组,这很糟糕。也许我应该找到另一个返回SecureString
的RSA类。 :/
答案 3 :(得分:0)
如果你坚持使用UTF-16怎么办?
在.NET内部(因此,SecureString)使用UTF-16(双字节)来存储字符串内容。您可以利用这一点并一次将受保护的数据转换为两个字节(即1个字符)......
加密时,剥离Char,然后使用Encoding.UTF16.GetBytes()获取两个字节,并将这两个字节压入加密流。相反,当您从加密流中读取时,一次读取两个字节,并使用UTF16.GetString()来获取您的字符。
它可能听起来很糟糕,但是它保留了你的秘密字符串中的所有字符都在一个地方,并且它为你提供了字符的可靠性&#34; size&#34; (你不必猜测下一个单字节是char,还是双宽字符的UTF标记)。观察者无法知道哪些角色与哪个角色有关,也不知道哪个角色,所以猜测这个秘密几乎是不可能的。
老实说,这只是一个建议的想法......我即将自己尝试,看看它有多可行。我的目标是生成扩展方法(SecureString.Encrypt和ICrypto.ToSecureString,或类似的东西)。
答案 4 :(得分:-1)
使用System.Encoding.Default.GetString GetString MSDN