7. Підписання HTTP Request Message на рівні застосунку

Редакція OpenBanking Ukraine

Цей розділ описує опціональний для ASPSP (застосовується виключно для PIS) механізм підписання HTTP-запиту на рівні застосунку. Якщо ASPSP вимагає цей захід безпеки, API client зобов’язаний підписувати HTTP-запити; HTTP-відповіді ASPSP не підписує, якщо інше не зазначено в документації ASPSP.

Підписання здійснюється з використанням профілю підпису на основі JSON Web Signature — RFC7515 (з урахуванням [OBEsign] як найкращої практики).

7.1. Сертифікати для використання

Для підписання HTTP-запиту на рівні застосунку API client повинен використовувати кваліфікований сертифікат електронної печатки (QSealC), виданий кваліфікованим надавачем електронних довірчих послуг.

QSealC не використовується для ідентифікації TPP; ідентифікація TPP має здійснюватись на підставі QWAC.

7.2. Підписання HTTP Message на основі RFC7515

Наведений нижче профіль підпису відповідає вимогам RFC7515 (JSON Web Signature).

7.2.1. Розширення до HTTP message

Path

Немає.

Header параметри

До header HTTP-request повідомлення мають бути додані наступні параметри:

АтрибутТипУмоваОпис
DigestStringConditionalМістить хеш-значення, обчислене за вмістом body повідомлення HTTP-request. Див. розділ 7.2.2.1.
x-jws-signatureStringConditionalJSON Web Signature, що містить закодований у base64url захищений header JWS та закодоване у base64url значення підпису JWS (розділені ..). Див. розділ 7.2.2.2.

Примітка до рядка «умова»: якщо HTTP-request має бути підписаний за допомогою цього профілю підпису, обидва атрибути є обов’язковими.

Message body

Немає.

7.2.2. Як створити розширення

7.2.2.1. Атрибут Digest

Коли API client включає підпис згідно з цим профілем підпису, він також має включити header Digest, як визначено в RFC3230. Header Digest містить хеш message body.

Якщо message не містить body, header Digest повинен містити хеш порожнього списку байтів.

Єдині алгоритми хешування, які можуть бути використані для обчислення Digest у контексті цієї специфікації, це SHA-256 і SHA-512, як визначено в RFC5843.

Примітка: у випадку multipart (багатокомпонентного) повідомлення використовується той самий метод для обчислення хешу. Тобто, обчислюється хеш (всього) message body, включаючи всі частини багатокомпонентного повідомлення, а також роздільники.

7.2.2.2. Атрибут x-jws-signature

Атрибут x-jws-signature містить JSON Web Signature і складається з таких трьох елементів:

ЕлементТипУмоваОпис
JWS Protected HeaderStringMandatoryМістить закодований у base64url захищений header JWS.
DelimiterStringMandatoryПостійний рядок ...
JWS Signature ValueStringMandatoryМістить закодоване у base64url значення підпису JWS.
JWS Protected Header

JWS Protected Header — JSON-структура, яка визначає, як створюється підпис. Вона містить такі елементи:

