【发布时间】:2023-03-10 08:15:02
【问题描述】:
我有一个加密的标准字符串,通过“convertfrom-securestring”在 powershell 中创建并保存到文件中。后来,我用 C# 应用程序(在同一用户 ID 和同一台机器上)读取了这个加密字符串。我想将此加密字符串转换为安全字符串对象。在 powershell 中,可以使用“convertto-securestring”来做到这一点。如何在 c# 中安全地做到这一点?
我知道这个答案 (How can I use ConvertTo-SecureString),但它假定加密字符串是使用已知密钥创建的。这与在没有密钥的情况下创建加密字符串的情况不同(因此与构建它的用户 ID 和机器相关联。)
另请注意,在上述问题的 cmets 中,引用了 Microsoft 的文档,表明在未提供密钥的情况下使用 Windows 数据保护 API (DPAPI)。这篇文章 (https://msdn.microsoft.com/en-us/library/ms229741(v=vs.110).aspx) 展示了如何使用 DPAPI,但不幸的是,以 Microsoft 文章中指定的方式使用它会再次导致字符串在字符串对象中以明文形式出现(我们无法控制,相对于处置),因此以指示的方式使用它是不安全的。
实际上,这实际上是这个 (How to decrypt a string in C# which is encrypted via PowerShell) 和 How can I use ConvertTo-SecureString 中的问题的组合。
作为记录,这段代码可以工作,但我不确定将解密的字符串存储在字节和字符数组中可能会出现什么问题,即使我尽可能快地破坏它:
public static System.Security.SecureString MakeSecurityString(string pwd)
{
int lngth = pwd.Length / 2;
byte[] encrypted = new byte[lngth];
for (int i = 0; i < lngth; ++i)
{
encrypted[i] = byte.Parse(pwd.Substring(2 * i, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture);
}
byte[] decrypted = System.Security.Cryptography.ProtectedData.Unprotect(encrypted, (byte[])null, System.Security.Cryptography.DataProtectionScope.CurrentUser);
var chars = System.Text.UnicodeEncoding.Unicode.GetChars(decrypted);
for(int i=0; i<decrypted.Length; ++i)
{
decrypted[i]=0;
}
var secureString = new System.Security.SecureString();
for (int i = 0; i < chars.Length; ++i)
{
secureString.AppendChar(chars[i]);
chars[i] = '\0';
}
return secureString;
}
我认为上面的代码是安全的,除了密码在字节数组decrypted然后是字符数组chars中的明文形式的短暂时刻,尽管我们尽快将它们归零这样至少密码在退出例程后不会在内存中徘徊。我们多次调用secureString.AppendChar,正如Oguz Ozgu 在下面指出的那样,这意味着secureString 必须在内部解密然后重新加密字符串——人们会认为这是安全地完成的。有人可以评论一下上述代码的安全问题吗?
编辑:请注意,下面的代码将避免多次调用secureString.AppendChar,但它必须以“不安全”的方式运行:
SecureString secureString;
fixed (char* pChars = chars)
{
secureString = new SecureString(pChars, chars.Length);
}
for (int i = 0; i < chars.Length; ++i)
{
chars[i] = '\0';
}
return secureString;
还请注意,下面 Indigo 评论中的链接清楚地表明,这仅在 WINDOWS 上的 .NET 框架中是安全的。
【问题讨论】:
-
SecureString 的安全性如何?它在每次 AppendChar() 调用、InsertAt() 调用或 SetAt() 调用中解除对缓冲区的保护(解密),然后完成工作,并再次加密缓冲区。内存块将在很短的时间内包含未加密的数据。我认为这也是您应该实施的;尽可能缩短内存中未加密的数据。
-
是的,单个字节会一个一个地以明文形式出现。但问题是这样的:当使用单个 char 存储字符时,该单个 char 不是堆上的东西,人们需要担心它的死后生命,即它在未处理的堆内存中徘徊。第二次我们将密码之类的东西放入字符串中,在 C# 中,我们失去了对包含该字符串的内存的控制:我们无法保证字节字符串的破坏(覆盖)。它可以在内存中停留很长时间,然后进入休眠图像等。
-
我知道这是旧的,但我只想对您的代码表示感谢!它对我帮助很大:)
-
@Indigo 请注意,如果您真的很关心安全性,上面的代码可能比我最初意识到的更有缺陷:它将密码的字符(一次一个字符)放入一系列字符串
passwd,死后可能会留在记忆中。我确信有 API 可以让人们在不使用字符串的情况下正确执行此操作,我只是不知道它们,并希望一些消息灵通的程序员可以用一种更安全/更好的做事方式发表评论。
标签: c# powershell securestring