PHP 中的 Azure REST Api 给予 "Given value does not match HMAC header structure"

Azure REST Api in PHP giving "Given value does not match HMAC header structure"

我正在尝试连接到 Azure Communications API 以使用 REST 端点发送 SMS 消息。

link 到 API 说明在这里:https://docs.microsoft.com/en-us/azure/azure-app-configuration/rest-api-authentication-hmac

我的PHP代码如下:

    public static function send(array $phoneNumbers, string $message)
    {
        $body = json_encode([
            'from' => config("azure.sms_phone_number"),
            'message' => $message,
            'smsRecipients' => array_map(fn ($num) => ['to' => $num], $phoneNumbers)
        ]);

        $endpoint = parse_url(config("azure.sms_endpoint"));

        $headers = [
            'Date' => gmdate("D, d M y H:i:s T"),
            'host' => $endpoint['host'],
            'x-ms-content-sha256' => base64_encode(hash('sha256', $body, true)),
            'Content-Type' => 'application/json',
            'Accept' => '*/*'
        ];

        $stringToSign = utf8_encode("POST\n" . $endpoint['path'] . "?" . $endpoint['query'] . "\n" . implode(";", array_values($headers)));

        $headers['Authorization'] = implode("&", [
            "HMAC-SHA256 Credential=" . config("azure.sms_key_id"),
            "SignedHeaders=" . implode(";", array_keys($headers)),
            'Signature=' . base64_encode(hash_hmac('sha256', $stringToSign, base64_decode(config("azure.sms_key")), true))
        ]);

        $client = new Client();

        $client->post(config("azure.sms_endpoint"), [
            'headers' => $headers,
            'body' => $body,
            'debug' => true
        ]);
    }

下面是一些变量

$body = '{"from":"+1844295xxx","message":"hi","smsRecipients":[{"to":"2019160xxx"}]};';

$stringToSign = 'POST
/sms?api-version=2021-03-07
Mon, 14 Mar 22 17:09:17 GMT;testxxxxxx.communication.azure.com;UXoK8141pppkVedAkc+eSQBqKOWciyoiq+AG/xxxxxx=;application/json;*/*;';

$headers = 
(
    [Date] => Mon, 14 Mar 22 17:09:17 GMT
    [host] => testxxxxx.communication.azure.com
    [x-ms-content-sha256] => UXoK8141pppkVedAkc+eSQBqKOWciyoiq+AG/xxxxxx=
    [Content-Type] => application/json
    [Accept] => */*
    [Authorization] => HMAC-SHA256 Credential=primaryKey&SignedHeaders=Date;host;x-ms-content-sha256;Content-Type;Accept&Signature=R8M7+fODzXaxXHbdcV5CHXiEq5R/7Fvd9VGYxxxxxxx=
)

我得到的结果是这样的:

Client error: `POST https://test.communication.azure.com/sms?api-version=2021-03-07` resulted in a `401 Unauthorized` response:
{"error":{"code":"Denied","message":"Given value does not match HMAC header structure."}}

访问密钥和名称来自此命令:

PS C:\Users\manko> az communication list-key --name scotttestingsms --resource-group smsresourcegroup
{
  "primaryConnectionString": "endpoint=https://scotttestingsms.communication.azure.com/;accesskey=E7lrdL/yyg/snSO++rbaZMEUF/bC5/0R9XBVGcFclt3fEN/MpWRb5kHB9t59NLtek9xsUYXHyAXxxxxxxx==",
  "primaryKey": "E7lrdL/yyg/snSO++rbaZMEUF/bC5/0R9XBVGcFclt3fEN/MpWRb5kHB9t59NLtek9xsUYXHyAXxxxxxx==",
  "secondaryConnectionString": "endpoint=https://scotttestingsms.communication.azure.com/;accesskey=IIK094eGVfkNG0uFii/32j+HVsEHJ4/QUOx06TVsqwLub7A/cv1AKKnkkZQbKiJKMn/KRx9o1biWQ5txxxxxx==",
  "secondaryKey": "IIK094eGVfkNG0uFii/32j+HVsEHJ4/QUOx06TVsqwLub7A/cv1AKKnkkZQbKiJKMn/KRx9o1biWQ5txxxxxx=="
}