ЕлементТипУмоваОпис
typStringMandatoryФіксований рядок "JOSE".
b64BooleanMandatoryПараметр згідно з RFC7797, що вказує, що корисне навантаження не потребує повторного кодування base64url. Згідно з [OBEsign], цей елемент має бути включений і встановлений на false.
x5cStringConditionalСертифікат, використаний API client для підписання HTTP-request. Сертифікат має бути включений у форматі base64url. Цей елемент може включати повний ланцюжок сертифікатів до довірчого кореневого сертифікату. Див. опцію 10 у [OBEsign]. Цей елемент має бути частиною JWS Protected Header, якщо сертифікат ще не відомий ASPSP. ASPSP може вимагати, щоб цей елемент був включений (має бути зазначено на порталі ASPSP).
x5t#S256StringConditionalХеш-значення сертифіката API client, використаного для підписання request. Для обчислення цього хеш-значення має використовуватися SHA-256. Хеш-значення має бути включено у форматі base64url. Цей елемент має бути частиною JWS Protected Header, якщо і тільки якщо елемент x5c не є частиною цього JWS Protected Header.
x5uStringОпціональнийURI, що вказує на ресурс, звідки може бути отриманий сертифікат X.509 (з або без шляху сертифікації). Цей елемент може бути частиною JWS Protected Header тільки якщо елемент x5t#S256 також є частиною JWS Protected Header.
critListMandatoryСписок назв елементів JWS Protected Header, які позначені як критичні. Згідно з [OBEsign], цей елемент має бути включений і містити наступний список: ["b64", "sigT", "sigD"]. Інші елементи не повинні бути позначені як критичні.
sigTDateTimeMandatoryЗаявлений час підписання. Час має бути в UTC (закінчуватися на Z) і вказувати дату та час до секунди.
sigDJSONMandatoryМає містити два піделементи: pars і mid. Піделемент pars містить список параметрів HTTP header, які використовуються для створення даних для підпису. Імена параметрів HTTP header мають бути вказані у нижньому регістрі в цьому списку. Піделемент mid ідентифікує механізм, що використовується для ідентифікації даних для підпису; має містити фіксоване значення http://uri.etsi.org/19182/HttpHeaders. Обмеження щодо pars: список має містити щонайменше "digest" і "x-requestid". Умовно список має включати: "api-contract-id" (якщо і тільки якщо API-ContractID включено як header HTTP-request); "psu-id" (якщо PSU-ID включено як header HTTP-request); "psu-corporate-id" (якщо PSU-Corporate-ID включено як header HTTP-request); "client-redirect-uri" (якщо і тільки якщо Client-Redirect-URI включено як header HTTP-request). Список може включати додаткові параметри header HTTP.
algStringMandatoryІдентифікує криптографічний алгоритм, використаний для створення значення JWS Signature. Згідно з [OBEsign], має бути включений і не повинен мати значення "none". Ідентифікатори слід використовувати (якщо можливо) згідно з RFC7518. Обмеження: алгоритм має ідентифікувати той самий алгоритм для підпису, як описано для відкритого ключа (Subject Public Key Info) у сертифікаті (вже відомому ASPSP або міститься в x5c) цього запиту. В іншому випадку запит має бути відхилений.
audStringMandatoryПараметр згідно з RFC7519, що містить метод і path HTTP-request у вигляді string. Ціль ідентифікується відносним URI, починаючи з /v2/. Якщо HTTP-request містить request параметри, вони також включаються в цей елемент. Приклади: "POST /v2/deferred-paymentscredit-transfers", "GET /v2/deferred-payments/credit-transfers/3d9a81b3-a47d-4130-8765-a9c0ff861100/initiations?dateFrom=2024-04-15".

Примітки:

  • Елемент aud згідно з RFC7519 є розширенням до [OBEsign]. Він використовується для захисту методу та відносного path HTTP-request за допомогою підпису.
  • Якщо використовується елемент x5t#S256: для поточної версії алгоритм SHA-256 має використовуватися для обчислення хеш-значення сертифіката.
  • Якщо використовується елемент x5t#S256: рекомендується, щоб довірена сторона перевірила хеш-значення в x5t#S256 проти сертифіката API client (вже відомого ASPSP за попередніми домовленостями, які не охоплюються цим документом).
    • Примітка: якщо використовується елемент x5t#S256, хеш-значення може використовуватися як ідентифікатор ключа для ідентифікації сертифіката/публічного ключа, який раніше обмінювався між API client і ASPSP.
  • Для елемента sigD рекомендація-23 з [OBEsign] не дотримується, тобто (request-target), Content-Type, Content-Encoding і Host можуть не бути частиною піделемента pars.
  • Для елемента sigD ASPSP може не вимагати включення додаткових параметрів header HTTP. API client може вирішити, які параметри HTTP header включати до генерації підпису, за умови дотримання вищезазначених обмежень.
  • Для елемента alg ASPSP може ввести додаткові обмеження щодо алгоритмів, які мають використовуватися; ASPSP має зазначити це на порталі.
JWS Signature Value

