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
)
关键是,你在梨包装中比较苹果和橙子。