2

如何X-HW-SIGNATURE在 PHP 中验证 a?

请求参数的文档内容如下:

消息头签名,必填项,表示
发送到您的接收上行消息的服务器的签名信息。

还有示例数据:

timestamp=1563105451261; nonce=:; value=E4YeOsnMtHZ6592U8B9S37238E+Hwtjfrmpf8AQXF+c=

关键是:

  • timestamp: 标准 Unix 时间戳
  • nonce: 冒号
  • value: 要加密的字符串

这是我不明白的部分:

时间戳+nonce+上行消息内容:使用HMAC-SHA256算法中设置的密码加密后,Base64编码后得到。


如何根据标头签名验证消息有效负载?

到目前为止,我尝试过的基本上是:

private function parse_request_body(): void {
    $this->rawBody = stream_get_contents(STDIN);
    if (isset($_SERVER['X-HW-SIGNATURE']) && !empty($_SERVER['X-HW-SIGNATURE'])) {
        if (! $this->hmac_verify( $this->rawBody, $_SERVER['X-HW-SIGNATURE'] )) {
            // spoof message
        }
    }
}

private function hmac_verify( string $payload, string $signature ): bool {
    // the problem obviously lies here ...
    return true;
}
4

3 回答 3

1

文档的另一页( X-HUAWEI-CALLBACK-ID) 上,我发现了类似的描述:

Base64 编码的字符串,已使用回调密钥进行了 HMAC-SHA256 加密。加密前的字符串由时间戳值、nonce 值和回调用户名组成,不带加号。


这里正在描述如何在 Android 上发送消息push.hcm.upstream。发送上游消息可能是获取有效负载以验证签名的最佳机会。发送时的服务器端过程如下:

Push Kit 服务器接收到上行消息时,会:

  • 将接收时间戳、冒号(:)和上行消息组合成一个字符数组进行加密(例如,123456789:your_data)。
  • 使用 HMAC 签名验证密钥以 HMAC-SHA256 模式加密字符数组,并将加密结果以 Base64 编码生成签名。
  • X-HW-SIGNATURE通过 HTTPS 请求标头中的和X-HW-TIMESTAMP字段 将签名和时间戳信息传输到您的应用服务器。

    您的应用服务器需要使用 HTTPS 请求头和 HMAC 签名验证密钥来验证上行消息的有效性。

无论“HMAC 签名验证密钥”是什么;占位符your_data听起来很相似,就好像(可能尚未进行 base64 编码)$payload->data将用于生成签名:

/** Concatenate the input string, generate HMAC hash with SHA256 algorithm, then encode as base64. */
private function generate_signature( int $timestamp, string $nonce, string $data_str, string $secret_key): string {
    $input = $timestamp.$nonce.$data_str;
    $hmac = hash_hmac( 'sha256', $input, $secret_key );
    return base64_encode( $hmac );
}

/** Convert the received signature string to object. */
private function to_object( string $signature ): stdClass {
    $input = str_getcsv( $signature, '; ' );
    $data = new stdClass();
    $data->timestamp = (int) str_replace('timestamp=', '', $input[0]);
    $data->nonce  = (string) str_replace(   ' nonce=', '', $input[1]);
    $data->value  = (string) str_replace(   ' value=', '', $input[2]);
    return $data;
}

public function hmac_verify( string $raw_body, string $secret_key, string $signature ): bool {

    /* Extract data-string from the raw body. */
    $payload = json_decode( $raw_body );
    $data_str = base64_decode( $payload->data );

    /* Convert the received signature string to object. */
    $signature = $this->to_object( $signature );

    /* Generate a signature which to compare to. */
    $generated = $this->generate_signature( $signature->timestamp, $signature->nonce, $data_str, $secret_key);

    /* Compare the generated with the received signature. */
    return $generated === $signature->value;
}

需要用实际的$_POST...测试一次


可以从 PushKit控制台获取“HMAC 签名验证密钥”(每个 web-hook) :

截屏

于 2022-01-13T14:13:15.593 回答
1

这就是我将如何验证签名。根据我对文档的理解。然而,这不是 100% 清楚的,因为他们没有提供一个例子,这是一个耻辱......

您应该在您的华为帐户中的某个地方拥有(或能够创建一个)密钥。

private function hmac_verify( string $payload, string $signature ): bool
{
    $secretKey = 'yoursecretkey';
    $parsedSignature = str_replace(';', '&', $signature); //'timestamp=1563105451261& nonce=:& value=E4YeOsnMtHZ6592U8B9S37238E+Hwtjfrmpf8AQXF+c='
    parse_str($parsedSignature, $signatureParts);

    // $signatureParts
    //
    // array(3) {
    //  ["timestamp"]=>
    //  string(13) "1563105451261"
    //  ["nonce"]=>
    //  string(1) ":"
    //  ["value"]=>
    //  string(44) "E4YeOsnMtHZ6592U8B9S37238E Hwtjfrmpf8AQXF c="
    // }

    $signed = hash_hmac("sha256", $signatureParts['timestamp'] + $signatureParts['nonce'] + $payload, $secretKey);

    return base64_encode($signed) === $signatureParts['value'];
}
于 2022-01-13T00:15:15.677 回答
0

感谢您提供有关此问题的信息。非常抱歉给您带来不便,现正组织研发团队补充示例代码。

X-HW-SIGNATURE字段用于检查消息是否来自华为服务。

在此处输入图像描述

在此处输入图像描述

用法:

时间戳+nonce+上行消息内容组合成一个字符串。使用配置的 HMAC HMAC-SHA256 算法和 Base64 编码,将得到的值与推送服务发送的值进行比较。如果相同,则消息来自推送服务,无需解析该字段的具体值。

于 2022-01-25T12:25:55.343 回答