Проверка подписи в PHP

Мне нужно проверить подпись, у меня есть пример кода, который я реализовал в nodeJS и работает как шарм.

const crypto = require('crypto');

app.post('/api/verify', (req, res) => {  
    const signature = req.headers['signature']
    let rawBody = req.body;
    rawBody = JSON.stringify(rawBody);
    const result = verifySignature(signature, rawBody)
    res.send(`Signature verification status: ${result}`); // Return True
});

const verifySignature = (signature, rawBody) => {
    const publicKey = fs.readFileSync('public.pem', 'utf8');
    const verifier = crypto.createVerify('RSA-SHA256')
    verifier.update(rawBody)
    return verifier.verify({ key: publicKey, padding: constants.RSA_PKCS1_PSS_PADDING }, signature, 'base64')
}

Теперь проблема в том, что я должен проверить это в PHP (LARAVEL), для этого я использую phpseclib.

use phpseclib\Crypt\RSA;

$signature = $request->header('signature');
$bodyContent = $request->getContent();
$result = $this->verify_signature($signature , $bodyContent); // Always False

protected function verify_signature($signature, $requestBody): bool
    {
        $rsa = new RSA();
        $rsa->setSignatureMode(RSA::SIGNATURE_PSS);
        $rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
        $rsa->setHash("sha256");
        $public_key = <<<EOD
-----BEGIN PUBLIC KEY-----
XXXXXXXXXXX
-----END PUBLIC KEY-----
EOD;

        $rsa->loadKey($public_key);
        $result = $rsa->verify($requestBody , base64_decode($signature));

        if ($result) {
            return true;
        }
        return false;
    }

Может кто-нибудь, пожалуйста, дайте мне знать, что мне здесь не хватает!?

verify() в коде NodeJS по умолчанию использует RSA_PSS_SALTLEN_AUTO для длины соли, которая автоматически определяет длину соли из структуры блока PSS. Насколько я знаю, phpseclib не поддерживает это, поэтому необходимо знать длину соли. Для этого требуется код для подписи или полный образец данных (нерабочая пара ключей, сообщение, подпись).
Topaco 19.05.2023 19:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
1
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Обе стороны используют разные параметры PSS. Однако для успешной проверки должны применяться те же параметры PSS. Параметры по умолчанию, используемые на стороне NodeJS, можно найти в документации NodeJS:

  • В качестве дайджеста MGF1 по умолчанию используется дайджест PSS (последний здесь указан как SHA256).
  • В качестве длины соли verify() по умолчанию применяется RSA_PSS_SALTLEN_AUTO, которая автоматически определяет длину соли из структуры блока PSS. Насколько я знаю, phpseclib не поддерживает это, поэтому вам нужно узнать длину соли. Если подписание также выполнялось с помощью NodeJS и использовалась длина соли по умолчанию, применяется максимально возможная длина соли ( sign() использует RSA_PSS_SALTLEN_MAX_SIGN), а именно:
    signature length (bytes) - digest output length (bytes) - 2 = 256 - 32 - 2.
    В противном случае вам нужно узнать длину соли, примененную во время подписи (часто это размер вывода дайджеста, т.е. 32 байта для SHA-256).
  • Нет необходимости устанавливать режим шифрования в контексте подписи.

В PHP-коде SHA1 используется по умолчанию для обоих дайджестов, т.е. хэш MGF1 должен быть указан явно. Длина соли по умолчанию равна 0, т.е. должна (вероятно) также быть задана явно. Значения по умолчанию см. в документации phpseclib, V2.

Пример: следующий код NodeJS выполнен успешно, т. е. подпись успешно проверена. Подпись также была сгенерирована с использованием NodeJS и значения по умолчанию (т. е. максимальной длины соли):

const crypto = require('crypto');

function verifySignature(signature, rawBody) {
    const publicKey = publicKeyPem = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunF5aDa6HCfLMMI/MZLT
5hDk304CU+ypFMFiBjowQdUMQKYHZ+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1E
bYDRopyTSfkrTzPzwsX4Ur/l25CtdQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQw
KtjI43lDKvAi5kEet2TFwfJcJrBiRJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1x
H9FLojQfyia89/EykiOO7/3UWwd+MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4
OhZu+0Bo1LXloCTe+vmIQ2YCX7EatUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4i
GwIDAQAB
-----END PUBLIC KEY-----`;
    const verifier = crypto.createVerify('RSA-SHA256');
    verifier.update(rawBody);
    return verifier.verify({ key: publicKey, padding: crypto.constants.RSA_PKCS1_PSS_PADDING }, signature, 'base64');
}

const signature = "LrQcFTFvgRxC/1aSnKn+lgHDmMNsv+F1E7/u6Y2KtbbzlUuO1ehls6F/sS+G9Q+oPuVVW/6xeMr++ygOJU5LlwwsliVxxs05OBZIvRrDwoNHqipakNN8SkZWSLdAuj1j9pv1bApwQjshFjOFPA11NgZGjdDvaB5Yayt4m3jewwR4Zxvtpqcd0MV1Ja22v3zfHQqca0Oahx4g6aLAy9WCLmVuFWZRauFKx0s+k+tPUs1ssTz/equl405T6PErQ1YbfApSqg4v0RAvHu7WWVG4s3lONU+jMUyqC4OHdf0mEgweNx9Kzy6gLA8nrrQGjxdomJmQuGxjX6BTlrRQjRecGw= = ";
const msg = "The quick brown fox jumps over the lazy dog";
const verified = verifySignature(signature, msg);
console.info(verified); // true

Аналог PHP с phpseclib (V2):

use phpseclib\Crypt\RSA;

$publicKey = '-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunF5aDa6HCfLMMI/MZLT
5hDk304CU+ypFMFiBjowQdUMQKYHZ+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1E
bYDRopyTSfkrTzPzwsX4Ur/l25CtdQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQw
KtjI43lDKvAi5kEet2TFwfJcJrBiRJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1x
H9FLojQfyia89/EykiOO7/3UWwd+MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4
OhZu+0Bo1LXloCTe+vmIQ2YCX7EatUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4i
GwIDAQAB
-----END PUBLIC KEY-----';

$rsa = new RSA();
$rsa->loadKey($publicKey);
$rsa->setSignatureMode(RSA::SIGNATURE_PSS);
$rsa->setHash("sha256");
$rsa->setMGFHash("sha256");     // set MGF digest explicitly
$rsa->setSaltLength(256-32-2);  // set salt length explicitly

$msg = "The quick brown fox jumps over the lazy dog";
$signature = "LrQcFTFvgRxC/1aSnKn+lgHDmMNsv+F1E7/u6Y2KtbbzlUuO1ehls6F/sS+G9Q+oPuVVW/6xeMr++ygOJU5LlwwsliVxxs05OBZIvRrDwoNHqipakNN8SkZWSLdAuj1j9pv1bApwQjshFjOFPA11NgZGjdDvaB5Yayt4m3jewwR4Zxvtpqcd0MV1Ja22v3zfHQqca0Oahx4g6aLAy9WCLmVuFWZRauFKx0s+k+tPUs1ssTz/equl405T6PErQ1YbfApSqg4v0RAvHu7WWVG4s3lONU+jMUyqC4OHdf0mEgweNx9Kzy6gLA8nrrQGjxdomJmQuGxjX6BTlrRQjRecGw= = ";
$result = $rsa->verify($msg , base64_decode($signature));

echo $result ? 'verified' : 'unverified'; // verified

который также успешно проверяет подпись.

Большое спасибо за ваше исчерпывающее объяснение, вы спасли мой день.

Ala Shariaty 20.05.2023 16:57

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