Продление клиентского сертификата (текущий сертификат, подписанное тело с новым запросом сертификата)
POST /d/v1/renew
https://devices.tanba-ooh.kz/d/v1/renew
Вызов проходит как heartbeat: клиентский сертификат и подпись тела ключом устройства. Ключ в запросе должен быть новым; собственная подпись запроса сертификата доказывает владение им. Платформа отказывает отозванному устройству, сертификату, который не самый новый у устройства, плохому или прежнему ключу и сертификату, у которого осталось больше 60 дней. Ответ несет новый сертификат (тот же идентификатор устройства, один год) и old_valid_until. Прежний сертификат принимается еще 24 часа. Плеер продлевает сертификат сам, когда остается меньше 30 дней.
Аутентификация
Клиентский сертификат устройства (взаимный TLS) и подпись тела ключом устройства.
Параметры
| Имя | Где | Тип | Обязательный | Описание | Пример |
|---|---|---|---|---|---|
X-Tanba-Signature | заголовок | string | да | Подпись ES256 точных байтов тела ключом устройства, сырые r||s (64 байта), base64url без заполнения. | q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw |
Тело запроса
application/json
| Имя | Тип | Обязательный | Описание | Пример |
|---|---|---|---|---|
csr | string | да | Запрос сертификата для нового ключа, PEM или base64 от DER; ECDSA P-256. | "MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA" |
force | boolean | Не применять правило 60 дней; только после разрешения администратора на 24 часа. | false |
Пример
{
"csr": "MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA",
"force": false
}
Ответы
200 Новый сертификат выдан; прежний принимается еще 24 часа.
application/json
| Имя | Тип | Обязательный | Описание | Пример |
|---|---|---|---|---|
device_id | string (uuid) | да | Тот же идентификатор устройства. | "5c0e8f3a-71d2-4b6e-9a14-d8e2f7b30c59" |
certificate_pem | string | да | Новый сертификат в формате PEM. | "-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n" |
chain_pem | string | да | Цепочка до центра сертификации устройств. | "-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n" |
not_after | string (date-time) | да | Когда истекает новый сертификат. | "2028-10-14T09:00:00Z" |
old_valid_until | string (date-time) | да | До какого момента еще принимается прежний сертификат. | "2026-10-15T09:00:00Z" |
Пример
{
"device_id": "5c0e8f3a-71d2-4b6e-9a14-d8e2f7b30c59",
"certificate_pem": "-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n",
"chain_pem": "-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n",
"not_after": "2028-10-14T09:00:00Z",
"old_valid_until": "2026-10-15T09:00:00Z"
}
Ошибки
Ошибки приходят документом problem (application/problem+json) со стабильным полем code.
| Статус | Значение |
|---|---|
| 401 | Подпись не проходит либо устройство неизвестно или отозвано. |
| 409 | В продлении отказано (renewal_refused): срок не пришел, сертификат не самый новый или ключ уже занят. |
| 422 | Ошибки полей (validation_failed). |
| default | Ошибка в виде документа problem. |
Примеры кода
curl
curl -X POST 'https://devices.tanba-ooh.kz/d/v1/renew' \
-H 'X-Tanba-Signature: q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw' \
-H 'Content-Type: application/json' \
-d '{
"csr": "MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA",
"force": false
}'
Python
import requests
response = requests.post(
"https://devices.tanba-ooh.kz/d/v1/renew",
headers={
"X-Tanba-Signature": "q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw",
"Content-Type": "application/json",
},
json={
"csr": "MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA",
"force": False,
},
)
print(response.status_code, response.text)
Go
package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{
"csr": "MIIBEjCBuAIBADAZMRcwFQYDVQQDEw5leGFtcGxlLXBsYXllcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA",
"force": false
}`)
req, err := http.NewRequest("POST", "https://devices.tanba-ooh.kz/d/v1/renew", body)
if err != nil {
panic(err)
}
req.Header.Set("X-Tanba-Signature", "q3J2y0uO8e1m4n7pXc5Zk9d2Wv6Tg0hLsB-aRfN1YjE6uHxPzD3oVtKiC8wMbQe2nSgA4rUl7yFhJ9XcT0dZaw")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(data))
}