Подтвердите подпись PHP ECDSA, созданную с помощью API Web Crypto

У меня есть следующая задача. Мне нужно сгенерировать пару ключей ECDSA. Подпишите данные закрытым ключом и проверьте подпись на PHP. По какой-то неизвестной мне причине я не могу успешно проверить данные.

Вот пример моего javascript, который генерирует ключи и подписывает данные.

(async () => {

  async function arrayBufferToBase64(arrayBuffer) {
    var binary = '';
    var bytes = new Uint8Array(arrayBuffer);
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return window.btoa(binary);
  }

  let keyPair = await window.crypto.subtle.generateKey(
    {
      name: "ECDSA",
      namedCurve: "P-256",
    },
    false,
    ["sign", "verify"]);

    let data = 'text_value';

    let signature = await window.crypto.subtle.sign({
      name: "ECDSA",
      hash: { name: "SHA-256" },
    }, keyPair.privateKey, new TextEncoder().encode(data));
    
    console.info('Data: ' + data);

    console.info('Signature: ' + await arrayBufferToBase64(signature));

    console.info('Public key: ' + await arrayBufferToBase64(await window.crypto.subtle.exportKey('spki', keyPair.publicKey)));

})();

Этот код дает аналогичный результат.

Data: text_value
Signature: TfsoDx8TyuAth7SzsoagHVykRU+eNWaGOWEulrSjZ57KKa2T/8zO/7+/8TvRXLvuXSbEloRFbigzUpIlOlGMcw==
Public key: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE01iwlSEr1pLOLu2Ks9gxMhH7C3NWQ95yKO2vvv7XYfRGKAVRaLVLJ6j3J6klnNez5kWeECdJ1OhoQULEyokEZQ==

Имея данные, подпись и открытый ключ, я отправляю их в PHP для проверки подписи.

У меня есть следующий PHP-код (я использую php 8.1):

<?php

$data = 'text_value';
$signature = base64_decode("TfsoDx8TyuAth7SzsoagHVykRU+eNWaGOWEulrSjZ57KKa2T/8zO/7+/8TvRXLvuXSbEloRFbigzUpIlOlGMcw= = ");
$public_key = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE01iwlSEr1pLOLu2Ks9gxMhH7C3NWQ95yKO2vvv7XYfRGKAVRaLVLJ6j3J6klnNez5kWeECdJ1OhoQULEyokEZQ= = ";
$public_key = "-----BEGIN PUBLIC KEY-----\n$public_key\n-----END PUBLIC KEY-----";

echo "Data: $data\n\n";
echo "Public key: \n$public_key\n\n";

$verify = openssl_verify($data, $signature, $public_key, OPENSSL_ALGO_SHA256);
echo $verify === 1 ? 'Valid signature' : 'Invalid signature';

echo "\n\nopenssl_error_string(): " . openssl_error_string() . "\n\n";

Что я делаю не так ?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

ECDSA использует разные форматы подписи: ASN.1/DER и формат IEEE P1363 (r|s). WebCrypto применяет IEEE P1363, PHP/OpenSSL использует формат ASN.1/DER. Чтобы проверка прошла успешно, подпись в формате IEEE P1363 необходимо преобразовать в формат ASN.1/DER.

Подпись IEEE1363 из вашего примера имеет шестнадцатеричную кодировку (вместо кодировки Base64):

4dfb280f1f13cae02d87b4b3b286a01d5ca4454f9e35668639612e96b4a3679eca29ad93ffccceffbfbff13bd15cbbee5d26c49684456e28335292253a518c73

и после преобразования в формат ASN.1/DER:

304502204dfb280f1f13cae02d87b4b3b286a01d5ca4454f9e35668639612e96b4a3679e022100ca29ad93ffccceffbfbff13bd15cbbee5d26c49684456e28335292253a518c73

или в кодировке Base64:

MEUCIE37KA8fE8rgLYe0s7KGoB1cpEVPnjVmhjlhLpa0o2eeAiEAyimtk//Mzv+/v/E70Vy77l0mxJaERW4oM1KSJTpRjHM=

Если используется подпись, отформатированная таким образом, проверка с помощью PHP-кода проходит успешно.


В формате IEEE P1363 значения r и s подписи ECDSA (по 32 байта для P-256) просто объединяются: r|s. В другом формате оба значения закодированы ASN.1/DER.

Здесь вы найдете описание обоих форматов, объясняющее вышеуказанное преобразование. Взаимосвязь между двумя форматами также становится ясной, когда закодированная подпись ASN.1/DER загружается в анализатор ASN.1/DER, например. здесь:

Спасибо за ответ. Это именно то, что я делал неправильно. Преобразование подписи в формат ASN.1/DER позволило работать проверке на PHP.

David Pankov 20.05.2024 10:44

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