Підпис має бути обчислений за допомогою алгоритму, вказаного в елементі alg JWS Protected Header. Використовуваний ключ має бути приватним ключем, що відповідає сертифікату (наприклад, QSealC) API client.

Щоб обчислити підпис, виконайте наступні кроки:

  1. Закодуйте JSON-структуру JWS Protected Header у формат base64url.
  2. Зберіть параметри HTTP header, як зазначено в піделементі pars елемента sigD JWS Protected Header (використовуючи послідовність, визначену вмістом pars), щоб сформувати рядок.
  3. Для імені HTTP header, що міститься в елементі pars, створіть header-поле string, об’єднавши ім’я поля header в нижньому регістрі, двокрапку :, пробіл і значення поля header. Будь-які початкові та кінцеві пробіли видаляються. Якщо є кілька екземплярів одного і того ж поля header, всі значення поля header мають бути об’єднані, розділені комою ASCII і пробілом , , і використовуватися в порядку, в якому вони з’являються в переданому HTTP message.
  4. Вставте символ нового рядка після всіх, крім останнього значення header HTTP.
  5. Об’єднайте рядки полів header.

Див. 5.2.8.2 ETSI TS 119 182-1 для цієї процедури.

  1. Підготуйте вхідні дані для підпису, об’єднавши JWS Protected Header у форматі base64url (крок 1) з результатом рядка з кроку 2, розділеними ..
  2. Обчисліть значення JWS Signature, використовуючи вхідні дані для підпису з кроку 6, застосовуючи алгоритм підпису, визначений елементом alg JWS Protected Header.

7.2.3. Приклад

Застереження: наступний приклад є лише інформативною частиною цієї специфікації. Його мета — показати приклад різних кроків для створення підписаного HTTP message, починаючи із заданого HTTP message. Це не слід використовувати як орієнтир для реалізацій підписування HTTP message.

Використані в цьому прикладі RSA-ключі мають довжину 768 біт (занизько для реальних застосувань, але достатньо для ілюстративних цілей). Використаний сертифікат — самопідписаний X.509.

Вихідні дані для прикладу

RSA keys

Modulus (hex)n97555bcfbb51d5a001dbcb343a56b61d18ff5ae0c64379cd b148838d0280b5dfc785eb4c0bcb9707e30bbbe683eca309 73d29afe7af469f6e1a60af9fc9e350637d9084b42cb1169 4d8ff15deeef1810a473d41ecba5d2cad9da91b7069d5403
Public exponent (hex)e3
Private exponent (hex)d1938e4a29f384e4555a4a1de09b91e5a2ed539d0210b3ef7 9d8c15ecd5c01e4ff69651e201f743d6a5d749fbc0a77081 51b190736e31904a2e0cf8857290349d667ef674664c699c 4601410c3aa50dc89b05d268c07c44a664da691928b605af
First prime factor (hex)рc3e31710d5a7cba9da2feede9ebfc331abfcea1bd77d90b7 8bdd525f571bfcbe6f42a44cb44bb8d2c4385c7ddd5968a7
Second prime factor (hex)qc5c6213910233c8ff32848faae7d362424e2577505830708 1dab18b537f4c89e930e415d94707e11b883bea234ffc945

Примітка: багато мов програмування (наприклад, Python і Rust) дозволяють створення пари RSA key з вищезазначених даних (наприклад, за допомогою бібліотеки OpenSSL).

X.509 certificate

