PHP 的 hash_hmac 使用什么字符串编码?

What string encoding is used by PHP's hash_hmac?

PHP 有一个方法 hash_hmac 使用给定的密钥和算法计算给定字符串的 HMAC 签名。但 HMAC 在技术上对二进制数据进行操作,PHP 将其所有参数作为字符串。它如何将这些字符串转换为二进制数据?

它只是 UTF-8(用于字符串文字)。

您可以在字符串中放入任何您想要的编码,hash_hmac() 不使用任何特定的编码,只是您的字符串具有的任何编码。

这里 an example from Wikipedia 使用 UTF-8 编码和 运行 二进制的 HMAC 算法:

HMAC_MD5("key", "The quick brown fox jumps over the lazy dog") = 80070713463e7749b90c2dc24911e275

这里是等效的 PHP 代码的结果,它得到相同的响应:

php > echo hash_hmac('md5', "The quick brown fox jumps over the lazy dog", "key");
80070713463e7749b90c2dc24911e275

简短回答:字符串编码只是 元数据 附在一堆二进制数据上。 PHP 字符串只是一团, 必须跟踪其余部分。

长答案:

PHP 采用 Honey Badger 方法进行本机字符串编码,换句话说,“PHP 不关心”。你给它一个字节序列,它存储它们。它没有编码的概念,直到你想使用一个关心它的函数。即使 then 你也需要 明确地 声明输入和输出编码,否则 PHP 将使用其配置的默认值,这通常不是什么任何人都想要。

function nice_hex($in) {
    return implode(' ', str_split(bin2hex($in), 2));
}

$utf8     = "You owe me €5.";
$utf16le  = mb_convert_encoding($utf8, 'utf-16le',   'utf-8');
$utf16be  = mb_convert_encoding($utf8, 'utf-16be',   'utf-8');
$iso88591 = mb_convert_encoding($utf8, 'iso-8859-1', 'utf-8');
$cp1252   = mb_convert_encoding($utf8, 'cp1252',     'utf-8');

var_dump(
    $utf8,
    nice_hex($utf8),
    hash_hmac('md5', $utf8, 'foo'),
    $utf16le,
    nice_hex($utf16le),
    hash_hmac('md5', $utf16le, 'foo'),
    $utf16be,
    nice_hex($utf16be),
    hash_hmac('md5', $utf16be, 'foo'),
    $iso88591,
    nice_hex($iso88591),
    hash_hmac('md5', $iso88591, 'foo'),
    $cp1252,
    nice_hex($cp1252),
    hash_hmac('md5', $cp1252, 'foo')
);

输出:

string(16) "You owe me €5."
string(47) "59 6f 75 20 6f 77 65 20 6d 65 20 e2 82 ac 35 2e"
string(32) "7724135d91c43906f8730a26dcd76ffb"
string(28) "You owe me � 5."
string(83) "59 00 6f 00 75 00 20 00 6f 00 77 00 65 00 20 00 6d 00 65 00 20 00 ac 20 35 00 2e 00"
string(32) "f4a2347b4a1336dae1db21554c54b9e2"
string(28) "You owe me  �5."
string(83) "00 59 00 6f 00 75 00 20 00 6f 00 77 00 65 00 20 00 6d 00 65 00 20 20 ac 00 35 00 2e"
string(32) "b0c1a98d8b853e6568bae513d764a029"
string(14) "You owe me ?5."
string(41) "59 6f 75 20 6f 77 65 20 6d 65 20 3f 35 2e"
string(32) "301a0fb55e23285904413323d10cc774"
string(14) "You owe me �5."
string(41) "59 6f 75 20 6f 77 65 20 6d 65 20 80 35 2e"
string(32) "fa1ee73d39e1a70fe2cde7a8c5bbf0ba"

之所以看起来如此,是因为:

  1. Whosebug 使用 UTF-8。
  2. 我的编辑器使用 UTF-8。
  3. 我的控制台使用 UTF-8。
  4. 事实上 PHP 不关心字符串编码,这让我很容易产生像上面这样的任意编码的垃圾输出。

补充推荐阅读:UTF-8 all the way through

有趣的事实: PHP6 从未结束的原因之一是因为他们想包括本机多字节字符串编码,但没有人就什么达成一致味道应该是。最终他们只是放弃了整个事情并把它留给了我们,就像在 PHP5.

中一样