1

我实际上是在尝试为我在嵌入式设备上使用 Visual C# .NET 2.0 开发的应用程序实现一个非常简单的登录机制。经过一番研究,我在 msdn 上发现了一个执行密码哈希的代码示例:

如何存储密码

不幸的是,当我尝试使用它时,该代码示例在byte.Parse对十六进制字符串的子字符串的调用上引发了 FormatException SaltValue。我真的很难理解为什么,因为我没有对代码进行任何更改。

这是代码:

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Globalization;

private const int SaltValueSize = 4;

private static string GenerateSaltValue()
{
UnicodeEncoding utf16 = new UnicodeEncoding();

if (utf16 != null)
{
    // Create a random number object seeded from the value
    // of the last random seed value. This is done
    // interlocked because it is a static value and we want
    // it to roll forward safely.

    Random random = new Random(unchecked((int)DateTime.Now.Ticks));

    if (random != null)
    {
        // Create an array of random values.

        byte[] saltValue = new byte[SaltValueSize];

        random.NextBytes(saltValue);

        // Convert the salt value to a string. Note that the resulting string
        // will still be an array of binary values and not a printable string. 
        // Also it does not convert each byte to a double byte.


        //Original line :
        //string saltValueString = utf16.GetString(saltValue);
        //Replaced by :
        string saltValueString = utf16.GetString(saltValue, 0, SaltValueSize);

        // Return the salt value as a string.

        return saltValueString;
    }
}

return null;
}

private static string HashPassword(string clearData, string saltValue, HashAlgorithm hash)
{
UnicodeEncoding encoding = new UnicodeEncoding();

if (clearData != null && hash != null && encoding != null)
{
    // If the salt string is null or the length is invalid then
    // create a new valid salt value.

    if (saltValue == null)
    {
        // Generate a salt string.
        saltValue = GenerateSaltValue();
    }

    // Convert the salt string and the password string to a single
    // array of bytes. Note that the password string is Unicode and
    // therefore may or may not have a zero in every other byte.

    byte[] binarySaltValue = new byte[SaltValueSize];

    //FormatException raised here
    binarySaltValue[0] = byte.Parse(saltValue.Substring(0, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
    binarySaltValue[1] = byte.Parse(saltValue.Substring(2, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
    binarySaltValue[2] = byte.Parse(saltValue.Substring(4, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);
    binarySaltValue[3] = byte.Parse(saltValue.Substring(6, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture.NumberFormat);

//...
//Some more code
//...
}
}

我只改变了一行:

string saltValueString = utf16.GetString(saltValue);

string saltValueString = utf16.GetString(saltValue, 0, SaltValueSize);

因为该方法的第一个版本似乎不适用于嵌入式 C#。但无论如何,我已经在不改变这一行的情况下进行了测试(在非嵌入式环境中),它仍然引发了 FormatException。

我已经SaltValueSize从其他 msdn 代码示例(相关)中复制了值: 如何验证密码

引发异常的测试:

HashPassword("youpi", null, new SHA1CryptoServiceProvider());

4

1 回答 1

2

问题在于您的GenerateSaltValue方法不返回十六进制数字字符串。

它返回一些随机符号的字符串,这些符号可能是有效的十六进制符号,也可能不是有效的十六进制符号——对我来说,它创建了大部分是中文象形文字的字符串,这些象形文字肯定不能被Byte.Parse方法解析。

此外,您的示例与Microsoft Commerce Server有关- 我不知道它是什么。

“解决方案:”

我不确定所有这些示例想要通过这种字符串到十六进制到二进制的转换来完成什么,但是要成功执行 GenerateSaltValue 应该是这样的:

public static string ByteArrayToString(byte[] byteArray)
{
    StringBuilder hex = new StringBuilder(byteArray.Length * 2);

    foreach (byte b in byteArray)
        hex.AppendFormat("{0:x2}", b);

    return hex.ToString();
}

// Renamed GenerateSaltValue method
private static string GenerateHexSaltString()
{
    Random random = new Random();

    // Create an array of random values.
    byte[] saltValue = new byte[SaltValueSize];

    random.NextBytes(saltValue);

    string saltValueString = ByteArrayToString(saltValue);

    // Return the salt value as a string.
    return saltValueString;
}

并且您的程序将“工作”,这要归功于如何将字节数组转换为十六进制字符串,反之亦然?

但:

  • 使用 Random 创建 Salt 是个坏主意。
  • 字符串到十六进制到二进制的转换看起来更糟。
  • 和其他问题...

所以:

阅读一些真正与 C# 密码散列和加密相关的文章,例如:

C# 中的哈希和盐密码

在搜索代码示例时要非常专心——他们可以使用其他版本、平台甚至语言。祝你好运。

于 2014-07-28T18:56:50.090 回答