使用SecureString

56

这个代码可以简化成一行吗?请随意重新编写,只要确保secureString被正确初始化。

SecureString secureString = new SecureString ();
foreach (char c in "fizzbuzz".ToCharArray())
{
    secureString.AppendChar (c);
}

1
另请参阅:https://dev59.com/mnI-5IYBdhLWcg3wio9k - H H
2
不要忘记将你的 SecureString 设为只读。 - Greg
8个回答

70

只需使用NetworkCredential,它已经内置了转换逻辑。

SecureString ss = new NetworkCredential("", "fizzbuzz").SecurePassword;

正如其他人所指出的那样,所有这些技术都会剥夺SecureString的安全性益处,但在某些情况下(例如单元测试)可能是可以接受的。

更新:

如评论中所指出的,NetworkCredential也可以用于将SecureString转换回字符串。

string s = new NetworkCredential("", ss).Password;

2
它在 .Net 4.0 之前不可用。 - rudolf_franek
1
@JohannesOvermann,both directions 是什么意思?(据我所见,NetworkCredential 可以使用明文密码或 SecureString 密码,但在前一种情况下只公开明文密码。) - CodeFox
@CodeFox:我的意思是它可以用于将String -> SecureString和SecureString -> String进行转换。我认为它总是同时消耗和暴露两者。但从你的问题中我理解到SecureString -> String不能正常工作? - Johannes Overmann
1
@JohannesOvermann,是的 - 在阅读了NetworkCredential类的MSDN文档后,这就是我的假设。我已经尝试过了,现在可以确认您最初的评论。再次感谢! - CodeFox
@CodeFox:感谢您尝试并确认它确实有效! - Johannes Overmann

53

您可以使用Linq:

"fizzbuzz".ToCharArray().ToList().ForEach(p => secureString.AppendChar(p));

我想我可以将这个放入扩展方法中,以获得我想要的结果:processInfo.Password = new SecureSring().FromString("fizzbuzz")。 - Todd Smith
31
您可以使用以下代码避免额外的 .ToList() 操作:Array.ForEach("fizzbuzz".ToCharArray(), secureString.AppendChar);(注:该代码功能与原始代码相同,只是不需要使用 .ToList() 方法将字符数组转换为列表。) - Steve Guidi
你不需要声明 ToCharArray,Linq 会自动枚举为 char 数组。 - Greg
这仍然是两行代码吗?你仍然需要声明 secureString - nagates
1
至少在较新的.NET版本(例如4.5),这是可能的:"fizzbuzz".ForEach(secureString.AppendChar) - jpaugh

31

除了使用不安全的代码和 char* 外,没有(更好的)方法。

这里的重点并不是将 SecureString 的内容复制到/从普通字符串中。常量 "fizzbuzz" 是安全漏洞所在。


1
大多数密码最初是作为字符串出现在软件中,然后需要转换为SecureString。我不确定你所说的“不要从正常字符串复制SecureString内容”的意思是什么。在正常情况下,那将是字符串密码。而“fizzbuzz”只是一种致敬。 - Todd Smith
1
是的,这大大降低了SecureString的可用性。 - H H
14
如果你正在从按键收集SecureString,那么实际上你没有原始字符串。我认为这就是SecureString的最初意图。 - Doug
1
我还没有找到一个关于如何在一开始就捕获想要保护的字符串的问答。例如,假设我从Windows表单上的密码表单控件中获取用户输入的密码。我怎样才能安全地获取这个输入?当我从控件中获取它时,它不是已经成为一个string了吗?这是否会使我的保护尝试无效?我可以在将其从控件中检索并放入我的SecureString后强制对string进行垃圾回收吗? - crush
是的,(G)UI支持几乎不存在。WPF passwordbox确实有一个securestring属性,但我不知道它的实现有多好(安全)。 - H H
显示剩余2条评论

19

用方法组替换Lambda表达式,对Sascha的答案进行轻微改进

"fizzbuzz".ToCharArray().ToList().ForEach(ss.AppendChar);

18
var s = "fizzbuzz".Aggregate(new SecureString(), (ss, c) => { ss.AppendChar(c); return ss; });

这非常优雅。 - Timothy Schoonover

9

以下是.NET中的NetworkCredential类如何执行:

SecureString secureString;
fixed (char* chPtr = plainString)
  secureString = new SecureString(chPtr, plainString.Length);

丑陋但可能是最有效的。


10
注意使用 fixed 需要在 unsafe 块中使用,而这又需要编译器开启 /unsafe 开关。 - DonBoitnott

9

由于 SecureString 使用了 IDispose 接口,您可以像这样实现。

SecureString secure = new SecureString();
foreach(var character in data.ToCharArray())
    secure.AppendChar(character);

本质上,data将会是一个参数。

如果您使用using来帮助减轻资源压力,您需要注意作用域。但根据使用情况,这可能是一个有益的替代方案。

更新:

您实际上可以使用完整的方法签名:

public static SecureString ConvertStringToSecureString(this string data)
{
     var secure = new SecureString();
     foreach(var character in data.ToCharArray())
         secure.AppendChar(character);

     secure.MakeReadOnly();
     return secure;
     
}

如果您想进行解密,则需要执行以下操作:

public static string ConvertSecureStringToString(this SecureString data)
{
     var pointer = IntPtr.Zero;
     try
     {
          pointer = Marshal.SecureStringToGlobalAllocUnicode(data);
          return Marshal.PtrToStringUni(pointer);
     }
     finally
     {
          Marshal.ZeroFreeGlobalAllocUnicode(pointer);
     }
}

以下文章将为您提供一些额外的信息。

为什么微软没有将这个加入框架中?如果无法访问该值,那么这个类就完全没用了。看起来他们(微软)要么不想让人们使用SecureString,要么比我之前指责的还要无能。 - Maxx

5

因为这个原因,我们需要尽可能少的代码,因此不需要使用.ToList()

Array.ForEach("fizzbuzz".ToCharArray(), secureString.AppendChar);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接