4

我有一个当前返回从字节数组转换的字符串的方法:

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);
}

我正在尝试更新此方法以返回 a SecureString,但我无法将RSACryptoServiceProvider.Decryptfrom的返回值转换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. SecureString我应该采取另一条路线来使用 RSA 加密来让我在解密时取回 a吗?

编辑:我不想将字节数组转换为常规字符串,然后将该字符串填充到 aSecureString中,因为这似乎首先破坏了使用 aSecureString的意义。但是,Decrypt返回是否也很糟糕,byte[]然后我试图将该字节数组填充到SecureString? 我猜如果Decrypt返回 a byte[],那么这是一种传递敏感信息的安全方式,因此将数据的一种安全表示转换为另一种安全表示似乎没问题。

4

5 回答 5

3

char 和 byte 可以与强制转换互换使用,因此请修改第二段代码:

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

return secStr;

这应该可以正常工作,但请记住,您仍然将未加密的信息带入内存中的“清晰”,因此它可能会受到损害(这有点违背了 a 的目的SecureString)。

**更新**

您的byte[]一些敏感信息不安全。您可以在内存中查看它并查看信息(特别是如果它只是一个字符串)。各个字节将按照字符串的确切顺序,因此“读取”它非常简单。

我自己(实际上大约一个小时前)只是在为同样的问题苦苦挣扎,据我所知,没有好的方法可以直接从解密器转到解密器,SecureString除非解密器经过专门编程以支持此策略。

于 2010-12-21T20:31:35.457 回答
3

我认为问题可能出在您的ByteConvert.GetChars方法上。我在 MSDN 文档中找不到该类或方法。我不确定这是一个错字,还是一个本土功能。无论如何,它很可能没有正确解释字节的编码。相反,请使用UTF8Encoding 的 GetChars 方法。假设它们最初是从 .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;
于 2015-07-10T19:43:44.083 回答
0

根据Coding Gorilla 的回答,我在我的方法中尝试了以下Decrypt方法:

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

调试时,decryptedString1decryptedString2不相等:

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字符。不过,就像 Coding Gorilla 所说的那样,这似乎再次在一定程度上推翻了 的观点,因为敏感数据以小块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;

这仍然会在内存中留下一个字节数组和一个密码字符数组,这很糟糕。也许我应该找到另一个返回SecureString. :/

于 2010-12-21T20:42:09.890 回答
0

如果你坚持使用 UTF-16 会怎样?

在内部,.NET(因此,SecureString)使用 UTF-16(双字节)来存储字符串内容。您可以利用这一点,一次将受保护的数据翻译两个字节(即 1 个字符)......

当您加密时,剥离一个 Char,并使用 Encoding.UTF16.GetBytes() 获取您的两个字节,并将这两个字节推送到您的加密流中。相反,当您从加密流中读取时,一次读取两个字节,然后使用 UTF16.GetString() 来获取您的字符。

这可能听起来很糟糕,但它可以防止你的秘密字符串的所有字符都集中在一个地方,并且它为你提供了字符“大小”的可靠性(你不必猜测下一个单个字节是否是一个字符,或双宽字符的 UTF 标记)。观察者无法知道哪个角色与哪个角色搭配,也不知道以何种顺序,因此猜测秘密应该几乎是不可能的。

老实说,这只是一个建议的想法......我将自己尝试一下,看看它有多可行。我的目标是生成扩展方法(SecureString.Encrypt 和 ICrypto.ToSecureString,或类似的东西)。

于 2017-11-03T19:43:51.197 回答
-1

使用 System.Encoding.Default.GetString GetString MSDN

于 2010-12-21T20:26:18.130 回答