问题 “字母数字”哈希 - A-Z,0-9


我正在寻找一个能产生“字母数字哈希”的函数。给定源字符串,它会生成一个确定的结果字符串,该字符串可以包含任何字母a-z或数字0-9,并且不能进行反向工程以生成源。这将用于基于秘密数据为系统生成密码,因此8到12个字符之间的字符串是理想的,安全散列也是理想的。

我想我可以使用正常的按位散列,将其折叠为64位(如果我使用,例如,SHA256),然后一次取5位数(产生数字0-31)并查找要从索引的有序集合中使用的字符代码。有26个字母和10个数字意味着我将不得不留下一些(如果手写的话可能删除可能被误认为其他人的字符)。 64位,一次5位,将产生一个12字符的字符串,剩下4位。

但是,我担心两件事:首先,通过采用2的非幂次数引入偏差;第二,如何处理剩余的比特。我是否因为知道只有16种可能性而使用它们,我是否会将它们关闭(并且丢失数据可能会引入偏差),或者我是否再合并一位来制作13个字符的字符串(最后一位应该在哪里来自)?

编辑: 这是我目前的努力;它需要一个可枚举的字节(比如大多数哈希算法生成的字节数组)并返回一个字符串:

    /// <summary>
    /// Converts an IEnumerable of bytes to a string representation which can have any lowercase letter a-z except for l, o, q and z, and any digit 0-9.
    /// Uses 5 bits of the byte array at a time to generate numbers from 0 to 31, which are then translated to letters or numbers.
    /// </summary>
    /// <param name="toConvert">the byte array to convert.</param>
    /// <returns>A string containing the alphanumeric case-insensitive representation of the bytes in the array.</returns>
    public static string ToInsensitiveAlphaNumericString(this IEnumerable<byte> toConvert)
    {
        var chars = new[]
                        {
                            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'p', 'r', 's', 't',
                            'u', 'v', 'w', 'x', 'y', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
                        };

        var enumerator = toConvert.GetEnumerator();
        enumerator.MoveNext();

        int buffer = enumerator.Current;
        short bufferLength = 8;
        const int valueLength = 5;

        var builder = new StringBuilder();

        while (true)
        {
            var value = buffer >> (bufferLength - valueLength);

            builder.Append(chars[value]);

            buffer = buffer - (value << (bufferLength - valueLength));
            bufferLength -= valueLength;

            if(bufferLength < valueLength )
            {
                if (enumerator.MoveNext())
                {
                    buffer = (buffer << 8) + enumerator.Current;
                    bufferLength += 8;
                }
                else
                {
                    //here's the main question; to include, or not to include?
                    if (bufferLength > 0)
                        builder.Append(chars[buffer]);
                    break;
                }
            }
        }

        return builder.ToString();
    }

5263
2017-07-27 01:05


起源



答案:


然后如何生成SHA256 Base36 编码结果?没有遗留位,没有偏见......

这样,您就拥有了经过验证的算法(记住使用盐并使用多个哈希迭代)的加密强度以及您需要的字母数字表示。


13
2017-07-27 01:09



如果你只想掩饰你用于散列的系统并且不希望结果很容易被哈希函数的输出反转,那么就这样做并对它应用凯撒转换 - 简单,陈旧,但仍然可以给出事情有趣的扭曲:) - John Mitchell
这个解决方案可以正常工作,但值得注意的是仍然存在偏见的特征。如果2 ^ {256}的base-36表示具有6作为其第一个数字(我认为它确实如此,但我只是粗略地检查过),那么每个编码值在该位置将具有0到6之间的值。 - David
@David:你会有偏见 角色的位置 但那没关系。 Base36只是一个 人性化的代表性 一个完全无偏的256位数字。 - Eric J.
是的,我完全同意。我之前提到它是因为OP提到了对偏见字符的关注(当时他已经在谈论一个完全无偏的64位数字的人类方便表示)。 - David
这是一个好主意,并且在64位数字上是微不足道的。存储在多个字节中的数字略微复杂,但仍然完全可能。 - KeithS


答案:


然后如何生成SHA256 Base36 编码结果?没有遗留位,没有偏见......

这样,您就拥有了经过验证的算法(记住使用盐并使用多个哈希迭代)的加密强度以及您需要的字母数字表示。


13
2017-07-27 01:09



如果你只想掩饰你用于散列的系统并且不希望结果很容易被哈希函数的输出反转,那么就这样做并对它应用凯撒转换 - 简单,陈旧,但仍然可以给出事情有趣的扭曲:) - John Mitchell
这个解决方案可以正常工作,但值得注意的是仍然存在偏见的特征。如果2 ^ {256}的base-36表示具有6作为其第一个数字(我认为它确实如此,但我只是粗略地检查过),那么每个编码值在该位置将具有0到6之间的值。 - David
@David:你会有偏见 角色的位置 但那没关系。 Base36只是一个 人性化的代表性 一个完全无偏的256位数字。 - Eric J.
是的,我完全同意。我之前提到它是因为OP提到了对偏见字符的关注(当时他已经在谈论一个完全无偏的64位数字的人类方便表示)。 - David
这是一个好主意,并且在64位数字上是微不足道的。存储在多个字节中的数字略微复杂,但仍然完全可能。 - KeithS


如果您只是按原样使用这些位(这样一个字符只有16种可能性),您仍然拥有完整的64位熵。如果你对64位熵感到满意(听起来像你的那样),那么没有理由认为一个角色的范围有限。

如果你有一些理由(美学?)喜欢所有的角色都有全部范围,那么你可以放弃这4位,但你将自己降低到60位的熵。如果您对8个字符的密码感到满意,那么听起来像60位也很多。

因此,无论哪个更容易应该工作正常。


0
2017-07-27 01:15