我正在尝试构建一个自动化工具,用于检查我的支票账户上的最近交易(监视我妻子的购买,哈哈)。该工具通过 SSL 发布我的用户名和密码,然后抓取银行页面内容以进行交易。当然,一切都是通过 SSL 发送的,但为了使其自动化,我需要将我的银行用户名和密码存储在某个地方。
所以我开始了 .NET 字符串、安全性和 SecureString 的兔子洞。我制作了一个表格,将我的用户名和密码输入到 SecureString(逐个字符)中,确保不使用任何 System.String。然后我从 SecureString 中检索一个 char[],从中获取一个 byte[],将 char[] 归零,使用 DPAPI 和内置代码的 salt 字符串加密 byte[],将加密的 byte[] 转换为 Base64 System.String,将字节 [] 归零,并将加密字符串保存到应用程序设置中。
问题是,如何在不将其设为 System.String 的情况下将其返回到 HttpWebRequest 中的 POST 数据中。我想出的是以下函数(它还对字符进行 URL 编码而不生成字符串)。
/// <summary>
/// Retrieves, (and then zero's,) a character array from the SecureString.
/// Then optionally URL encodes the characters and returns an encoded byte array.
/// Make sure to zero out the byte array when you're done with it.
/// </summary>
/// <param name="secure">The SecureString to be retrieved.</param>
/// <param name="encodingFormat">The encoding format used to retrieve the byte array. The default is UTF-8.</param>
/// <param name="urlEncode">If true will URL encode characters which are invalid for URL's.</param>
/// <returns></returns>
public static byte[] ToByteArray(this SecureString secure, System.Text.Encoding encodingFormat, bool urlEncode)
{
if (secure == null) throw new ArgumentNullException("secure");
if (encodingFormat == null)
encodingFormat = System.Text.Encoding.UTF8;
char[] chars = new char[secure.Length];
byte[] bytesToReturn;
// copy the SecureString data into an unmanaged char array and get a pointer to it
IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(secure);
try
{
// copy the unmanaged char array into a managed char array
Marshal.Copy(ptr, chars, 0, secure.Length);
}
finally
{
Marshal.ZeroFreeCoTaskMemUnicode(ptr);
}
if (false == urlEncode)
{
bytesToReturn = encodingFormat.GetBytes(chars);
}
else
{
List<int> unsafeUrlCharIntValues = new List<int>();
unsafeUrlCharIntValues.AddRange(Enumerable.Range(32, 16)); // 32-47 Reserved Characters: ' '!?#$%&'()*+,-./
unsafeUrlCharIntValues.AddRange(Enumerable.Range(58, 7)); // 58-64 Reserved Characters: :;<=>?@
unsafeUrlCharIntValues.AddRange(Enumerable.Range(91, 6)); // 91-96 Reserved Characters: [\]^_`
unsafeUrlCharIntValues.AddRange(Enumerable.Range(123, 4)); // 123-126 Reserved Characters: {|}~
// Get a count of the unsafe URL characters to see if any processing is required and how many extra char's we'll need in the array.
int unsafeCharCount = 0;
for (int i = 0; i < chars.Length; i++)
if (unsafeUrlCharIntValues.Contains((int)chars[i]))
unsafeCharCount++;
if (unsafeCharCount < 1)
{
bytesToReturn = encodingFormat.GetBytes(chars);
}
else
{
char[] encodedChars = EncodeChars(chars);
bytesToReturn = encodingFormat.GetBytes(encodedChars);
// zero out encoded chars for security
for (int i = 0; i < encodedChars.Length; i++)
encodedChars[i] = '\0';
}
}
// zero out chars
for (int i = 0; i < chars.Length; i++)
chars[i] = '\0';
return bytesToReturn;
}
在我完成它们之后,我将 char[] 清零。在将返回的 byte[] 写入 HttpWebRequest 流后,我还要确保将它们归零。这是安全的还是 GC 会复制 char[] 和 byte[] 对象?这比使用 System.String 更好吗?我读过其他帖子说要使用不安全的代码并在非托管内存中做所有事情,但最后我仍然需要为我的 HttpWebRequest 获取一个字节 []。