PHP中的HMAC-SHA-256

7

我需要从这个字符串构建一个授权哈希:

kki98hkl-u5d0-w96i-62dp-xpmr6xlvfnjz:20151110171858:b2c13532-3416-47d9-8592-a541c208f755:hKSeRD98BHngrNa51Q2IgAXtoZ8oYebgY4vQHEYjlmzN9KSbAVTRvQkUPsjOGu4F

这个密钥用于HMAC哈希函数:

LRH9CAkNs-zoU3hxHbrtY0CUUcmqzibPeN7x6-vwNWQ=

我需要生成的授权哈希是这样的:
P-WgZ8CqV51aI-3TncZj5CpSZh98PjZTYxrvxkmQYmI=

需要注意以下几点:

  1. 签名必须采用HMAC-SHA-256,如RFC 2104中所规定。
  2. 签名必须采用Base64 URL兼容编码,如RFC 4648 Section 5(安全字母表)中所规定。

还有一些伪代码可以用于生成:

Signatur(Request) = new String(encodeBase64URLCompatible(HMAC-SHA-256(getBytes(Z, "UTF-8"), decodeBase64URLCompatible(getBytes(S, "UTF-8")))), "UTF-8")

我在PHP中尝试了各种方法,但还没有找到正确的算法。这是我现在拥有的代码:

if(!function_exists('base64url_encode')){
    function base64url_encode($data) {
        $data = str_replace(array('+', '/'), array('-', '_'), base64_encode($data));
        return $data;
    }
}

$str = "kki98hkl-u5d0-w96i-62dp-xpmr6xlvfnjz:20151110171858:b2c13532-3416-47d9-8592-a541c208f755:hKSeRD98BHngrNa51Q2IgAXtoZ8oYebgY4vQHEYjlmzN9KSbAVTRvQkUPsjOGu4F";
$sec = "LRH9CAkNs-zoU3hxHbrtY0CUUcmqzibPeN7x6-vwNWQ=";
$signature = mhash(MHASH_SHA256, $str, $sec);
$signature = base64url_encode($signature);

if($signature != "P-WgZ8CqV51aI-3TncZj5CpSZh98PjZTYxrvxkmQYmI=")
    echo "wrong: $signature";
else
    echo "correct";

它会生成这个签名:
K9lw3V-k5gOedmVwmO5vC7cOn82JSEXsNguozCAOU2c=

您可以看到,长度为44个字符是正确的。请帮我找出错误,这个简单的问题花费了我数小时,但没有解决方案。


阅读了您的问题两次后,我仍然不知道您的问题或疑问是什么。 - Charlotte Dunois
@CharlotteDunois:我需要对一个字符串和一个密钥进行HMAC哈希签名。如果我尝试使用PHP生成它,会失败或者得到错误的哈希值。我认为RFC规范没有被满足。 - Richard
我认为这主要是因为 base64url_encode。如果出于任何原因您需要对其进行urlencode,请在验证之前在服务器上使用urlencodeurldecode - Charlotte Dunois
2个回答

18

需要注意以下几点:

  1. 你的密钥是base64编码的。在使用php函数之前,你需要对其进行解码。这是你忽略掉的最重要的事情。
  2. mhash已经被Hash扩展取代。
  3. 你希望输出以自定义方式进行编码,因此需要从hmac函数中获取原始输出(默认情况下,php会将其十六进制编码)。

因此,使用hash扩展可以得到:

$key = "LRH9CAkNs-zoU3hxHbrtY0CUUcmqzibPeN7x6-vwNWQ=";
$str = "kki98hkl-u5d0-w96i-62dp-xpmr6xlvfnjz:20151110171858:b2c13532-3416-47d9-8592-a541c208f755:hKSeRD98BHngrNa51Q2IgAXtoZ8oYebgY4vQHEYjlmzN9KSbAVTRvQkUPsjOGu4F";

function encode($data) {
    return str_replace(['+', '/'], ['-', '_'], base64_encode($data));
}

function decode($data) {
    return base64_decode(str_replace(['-', '_'], ['+', '/'], $data));
}

$binaryKey = decode($key);

var_dump(encode(hash_hmac("sha256", $str, $binaryKey, true)));

输出:

string(44) "P-WgZ8CqV51aI-3TncZj5CpSZh98PjZTYxrvxkmQYmI="

非常感谢,这正是我需要的结果!问题就在于 hash_hmac 中的 true 和密钥的 base64 解码,需要使用 base64 URL。 - Richard
hash_hmac函数是否总是生成唯一的字符串? - Muhammad Usama Mashkoor
不,它的输出基于输入字符串和密钥。给它相同的密钥和字符串,它将生成相同的输出。 - weirdan

7

只需使用 PHP 中提供的 hash_hmac() 函数。

示例:

hash_hmac('sha256', $string, $secret);

这里有文档:http://php.net/manual/fr/function.hash-hmac.php

hash_hmac() 函数可用于计算哈希值,使用指定的密钥和算法。哈希值是一种用于验证数据完整性和真实性的数字签名。在 PHP 中,您可以使用 hash_hmac() 函数轻松计算哈希值。

输出结果为 d6285e20328c93ef83aa34b2318e3d14c193fc517e81f4cf13e8120a7b995899,这不是期望的答案。 - Raptor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接