"Given value does not match HMAC header structure."

错误表明您传递的 header 结构格式存在一些问题。

  • 请按照以下结构构建header

通信Header授权应采用以下格式:

`Authorization`: **HMAC-SHA256** `Credential`=<value>&`SignedHeaders`=<value>&`Signature`=<value>  
  • 凭据 - HMAC-SHA256 凭据
  • SignedHeaders - 在 signedHeaders 中,我们必须传递以下值
var signedHeaders = "x-ms-date;host;x-ms-content-sha256;`Content-Type`;`Accept`";

HTTP request header names, separated by semicolons, required to sign the request. These HTTP headers must be correctly provided with the request as well. Don't use white spaces.

x-ms-date - x-ms-date日期 你有通过。

  • x-ms-date header 的正确格式是 Mon, 14Mar 2022 23:39:12 GMT.

  • 主机 - 你的主机名

  • x-ms-content-sha256 - 请求 body 的 base64 编码 SHA256 散列。即使没有 body 也必须提供。 base64_encode(SHA256(body))

  • Content-Type - 你必须通过 content-type

  • 接受 - 你必须通过<MIME_type>/<MIME_subtype>

  • Signature - String-To-Sign 的 Base64 编码 HMACSHA256 哈希。它使用由 Credential 标识的访问密钥。 base64_encode(HMACSHA256(String-To-Sign, Secret))

String-To-Sign - HTTP_METHOD + '\n' + path_and_query + '\n' + signed_headers_values

  • HTTP_METHOD - 与请求一起使用的大写 HTTP 方法名称。更多信息(GET,POST, PUT, DELETE)
  • path_and_query - 请求绝对 URI 路径的串联 & Query_String.
  • signed_headers_values - Semicolon-separated 中列出的所有 HTTP 请求 header 的值 SignedHeaders

请参考您提供的MSDoc中的示例。

经过相当长的反复试验,但以下代码有效。有关详细信息,请参阅 https://docs.microsoft.com/en-us/azure/communication-services/tutorials/hmac-header-tutorial

use GuzzleHttp\Client;

    public static function send(array $phoneNumbers, string $message)
    {
        $body = [
            'from' => config("azure.sms_phone_number"),
            'message' => $message,
            'smsRecipients' => array_map(fn ($num) => ['to' => $num], $phoneNumbers)
        ];

        $endpoint = parse_url(config("azure.sms_endpoint"));

        $headers = [
            'Date' => gmdate("D, d M Y H:i:s T"),
            'host' => $endpoint['host'],
            'x-ms-content-sha256' => base64_encode(hash('sha256', json_encode($body), true)),
        ];

        $stringToSign = utf8_encode(implode("\n", [
            "POST",
            $endpoint['path'] . "?" . $endpoint['query'],
            implode(";", array_values($headers))
        ]));

        $headers['Authorization'] = implode("&", [
            "HMAC-SHA256 SignedHeaders=" . implode(";", array_keys($headers)),
            'Signature=' . base64_encode(hash_hmac('sha256', $stringToSign, base64_decode(config("azure.sms_key")), true))
        ]);

        $client = new Client();  // <-- this is guzzle

        $response = $client->post(config("azure.sms_endpoint"), [
            'headers' => $headers,
            'json' => $body
        ]);

    }

你只需要三个数据

  1. config("azure.sms_phone_number") 是原始 phone 号码。它必须是 E.164 格式,例如“+12024561414”
  2. config("azure.sms_endpoint") 是完整端点,例如"https://test.communication.azure.com/sms?api-version=2021-03-07"
  3. config("azure.sms_key") 是在 base64 中从 Azure 门户复制的应用程序密钥,例如“E7lrdL/yyg/snSO++rbaZMEUF/d1G/0R9XBVGch0tq3xxxxxxxxxxxx==”