PHP和Java hmac哈希输出匹配hex,在原始二进制文件中不匹配。 发生了什么?

我正在开发一个Java游戏,它将打包为applet,我正在研究网络方面。 我设计了一个会话流程,可以满足请求频率和安全需求,而无需使用SSL。 数据传输过程基于facebook在其OAuth流程中使用signed_token的方式。 这是简化的上下文:

  • 我的php / java实现使用hash_hmac / javax.crypto.Mac生成一个用于签署有效负载的模糊签名,基于共享,秘密,唯一令牌和各种JSON有效负载
  • 两个输出必须完全匹配,因为它们是更大的编码/解码压缩方案的一部分
  • 此签名将通过带有效负载的URL传递,并用于validation有效负载的有效性和完整性

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

注意:由于php和java如何为php关联数组/ java哈希映射生成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)."
\n"); echo(htmlentities($hmac_b)."
\n");

浏览器内输出:

ff21a9e468ac49863e5e992324ac8bc92f239a08100b0f329b087be16f5ad382

Y!©äh¬I†> ^™#$¬ {á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并告诉echo时,第二个字符串如下所示:

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

请注意,虽然hex是相同的,但二进制文件不同,但在从同一来源显示时包含相同的结尾(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没什么不同,因为它违背了标准字符集的概念。 这里发生了什么?

注意:虽然我现在更喜欢并且能够使用hex版本进行URL编码,但我仍然想知道这个字符集废话是怎么回事,可能还有如何修复它。

我不是Java专家,但看起来你做了两件不同的事……

你在PHP中使用htmlentities() ,它将像ÿ转换为&yulm; ,而你的Java剪辑试图转储出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

关键是,你在梨包装中比较苹果和橙子。