ПолеЗначення
Certificate in PEM format-----BEGIN CERTIFICATE----- MIIB5DCCAW6gAwIBAgIUFNIhspmc7vzDwkYUfYdwpc2ElKQwDQYJKoZIhvcNAQEL BQAwUjELMAkGA1UEBhMCREUxDDAKBgNVBAgMA05SVzENMAsGA1UEBwwEQm9ubjEQ MA4GA1UECgwHRXhhbXBsZTEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjcxMTEx MTExMTExWhcNMjgxMTEwMTExMTExWjBSMQswCQYDVQQGEwJERTEMMAoGA1UECAwD TlJXMQ0wCwYDVQQHDARCb25uMRAwDgYDVQQKDAdFeGFtcGxlMRQwEgYDVQQDDAtl eGFtcGxlLmNvbTB6MA0GCSqGSIb3DQEBAQUAA2kAMGYCYQCXVVvPu1HVoAHbyzQ6 VrYdGP9a4MZDec2xSIONAoC138eF60wLy5cH4wu75oPsowlz0pr+evRp9uGmCvn8 njUGN9kIS0LLEWlNj/Fd7u8YEKRz1B7LpdLK2dqRtwadVAMCAQMwDQYJKoZIhvcN AQELBQADYQBCX1mw+zkSCrJY2/o9CcBQ6GBa1I+FlGbV6z1xhP/WRsZD/VE2DBhs +lE+JKR53O1A53uSmjUz6+PD0eoxw57U3D5eew3O8x8fd/M4dVe+eftAyculvO8Q q/xPzmX3Pw0= -----END CERTIFICATE-----
Certificate SHA-256 hash (hex)f041622e4e2d689d272edc6dbda66b9cfc5cef85f878fa89d27bddbaf0dca712
Certificate SHA-256 hash (base64)8EFiLk4taJ0nLtxtvaZrnPxc74X4ePqJ0nvduvDcpxI=

Примітка: приклади полів сертифіката наведені у документі “Профілі сертифікатів і вимоги політики TSP для відкритого банківського обслуговування (ETSI TS 119 495 V1.5.1 (2021-04), IDT)”, затвердженого наказом ДП “Український науководослідний і навчальний центр проблем стандартизації, сертифікації та якості” від 29 листопада 2022 року № 232 (далі – ДСТУ ETSI TS 119 495).

У додатку до Положення про використання електронних довірчих послуг під час отримання надавачами платіжних послуг доступу до рахунків користувачів платіжних послуг (пункт 4) вказано Перелік відомостей, що містяться у кваліфікованому сертифікаті відкритого банкінгу.

Крок 0: Візьміть HTTP message (HTTP header + HTTP body)

Опис: підготуйте непідписане HTTP message для функції JWS.

Примітка: кожен рядок закінчується парою символів повернення та переведення рядка (0x0D0A), включаючи останній рядок.

POST /v2/payments/credit-transfers

HTTP/1.1 Host: api.testbank.com

Content-Type: application/json

X-Request-ID: 99391c7e-ad88-49ec-a2ad-99ddcb1f7721

PSU-IP-Address: 192.168.8.78

PSU-GEO-Location: GEO:52.506931,13.144558

PSU-User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0

Date: Mon, 26 Oct 2020 11:24:37 GMT

{

"instructedAmount": {"currency": "EUR", "amount": "123.50"}, "debtorAccount": {"iban": "DE40100100103307118608"},

"creditor": {"name": "Merchant123"},

"creditorAccount": {"iban": "DE02100100109307118603"}, "remittanceInformationUnstructured": ["Ref Number Merchant"]

}

Примітка: рядок Gecko/20100101 Firefox/54.0 фактично є частиною рядка PSU-User-Agent (тобто перед Gecko немає символу CRLF).

Крок 1: Створення JWS Protected Header

Опис: створіть параметри JWS header, які визначають, як створюється підпис:

  • "b64": false означає, що не потрібно кодувати дані header, які підписуються, у форматі base64url.
  • "x5t#S256": "…." — хеш сертифіката підпису (закодований у base64url).
  • "crit": ["sigT","sigD","b64"].
  • "sigT": "…." — заявлений час підпису.
  • "sigD": {….} — поля заголовка HTTP у нижньому регістрі, які мають бути підписані.
  • "alg": "RS256" — алгоритм підпису.

Примітка: нижче наведено у форматі pretty print. Це буде відправлено як один рядковий рядок без розбиттів на рядки або додаткових пробілів. Символи екранування не використовуються.

{
  "b64": false,
  "x5t#S256": "dytPpSkJYzhTdPXSWP7jhXgG4kCOWIWGiesdzkvNLzY",
  "crit": ["sigT", "sigD", "b64"],
  "sigT": "2020-10-26T11:26:57Z",
  "sigD": {
    "pars": ["x-request-id", "digest"],
    "mId": "http://uri.etsi.org/19182/HttpHeaders"
  },
  "alg": "RS256"
}

