Робота з підписанням
Структура JWS
Типовий JWS складається з трьох частин, розділених крапками (.):
{Base64Url(header)}.{Base64Url(payload)}.{Base64Url(signature)}Де:
header — описує загальні мета данні
{
"alg": "ES256", // Алгоритм підписання
"kid": "uuid", // Ідентифікатор публічного ключа (надається від співробітника банку)
"ts" : "1763034308", // Дата та час у форматі timestamp
"targetUrl" : "/ecom/jws/payments/create/purchase_v3" // URL на який надсилається запит
}payload — дані, які потрібно передати (наприклад, інформація про користувача або дані запит).
signature — цифровий підпис, створений за допомогою приватного ключа відправника.
Приклад JWS:
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VySWQiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ.
MEYCIQDmG...Як виконується підписання
Формується header (мета дані) payload (дані запиту).
Обидві частини кодуються у формат Base64Url.
Об’єднуються в одне повідомлення:
signingInput = base64Url(header) + "." + base64Url(payload)Це повідомлення підписується приватним ключем (детальніше як отирмати ключ - Генерація ключів) за вказаним алгоритмом .
Результат (signature) додається як третя частина JWS.
Приклад генерації JWS можна переглянути на сайті https://www.jwt.io/
Приклад коду для генерації підпису:
import { CompactSign, importPKCS8 } from 'jose';
// === Дані ===
const privateKeyPem = `
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBOS+Xg3qgYSe5T4H2AoB0zWrhb0jNmcdW6o2bTXXWlQoAoGCCqGSM49
AwEHoUQDQgAEMO4qQsgPskZ4E1X6nHZU9yHnJrZ97K3QAjQ1oG1zgrht3mA4+lf5
aMqTIk1glXZpMT4pAzTURmmP2+prvQul3g==
-----END EC PRIVATE KEY-----
`;
const header = {
alg: "ES256",
kid: "9a3f6e6d-4f56-4b8b-99d3-24db19f54d8a",
ts: Math.floor(Date.now() / 1000),
targetUrl: "/ecom/jws/payments/create/purchase_v3"
};
// payload — це тіло запиту
const payload = {
merchantId: "12345",
amount: "1000.00",
currency: "UAH",
orderId: "ORD-2025-001"
};
// === Генерація JWS ===
const privateKey = await importPKCS8(privateKeyPem, 'ES256');
const encoder = new TextEncoder();
const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url');
const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64url');
const signingInput = `${encodedHeader}.${encodedPayload}`;
const sign = new CompactSign(encoder.encode(JSON.stringify(payload)))
.setProtectedHeader(header);
const jws = await sign.sign(privateKey);
console.log("JWS:", jws);
from jwcrypto import jwk, jws
import json, time, base64
# === Приватний ключ ===
private_key_pem = """-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBOS+Xg3qgYSe5T4H2AoB0zWrhb0jNmcdW6o2bTXXWlQoAoGCCqGSM49
AwEHoUQDQgAEMO4qQsgPskZ4E1X6nHZU9yHnJrZ97K3QAjQ1oG1zgrht3mA4+lf5
aMqTIk1glXZpMT4pAzTURmmP2+prvQul3g==
-----END EC PRIVATE KEY-----"""
key = jwk.JWK.from_pem(private_key_pem.encode())
header = {
"alg": "ES256",
"kid": "9a3f6e6d-4f56-4b8b-99d3-24db19f54d8a",
"ts": int(time.time()),
"targetUrl": "/ecom/jws/payments/create/purchase_v3"
}
payload = {
"merchantId": "12345",
"amount": "1000.00",
"currency": "UAH",
"orderId": "ORD-2025-001"
}
protected = base64.urlsafe_b64encode(json.dumps(header).encode()).decode().rstrip("=")
body = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip("=")
signing_input = f"{protected}.{body}"
signature = jws.JWS(json.dumps(payload))
signature.add_signature(key, protected=json.dumps(header), alg="ES256")
print("JWS:", signature.serialize(compact=True))import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jose.util.Base64URL;
import java.security.*;
import java.security.spec.*;
import java.util.*;
public class GenerateJWS {
public static void main(String[] args) throws Exception {
// Завантажуємо приватний ключ (EC P-256)
String privateKeyPEM = """
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBOS+Xg3qgYSe5T4H2AoB0zWrhb0jNmcdW6o2bTXXWlQoAoGCCqGSM49
AwEHoUQDQgAEMO4qQsgPskZ4E1X6nHZU9yHnJrZ97K3QAjQ1oG1zgrht3mA4+lf5
aMqTIk1glXZpMT4pAzTURmmP2+prvQul3g==
-----END EC PRIVATE KEY-----
""";
// (Простий приклад: створити EC ключову пару)
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
keyGen.initialize(Curve.P_256.toECParameterSpec());
KeyPair pair = keyGen.generateKeyPair();
Map<String, Object> payload = new HashMap<>();
payload.put("merchantId", "12345");
payload.put("amount", "1000.00");
payload.put("currency", "UAH");
payload.put("orderId", "ORD-2025-001");
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256)
.customParam("kid", "9a3f6e6d-4f56-4b8b-99d3-24db19f54d8a")
.customParam("ts", System.currentTimeMillis() / 1000)
.customParam("targetUrl", "/ecom/jws/payments/create/purchase_v3")
.build();
Payload jwsPayload = new Payload(new com.nimbusds.jose.util.JSONObjectUtils().toJSONString(payload));
JWSObject jwsObject = new JWSObject(header, jwsPayload);
jwsObject.sign(new ECDSASigner((ECPrivateKey) pair.getPrivate()));
System.out.println("JWS: " + jwsObject.serialize());
}
}<?php
require 'vendor/autoload.php';
use Jose\Component\Core\JWK;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\Signature\Algorithm\ES256;
use Jose\Component\Core\Util\JsonConverter;
// Приватний ключ
$privateKey = <<<EOD
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBOS+Xg3qgYSe5T4H2AoB0zWrhb0jNmcdW6o2bTXXWlQoAoGCCqGSM49
AwEHoUQDQgAEMO4qQsgPskZ4E1X6nHZU9yHnJrZ97K3QAjQ1oG1zgrht3mA4+lf5
aMqTIk1glXZpMT4pAzTURmmP2+prvQul3g==
-----END EC PRIVATE KEY-----
EOD;
$jwk = JWK::createFromPem($privateKey);
$header = [
'alg' => 'ES256',
'kid' => '9a3f6e6d-4f56-4b8b-99d3-24db19f54d8a',
'ts' => time(),
'targetUrl' => '/ecom/jws/payments/create/purchase_v3'
];
$payload = [
'merchantId' => '12345',
'amount' => '1000.00',
'currency' => 'UAH',
'orderId' => 'ORD-2025-001'
];
$jwsBuilder = new JWSBuilder(null, [new ES256()]);
$jws = $jwsBuilder
->create()
->withPayload(JsonConverter::encode($payload))
->addSignature($jwk, $header)
->build();
$serializer = new \Jose\Component\Signature\Serializer\CompactSerializer();
echo "JWS: " . $serializer->serialize($jws, 0);
Вимоги до JWS
Частина header JWS :
Токен має термін дії в 1 хвилину, якщо буде передано параметр
tsз значенням більше 1 хвилини від поточного часу, винекне помилкаЯкщо в параметрі
targetUrlбуде передане неіснуюче або невалідне значення - виникне помилкаЯкщо в параметрі
algбуде значення, що не відповідає алгоритмку згенерованого ключа - виникне помилкаЯкщо в параметі
kidбуде передане неіснуюче або невалідне значення - виникне помилка
Частина payload JWS:
Передається тіло запиту, що повинно відповідати вимогам самого запиту та його обов'язкових параметрів
Якщо в тілі запиту буде вказане значення
merchantId, що неіснує - виникне помилка
Last updated