【问题标题】:PHP and Java hmac hash output matches in hex, doesn't match in raw binary. What's happening?PHP 和 Java hmac 哈希输出匹配十六进制,不匹配原始二进制。发生了什么?
【发布时间】:2012-10-01 04:55:59
【问题描述】:

我正在用 Java 开发一款将被打包为小程序的游戏,并且我正在研究网络方面的问题。我设计了一个会话流,可以满足请求频率和安全需求,而不需要使用 SSL。数据传输过程松散地基于 facebook 签署其签名令牌与 OAuth 过程一起使用的方式。这是简化的上下文:

  • 我的 php/java 实现使用 hash_hmac/javax.crypto.Mac 根据共享的、秘密的、唯一的令牌和不同的 JSON 有效负载生成隐藏签名以对有效负载进行签名
  • 两个输出必须完全匹配,因为它们是更大的编码/解码压缩方案的一部分
  • 此签名将通过 URL 与负载一起传递,用于验证负载的有效性和完整性

您可以推断,如果它们不匹配,那么由于发送的数据无效,我已经丢弃了数据包和错误。我的问题是,虽然结果的十六进制编码完美匹配,但原始二进制文件似乎永远不匹配。以下是我设置的提取的 php 和 Java 测试用例:

注意:由于 php 和 java 为 php 关联数组/java hashmaps 生成 JSON 结构的方式不同,我使用秘密的值代替字符串有效负载,以便两个字段保持一致平台之间。

PHP:

$secret = "922ec205d8e4d0ea06079d60a5336fffd9cf0aea";
$json = $secret; //json_encode($test_array);
$hmac_a = hash_hmac('sha256',$json,$secret);
$hmac_b = hash_hmac('sha256',$json,$secret,$raw=true); 
echo(htmlentities($hmac_a)."<br/>\n");
echo(htmlentities($hmac_b)."<br/>\n");

浏览器内输出:

ff21a9e468ac49863e5e992324ac8bc92f239a08100b0f329b087be16f5ad382

ÿ!©äh¬I†>^™#$¬‹É/#š2›{áoZÓ‚

Java:

Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(Charset.forName("UTF-8").encode(this.secret).array(), "HmacSHA256");
hmac.init(secret_key);
byte[] digest = hmac.doFinal(this.secret.getBytes("UTF-8"));
System.out.println(hexify(digest));
System.out.println(new String(digest,"UTF-8"));

控制台输出:

ff21a9e468ac49863e5e992324ac8bc92f239a08100b0f329b087be16f5ad382

�!��h�I�>^�#$���/#�2� {�oZӂ

当复制到 php 并告诉回显时,第二个字符串如下所示:

:�!��h�I�>^�#$���/#���2{�oZÓ,

请注意,虽然十六进制相同,但二进制不同,但从相同来源显示时包含相同的结尾 (oZÓ‚ )。实际上,它按顺序包含所有更常见的字符 (!hI>^#$/#2{oZÓ,)。我尝试将控制台输出复制到 php,然后显示为二进制字符串、常规字符串、utf8_encode'd 二进制/常规字符串以及 utf8_encode'ing $hmac_b。似乎没有什么可以使原始版本匹配。

我在 php 的 hmac 上运行了 mb_detect_encoding,它告诉我 UTF-8。我还将 javax.crypto.Mac 中的所有内容都设置为 UTF-8,并显示为 UTF-8,但没有骰子。 我知道 Java 的 UTF-8 与 php 的 UTF-8 没有什么不同,因为这违背了拥有标准字符集的概念。这是怎么回事?

注意:虽然我现在更喜欢并且能够使用十六进制版本的 URL 编码,但我仍然想知道这个字符集废话是怎么回事,以及可能如何解决它。

【问题讨论】:

    标签: java php character-encoding hmac


    【解决方案1】:

    我不是 Java 专家,但看起来你在做两件不同的事情......

    您在 PHP 中使用htmlentities(),它将ÿ 等字符转换为&amp;yulm;,而您的Java snipped 试图转储UTF-8 数据。

    为什么您实际上期望在 HMAC 之后得到有效的 UTF-8 数据? UTF-8 用于表示 Unicode 字符,而不是随机散列。

    在 PHP 中使用它:

    $secret = "922ec205d8e4d0ea06079d60a5336fffd9cf0aea";
    $json = $secret;
    $hmac_a = hash_hmac('sha256',$json,$secret);
    $hmac_b = hash_hmac('sha256',$json,$secret,$raw=true); 
    echo $hmac_a . "\n";
    echo $hmac_b . "\n";
    

    我得到以下信息(在支持 UTF-8 的终端中):

    ff21a9e468ac49863e5e992324ac8bc92f239a08100b0f329b087be16f5ad382
    �!��h�I�>^�#$���/#2{�oZӂ
    

    这完全是意料之中的。 $hmac_b 实际上是二进制被解释为 UTF-8,因此它将充满无效的 UTF-8 序列。不要指望它是字符。您最好将其视为 ISO-8859-1 输出,而不是多字节:

    ff21a9e468ac49863e5e992324ac8bc92f239a08100b0f329b087be16f5ad382
    �!��h�I�>^�#$���/#ï¿2ï¿{�oZÓ
    

    (在该输出的末尾还有一个控制字符\x82

    重点是,您是在比较梨包装中的苹果和橙子。

    【讨论】:

      猜你喜欢
      • 2013-04-05
      • 2012-10-04
      • 2017-09-06
      • 1970-01-01
      • 1970-01-01
      • 2020-04-06
      • 2013-01-27
      • 2023-03-22
      • 2019-12-27
      相关资源
      最近更新 更多