Несоответствие SHA256 HMAC WebCrypto JS

У меня есть два сценария, которые генерируют SHA256 HMAC с текстовым сообщением и ключом в кодировке Base64. Один написан на PHP, а другой - на JavaScript. Скрипт PHP возвращает правильный HMAC, но по какой-то причине версия JS - нет. Чем это вызвано?

Вот примеры кода с отредактированным (все еще похожим по сути) ключом.

PHP

      <?php

header("content-type: text/plain");

$key = "YdkQZp9Pq0OsKT5TlFzrgry7j1nw0XEmbNFm86zNU3+XFEmM/I+WxrAZE7yjFAD3iWJTQ10VN2+JK3fz4b3Viw==";
$message = "1614117737467myJSON.json" . '{"json_data": "to-be-encoded"}';

$hmac = base64_encode(hash_hmac('sha256', $message, base64_decode($key), true));

// to base64
echo base64_encode("1614117737467;" . $hmac);

Этот скрипт возвращает MTYxNDExNzczNzQ2NztFdXcwQ1l0bTBTMkdIdnZ2ZnN2ZGFkTEFDMGVPbVlJeHFzZk9PQWExS1BzPQ==

JS

      async function hash_hmac(type, message, key, base64) {
    const getUtf8Bytes = str =>
        new Uint8Array(
        [...unescape(encodeURIComponent(str))].map(c => c.charCodeAt(0))
    );

    const keyBytes = getUtf8Bytes(key);
    const messageBytes = getUtf8Bytes(message);

    const cryptoKey = await crypto.subtle.importKey(
        "raw", keyBytes, { name: "HMAC", hash: type },
        true, ["sign"]
    );

    const sig = await crypto.subtle.sign("HMAC", cryptoKey, messageBytes);

    const data = String.fromCharCode(...new Uint8Array(sig));

    return base64 ? btoa(data) : data;
}

(async function() {
    let key = "YdkQZp9Pq0OsKT5TlFzrgry7j1nw0XEmbNFm86zNU3+XFEmM/I+WxrAZE7yjFAD3iWJTQ10VN2+JK3fz4b3Viw==";
    let message = "1614117737467myJSON.json" + '{"json_data": "to-be-encoded"}';
    
    let hmac = await hash_hmac("SHA-256", message, atob(key), true);

    console.log(
        btoa("1614117737467;" + hmac)
    );
})();

Что возвращается MTYxNDExNzczNzQ2NztBeGxFRVJCTzVYWm5KN2ZHNCtoeWlxalJ0VmxZQmJpekNUSEwzcldMQVhzPQ==

Почему эти, казалось бы, одинаковые скрипты возвращают разные результаты?

1 ответ

Решение

Это связано с различиями в обработке двоичных массивов или строк в php / javascript. Если вы измените base64_decode($key) к $key (php) и atob(key) к key (javascript) он отлично работает.

Редактировать:

Ошибка в unescape(encodeURIComponent(str)), просто удалите функции и измените на str и он должен работать.

Другие вопросы по тегам