7. Підписання HTTP Request Message на рівні застосунку
Цей розділ описує опціональний для 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 повідомлення мають бути додані наступні параметри:
| Атрибут | Тип | Умова | Опис |
|---|---|---|---|
Digest | String | Conditional | Містить хеш-значення, обчислене за вмістом body повідомлення HTTP-request. Див. розділ 7.2.2.1. |
x-jws-signature | String | Conditional | JSON 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 Header | String | Mandatory | Містить закодований у base64url захищений header JWS. |
| Delimiter | String | Mandatory | Постійний рядок ... |
| JWS Signature Value | String | Mandatory | Містить закодоване у base64url значення підпису JWS. |
JWS Protected Header
JWS Protected Header — JSON-структура, яка визначає, як створюється підпис. Вона містить такі елементи:
| Елемент | Тип | Умова | Опис |
|---|---|---|---|
typ | String | Mandatory | Фіксований рядок "JOSE". |
b64 | Boolean | Mandatory | Параметр згідно з RFC7797, що вказує, що корисне навантаження не потребує повторного кодування base64url. Згідно з [OBEsign], цей елемент має бути включений і встановлений на false. |
x5c | String | Conditional | Сертифікат, використаний API client для підписання HTTP-request. Сертифікат має бути включений у форматі base64url. Цей елемент може включати повний ланцюжок сертифікатів до довірчого кореневого сертифікату. Див. опцію 10 у [OBEsign]. Цей елемент має бути частиною JWS Protected Header, якщо сертифікат ще не відомий ASPSP. ASPSP може вимагати, щоб цей елемент був включений (має бути зазначено на порталі ASPSP). |
x5t#S256 | String | Conditional | Хеш-значення сертифіката API client, використаного для підписання request. Для обчислення цього хеш-значення має використовуватися SHA-256. Хеш-значення має бути включено у форматі base64url. Цей елемент має бути частиною JWS Protected Header, якщо і тільки якщо елемент x5c не є частиною цього JWS Protected Header. |
x5u | String | Опціональний | URI, що вказує на ресурс, звідки може бути отриманий сертифікат X.509 (з або без шляху сертифікації). Цей елемент може бути частиною JWS Protected Header тільки якщо елемент x5t#S256 також є частиною JWS Protected Header. |
crit | List | Mandatory | Список назв елементів JWS Protected Header, які позначені як критичні. Згідно з [OBEsign], цей елемент має бути включений і містити наступний список: ["b64", "sigT", "sigD"]. Інші елементи не повинні бути позначені як критичні. |
sigT | DateTime | Mandatory | Заявлений час підписання. Час має бути в UTC (закінчуватися на Z) і вказувати дату та час до секунди. |
sigD | JSON | Mandatory | Має містити два піделементи: 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. |
alg | String | Mandatory | Ідентифікує криптографічний алгоритм, використаний для створення значення JWS Signature. Згідно з [OBEsign], має бути включений і не повинен мати значення "none". Ідентифікатори слід використовувати (якщо можливо) згідно з RFC7518. Обмеження: алгоритм має ідентифікувати той самий алгоритм для підпису, як описано для відкритого ключа (Subject Public Key Info) у сертифікаті (вже відомому ASPSP або міститься в x5c) цього запиту. В іншому випадку запит має бути відхилений. |
aud | String | Mandatory | Параметр згідно з 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. - Для елемента
sigDASPSP може не вимагати включення додаткових параметрів header HTTP. API client може вирішити, які параметри HTTP header включати до генерації підпису, за умови дотримання вищезазначених обмежень. - Для елемента
algASPSP може ввести додаткові обмеження щодо алгоритмів, які мають використовуватися; ASPSP має зазначити це на порталі.
JWS Signature Value
Підпис має бути обчислений за допомогою алгоритму, вказаного в елементі alg JWS Protected Header. Використовуваний ключ має бути приватним ключем, що відповідає сертифікату (наприклад, QSealC) API client.
Щоб обчислити підпис, виконайте наступні кроки:
- Закодуйте JSON-структуру JWS Protected Header у формат base64url.
- Зберіть параметри HTTP header, як зазначено в піделементі
parsелементаsigDJWS Protected Header (використовуючи послідовність, визначену вмістомpars), щоб сформувати рядок. - Для імені HTTP header, що міститься в елементі
pars, створіть header-поле string, об’єднавши ім’я поля header в нижньому регістрі, двокрапку:, пробіл і значення поля header. Будь-які початкові та кінцеві пробіли видаляються. Якщо є кілька екземплярів одного і того ж поля header, всі значення поля header мають бути об’єднані, розділені комою ASCII і пробілом,, і використовуватися в порядку, в якому вони з’являються в переданому HTTP message. - Вставте символ нового рядка після всіх, крім останнього значення header HTTP.
- Об’єднайте рядки полів header.
Див. 5.2.8.2 ETSI TS 119 182-1 для цієї процедури.
- Підготуйте вхідні дані для підпису, об’єднавши JWS Protected Header у форматі base64url (крок 1) з результатом рядка з кроку 2, розділеними
.. - Обчисліть значення JWS Signature, використовуючи вхідні дані для підпису з кроку 6, застосовуючи алгоритм підпису, визначений елементом
algJWS Protected Header.
7.2.3. Приклад
Застереження: наступний приклад є лише інформативною частиною цієї специфікації. Його мета — показати приклад різних кроків для створення підписаного HTTP message, починаючи із заданого HTTP message. Це не слід використовувати як орієнтир для реалізацій підписування HTTP message.
Використані в цьому прикладі RSA-ключі мають довжину 768 біт (занизько для реальних застосувань, але достатньо для ілюстративних цілей). Використаний сертифікат — самопідписаний X.509.
Вихідні дані для прикладу
RSA keys
| Modulus (hex) | n | 97555bcfbb51d5a001dbcb343a56b61d18ff5ae0c64379cd b148838d0280b5dfc785eb4c0bcb9707e30bbbe683eca309 73d29afe7af469f6e1a60af9fc9e350637d9084b42cb1169 4d8ff15deeef1810a473d41ecba5d2cad9da91b7069d5403 |
|---|---|---|
| Public exponent (hex) | e | 3 |
| Private exponent (hex) | d | 1938e4a29f384e4555a4a1de09b91e5a2ed539d0210b3ef7 9d8c15ecd5c01e4ff69651e201f743d6a5d749fbc0a77081 51b190736e31904a2e0cf8857290349d667ef674664c699c 4601410c3aa50dc89b05d268c07c44a664da691928b605af |
| First prime factor (hex) | р | c3e31710d5a7cba9da2feede9ebfc331abfcea1bd77d90b7 8bdd525f571bfcbe6f42a44cb44bb8d2c4385c7ddd5968a7 |
| Second prime factor (hex) | q | c5c6213910233c8ff32848faae7d362424e2577505830708 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 для прикладу:
| Hex | 70a068522c727079cd0de57387f64622d67f39626dc22db21a67e0fa299b 689637bc405fc8d589bd3cee565aa93e2e11cbd3987315ac6bde241cd2eb 25cdb7dca24868b13f8857f76370ada532846d3cdf1cd856ab8f9d37ae0e 04ba8f31d70d |
|---|---|
| base64URL | cKBoUixycHnNDeVzh_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.