Крок 2: Закодуйте JWS Protected Header у формат Base64url

Опис: перетворіть JWS Protected Header (без розривів рядків або зайвих пробілів) у рядок, закодований у форматі base64url.

Закодований рядок Base64url:

eyJiNjQiOmZhbHNlLCJ4NXQjUzI1NiI6IjhFRmlMazR0YUowbkx0eHR2YVpyblB4Yzc0WDRlUHF KMG52ZHV2RGNweEk9IiwiY3JpdCI6WyJzaWdUIiwic2lnRCIsImI2NCJdLCJzaWdUIjoiMjAyMC 0xMC0yNlQxMToyNjo1N1oiLCJzaWdEIjp7InBhcnMiOlsieC1yZXF1ZXN0LWlkIiwiZGlnZXN0I l0sIm1JZCI6Imh0dHA6Ly91cmkuZXRzaS5vcmcvMTkxODIvSHR0cEhlYWRlcnMifSwiYWxnIjoi UlMyNTYifQ

Крок 3a: Обчислення дайджесту HTTP body

Опис: обчисліть хеш HTTP body (вміст без HTTP header та наступного порожнього рядка).

Для наведеного вище прикладу body, SHA-256 буде:

Hash of the body (hex)98420e321d1e9514e464261450dce761f57d546d4c4efef1c1313d4bcc8da632
Hash of the body (base64)mEIOMh0elRTkZCYUUNznYfV9VG1MTv7xwTE9S8yNpjI=

Крок 3b: Збір HTTP header для підпису

Опис: створіть рядок header HTTP, як вибрано за допомогою параметра header JWS sigD, включаючи Digest (закодований у base64).

x-request-id: 99391c7e-ad88-49ec-a2ad-99ddcb1f7721

digest: SHA-256=mEIOMh0elRTkZCYUUNznYfV9VG1MTv7xwTE9S8yNpjI=

Примітка: пам’ятайте (з опису значення підпису JWS наприкінці розділу 7.2.2.2), що кожен рядок HTTP header закінчується символом переведення рядка (0x0A), але не останній рядок.

Крок 4: Підготовка вхідних даних для обчислення значення підпису

Опис: об’єднайте закодований у форматі base64url JWS Protected Header з HTTP header для підпису, розділеними ., готовими для обчислення значення підпису.

eyJiNjQiOmZhbHNlLCJ4NXQjUzI1NiI6IjhFRmlMazR0YUowbkx0eHR2YVpyblB4Yzc0WDRlUHF KMG52ZHV2RGNweEk9IiwiY3JpdCI6WyJzaWdUIiwic2lnRCIsImI2NCJdLCJzaWdUIjoiMjAyMC 0xMC0yNlQxMToyNjo1N1oiLCJzaWdEIjp7InBhcnMiOlsieC1yZXF1ZXN0LWlkIiwiZGlnZXN0I l0sIm1JZCI6Imh0dHA6Ly91cmkuZXRzaS5vcmcvMTkxODIvSHR0cEhlYWRlcnMifSwiYWxnIjoi UlMyNTYifQ.x-request-id: 99391c7e-ad88-49ec-a2ad-99ddcb1f7721

digest: SHA-256=mEIOMh0elRTkZCYUUNznYfV9VG1MTv7xwTE9S8yNpjI=

Крок 5: Обчислення значення підпису JWS

Опис: обчисліть криптографічне значення цифрового підпису над послідовністю октетів, отриманих з JWS Protected Header та даних header HTTP, які потрібно підписати. Підпис створюється за допомогою ключа підпису, пов’язаного з сертифікатом, зазначеним у JWS Protected Header x5t#S256, і з використанням алгоритму підпису, визначеного alg.

RSA Signature для прикладу:

