我正在用 Java 开发一个游戏,该游戏将被打包为一个小程序,并且我正在研究网络方面。我设计了一个会话流,可以满足请求频率和安全需求,而不需要使用 SSL。数据传输过程松散地基于 facebook 签署其签名令牌与 OAuth 过程一起使用的方式。这是简化的上下文:
- 我的 php/java 实现使用 hash_hmac/javax.crypto.Mac 基于共享的、秘密的、唯一的令牌和不同的 JSON 有效负载生成一个模糊的签名来签署有效负载
- 两个输出必须完全匹配,因为它们是更大的编码/解码压缩方案的一部分
- 此签名将通过 URL 与有效负载一起传递,并用于验证有效负载的有效性和完整性
正如您可以推断的那样,如果它们不匹配,那么由于发送的数据无效,我已经丢弃了数据包和错误。我的问题是,虽然结果的十六进制编码完美匹配,但原始二进制文件似乎永远不匹配。以下是我设置的提取的 php 和 Java 测试用例:
注意:由于 php 和 java 为 php 关联数组/java hashmaps 生成 JSON 结构的方式不同,我使用 secret 的值代替字符串有效负载,以便两个字段在平台之间保持一致。
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Ó‚</p>
爪哇:
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Ó,</p>
请注意,虽然十六进制相同,但二进制不同,但从相同来源显示时包含相同的结尾 (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 编码,但我仍然想知道这个字符集废话是怎么回事,以及可能如何解决它。