Проверка подписи
Наш сервер отправляет вам нотификации, которые содержат HTTP-заголовок x-request-signature
.
x-request-signature
содержит JWT-токен в формате EdDSA. Вам необходимо выполнить следующие шаги для проверки подписи:
- Проверить токен с использованием публичного ключа (выдается при подключении).
- Извлечь свойство
hash
из тела токена. - Получить SHA256 тела запроса (в формате JSON) и перевести его в HEX.
- Сравнить созданный хэш с извлеченным
hash
из подписанного токена.
Пример функции проверки подписи на разных языках
- JavaScript
- PHP
- GO
- Python
main.js
import path from 'node:path'
import { readFileSync } from 'fs'
import { createVerifier } from 'fast-jwt'
const API_PUBLIC_KEY_PATH = path.join('keys', 'api-ed25519-public.pem')
const verifyJwt = createVerifier({
algorithms: 'EdDSA',
key: readFileSync(API_PUBLIC_KEY_PATH),
})
const verifyRequest = (token, jsonBody) => {
const decoded = verifyJwt(token)
const hashedBody = createHash('sha256').update(jsonBody).digest('hex')
const verified = hashedBody === decoded.hash
return verified
}
main.php
<?php
require_once 'vendor/autoload.php';
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Signer\EdDSA;
use Lcobucci\JWT\Signer\Key\InMemory;
$config = Configuration::forAsymmetricSigner(
new EdDSA(),
InMemory::file(__DIR__ . '/public.bin'),
InMemory::file(__DIR__ . '/public.bin'),
);
function verifyRequest($token, $jsonBody) {
global $config;
try {
$jwtToken = $config->parser()->parse($token);
$signedWith = new SignedWith($config->signer(), $config->verificationKey());
$verified = $config->validator()->validate($jwtToken, $signedWith);
if ($verified) {
$hash = hash('sha256', $jsonBody);
return $hash === $jwtToken->claims()->get('hash');
}
return false;
} catch (Exception $e) {
return false;
}
}
?>
main.go
package main
import (
"crypto/ed25519"
"crypto/sha256"
"encoding/hex"
"fmt"
"io/ioutil"
"github.com/golang-jwt/jwt/v4"
)
const apiPublicKeyPath = "keys/api-ed25519-public.pem"
func verifyRequest(token string, jsonBody string) (bool, error) {
publicKeyData, err := ioutil.ReadFile(apiPublicKeyPath)
if err != nil {
return false, err
}
publicKey, err := jwt.ParseEdPublicKeyFromPEM(publicKeyData)
if err != nil {
return false, err
}
claims := jwt.MapClaims{}
jwtToken, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
return publicKey.(*ed25519.PublicKey), nil
})
if err != nil {
return false, err
}
if !jwtToken.Valid {
return false, fmt.Errorf("invalid token")
}
hasher := sha256.New()
hasher.Write([]byte(jsonBody))
hashedBody := hex.EncodeToString(hasher.Sum(nil))
hashClaim, _ := jwtToken.Claims.(jwt.MapClaims)["hash"].(string)
return hashedBody == hashClaim, nil
}
main.py
import hashlib
import jwt
from jwt.algorithms import Ed25519Algorithm
from pathlib import Path
api_public_key_path = Path("keys/api-ed25519-public.pem")
with open(api_public_key_path, "r") as key_file:
public_key = key_file.read()
def verify_request(token: str, json_body: str) -> bool:
try:
decoded = jwt.decode(token, public_key, algorithms="EdDSA")
hashed_body = hashlib.sha256(json_body.encode()).hexdigest()
verified = hashed_body == decoded["hash"]
return verified
except jwt.InvalidTokenError:
return False