【问题标题】:Converting a byte array to string and then back again produced different results将字节数组转换为字符串然后再返回会产生不同的结果
【发布时间】:2016-12-22 16:34:14
【问题描述】:

我正在使用.net port of libsodium。哈希生成函数有两种形式,一种接受字节数组,一种接受字符串:

public static byte[] ArgonHashBinary(string password, string salt, long opsLimit, int memLimit, long outputLength = ARGON_SALTBYTES)  

public static byte[] ArgonHashBinary(byte[] password, byte[] salt, long opsLimit, int memLimit, long outputLength = ARGON_SALTBYTES)

我遇到的问题是,当输入值相同时,两种形式都会产生相同的哈希值。

var saltAsBytes = PasswordHash.ArgonGenerateSalt();
var saltAsString = Encoding.UTF8.GetString(saltAsBytes);
var tmp = Encoding.UTF8.GetBytes(saltAsString);

var hash1 = PasswordHash.ArgonHashBinary(password, saltAsString, 6, 134217728, 16);
var hash2 = PasswordHash.ArgonHashBinary( Encoding.UTF8.GetBytes(password), saltAsBytes, 6, 134217728, 16);

任何带有“PasswordHash”的东西。是 libsodium 而不是我的代码。

从上面的代码中,当我将它从字符串转换回字节数组时,字节数组。字节数组数组总是不同的长度。 ArgonGenerateSalt() 产生一个长度为 16 的字节数组。当我将它从高于其通常 ~30 的字符串转换回来时(由于产生的盐不同,每次都不同)。

我为什么要转换为 UTF8?因为这就是他们在内部做的事情: https://github.com/adamcaudill/libsodium-net/blob/master/libsodium-net/PasswordHash.cs

public static byte[] ArgonHashBinary(string password, string salt, StrengthArgon limit = StrengthArgon.Interactive, long outputLength = ARGON_SALTBYTES)
    {
      return ArgonHashBinary(Encoding.UTF8.GetBytes(password), Encoding.UTF8.GetBytes(salt), limit, outputLength);
    }

当我将盐转换为 UTF8 字符串时,散列函数将失败,因为他们正在检查字节数组的长度以确保其 16 个字节。如果我将它转换为 ASCII 字符串,它可以工作,但会产生不同的哈希(这是预期的)。

澄清这段代码中的散列部分不是问题。弄清楚为什么tmpsaltAsBytes 不同是关键。

【问题讨论】:

  • 您是否单步执行过PasswordHash 中的代码,看看哪里有什么不同?
  • @JimMischel 哪个部分?上面最重要的部分是当我生成盐然后将其转换为字符串然后再返回到字节数组时。大部分代码当然是我自己的。图书馆唯一要做的就是产生随机盐。 'tmp' 变量最关心的与'saltAsBytes' 不同。所以我认为这与散列片无关。一旦我弄清楚为什么会发生这种情况,散列部分可能会起作用。
  • 您确定字节数组只包含有效的 UTF-8 代码吗?当数组包含无效代码点时,我不确定 GetBytes() 是如何工作的。
  • @Theo:钱在哪里? :-) `var bytes = new byte[] { 255, 255, 255 }; var buf = Encoding.Unicode.GetString(bytes); var newbytes = Encoding.Unicode.GetBytes(buf); var n = String.Join(", ", newbytes); ` 返回 255、255、253、255
  • @Theo 在我的回答中查看测试用例,然后把钱给一个无家可归的人。

标签: c# hash encoding character-encoding


【解决方案1】:

将字节数组转换为字符串然后再返回会产生不同的结果

二进制数据不能转换成字符串再转换回字节数组 使用Encoding.[AnyEncoding].GetBytesEncoding.[AnyEncoding].GetString

改为使用Convert.ToBase64StringConvert.FromBase64String

您可以轻松测试...

var bytes = new byte[] { 255, 255, 255 }; 
var buf = Encoding.UTF8.GetString(bytes);
var newbytes = Encoding.UTF8.GetBytes(buf);

newbytes 的长度为 9.....

编辑:这是@Theo的测试用例

var bytes = new byte[] { 0, 216 }; //any new byte[] { X, 216 };
var buf = Encoding.Unicode.GetString(bytes);
var newbytes = Encoding.Unicode.GetBytes(buf); //253,255

【讨论】:

  • 其实在问这个问题之前也有这个想法,可惜没用。将字符串传递给哈希函数时,长度将为 != 16,这会导致发生“SaltOutOfRangeException”异常
  • @coding4fun 我不明白你的意思,但你需要一个 reversable 在字符串和任意二进制数据之间的函数......使用编码不是正确的方法。 .. 您还有其他选择,例如 BitConverter.ToStringSoapHexBinary 但 base64 编码是最自然的...
  • @coding4fun BTW:您应该编码一个长度为 16 的字节数组并转换为 Base64 字符串...它的反转将再次是一个 16 字节字节数组
  • 在内部将其转换为 'UTF8' 后,您传递给 'ArgonGenerateSalt' 的 'string' 版本的盐必须是 16 字节长。获取库生成的盐并将其转换为 Base64 字符串会使其大于 16,从而引发异常。
【解决方案2】:

我认为这里的问题是 ArgonGenerateSalt 方法没有返回 UTF8 编码的字符串,而是 returns completely random bytes

您不能将随机字节解码为 UTF8 字符串并期望它往返。一个简单的例子来看看这在哪里爆炸是执行以下操作:

var data = new byte[] { 128 };
var dataAsString = Encoding.UTF8.GetString( data );
var dataAsBytes = Encoding.UTF8.GetBytes( dataAsString );

在此之后,dataAsBytes 将是 3 个字节(具体为 239、191、189)。

【讨论】:

  • 这让你认为 `ArgonHashBinary' 的字符串版本的 'salt' 参数永远不能由库生成。你必须自己生产。如果 true 似乎是一个糟糕的设计。
  • @coding4fun 这似乎确实是一个重大的疏忽。
猜你喜欢
  • 1970-01-01
  • 2011-12-18
  • 2011-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-01
相关资源
最近更新 更多