Hex70a068522c727079cd0de57387f64622d67f39626dc22db21a67e0fa299b 689637bc405fc8d589bd3cee565aa93e2e11cbd3987315ac6bde241cd2eb 25cdb7dca24868b13f8857f76370ada532846d3cdf1cd856ab8f9d37ae0e 04ba8f31d70d
base64URLcKBoUixycHnNDeVzh_ZGItZ_OWJtwi2yGmfg-imbaJY3vEBfyNWJvTzuVlq pPi4Ry9OYcxWsa94kHNLrJc233KJIaLE_iFf3Y3CtpTKEbTzfHNhWq4-dN6 4OBLqPMdcN

Крок 6: Створення JSON Web Signature

Опис: створіть JSON Web Signature, що містить закодований у форматі base64url JWS Protected Header, .. та значення підпису JWS. Це закодовано за допомогою компактної серіалізації JWS, причому дані header HTTP для підпису від’єднані від підпису.

eyJiNjQiOmZhbHNlLCJ4NXQjUzI1NiI6IjhFRmlMazR0YUowbkx0eHR2YVpyblB4Yzc0WDRlUHF KMG52ZHV2RGNweEk9IiwiY3JpdCI6WyJzaWdUIiwic2lnRCIsImI2NCJdLCJzaWdUIjoiMjAyMC 0xMC0yNlQxMToyNjo1N1oiLCJzaWdEIjp7InBhcnMiOlsieC1yZXF1ZXN0LWlkIiwiZGlnZXN0I l0sIm1JZCI6Imh0dHA6Ly91cmkuZXRzaS5vcmcvMTkxODIvSHR0cEhlYWRlcnMifSwiYWxnIjoi UlMyNTYifQ..cKBoUixycHnNDeVzh\_ZGItZ\_OWJtwi2yGmfg-imbaJY3vEBfyNWJvTzuVlqpPi4 Ry9OYcxWsa94kHNLrJc233KJIaLE\_iFf3Y3CtpTKEbTzfHNhWq4-dN64OBLqPMdcN

Крок 7: Вставте JSON Web Signature у HTTP Message для формування підписаного HTTP Signed Message

Опис: HTTP message, відправлений по мережі з вставленим JSON Web Signature.

POST /v2/payments/credit-transfers HTTP/1.1

Host: api.testbank.com

Content-Type: application/json

X-Request-ID: 99391c7e-ad88-49ec-a2ad-99ddcb1f7721

PSU-IP-Address: 192.168.8.78

PSU-GEO-Location: GEO:52.506931,13.144558

PSU-User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0 Date: Mon, 26 Oct 2020 11:24:37 GMT

Digest: SHA-256=mEIOMh0elRTkZCYUUNznYfV9VG1MTv7xwTE9S8yNpjI= x-jws-signature: eyJiNjQiOmZhbHNlLCJ4NXQjUzI1NiI6IjhFRmlMazR0YUowbkx0eHR2YV pyblB4Yzc0WDRlUHFKMG52ZHV2RGNweEk9IiwiY3JpdCI6WyJzaWdUIiwic2lnRCIsImI2NCJdL CJzaWdUIjoiMjAyMC0xMC0yNlQxMToyNjo1N1oiLCJzaWdEIjp7InBhcnMiOlsieC1yZXF1ZXN0LWlkIiwiZGlnZXN0Il0sIm1JZCI6Imh0dHA6Ly91cmkuZXRzaS5vcmcvMTkxODIvSHR0cEhlYWR lcnMifSwiYWxnIjoiUlMyNTYifQ..cKBoUixycHnNDeVzh\_ZGItZ\_OWJtwi2yGmfg-imbaJY3vE BfyNWJvTzuVlqpPi4Ry9OYcxWsa94kHNLrJc233KJIaLE\_iFf3Y3CtpTKEbTzfHNhWq4-dN64OB LqPMdcN

{

"instructedAmount": {"currency": "EUR", "amount": "123.50"},

"debtorAccount": {"iban": "DE40100100103307118608"},

"creditor": {"name": "Merchant123"},

"creditorAccount": {"iban": "DE02100100109307118603"},

"remittanceInformationUnstructured": "Ref Number Merchant"

}

Примітка: як і раніше, рядок Gecko/20100101 Firefox/54.0 фактично є частиною рядка PSU-User-Agent.