簡體   English   中英

C# - RSACryptoServiceProvider解密為SecureString而不是字節數組

[英]C# - RSACryptoServiceProvider Decrypt into a SecureString instead of byte array

我有一個方法,當前返回從字節數組轉換的字符串:

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嗎?

編輯:我不想將字節數組轉換為常規字符串,然后將該字符串填充到SecureString ,因為這似乎首先打敗了使用SecureString的點 但是, Decrypt返回byte[]並且然后我嘗試將該字節數組填充到SecureString也是不好的? 我的猜測是,如果Decrypt返回一個byte[] ,那么這是一種傳遞敏感信息的安全方法,因此將數據的一個安全表示轉換為另一個安全表示似乎沒問題。

我認為問題可能是你的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;

char和byte可以與cast一起使用,因此修改你的第二塊代碼:

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

return secStr;

這應該可以正常工作,但請記住,您仍然將未加密的信息帶入內存中的“清除”,因此有一點可以讓它受到損害(哪種類型會破壞SecureString的目的)。

** 更新 **

敏感信息的byte[]不安全。 您可以在內存中查看它並查看信息(特別是如果它只是一個字符串)。 單個字節將按照字符串的確切順序排列,因此“讀取”它非常簡單。

我(實際上大約一個小時前)我自己正在努力解決同樣的問題,據我所知,沒有好辦法直接從解密器到SecureString除非解密器專門編程支持這種策略。

根據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字符。 就像編碼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) "標準語 明治維新 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類。 :/

如果你堅持使用UTF-16怎么辦?

在內部,.NET(以及SecureString)使用UTF-16(雙字節)來存儲字符串內容。 您可以利用這一點並一次將受保護的數據轉換為兩個字節(即1個字符)...

加密時,剝離Char,然后使用Encoding.UTF16.GetBytes()獲取兩個字節,並將這兩個字節壓入加密流。 相反,當您從加密流中讀取時,一次讀取兩個字節,並使用UTF16.GetString()來獲取您的char。

它可能聽起來很糟糕,但它保留了你的秘密字符串中的所有字符都在一個地方,並且它為你提供了字符“大小”的可靠性(你不必猜測下一個字節是否為char,或雙寬字符的UTF標記。 觀察者無法知道哪些角色與哪個角色有關,也無法以何種順序進行,因此猜測秘密幾乎是不可能的。

老實說,這只是一個建議的想法......我即將自己嘗試,看看它有多可行。 我的目標是生成擴展方法(SecureString.Encrypt和ICrypto.ToSecureString,或類似的東西)。

使用System.Encoding.Default.GetString GetString MSDN

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM