# КриптоПро УЦ 2.0 — Полная техническая документация
Полная техническая документация по интеграции КриптоПро УЦ 2.0 с 1Формой: требования к серверу, архитектура, дополнительные параметры на форме задачи, справочник функций Lua-библиотеки и C# API, процесс выпуска сертификата, отзыв, настройка кнопок МТФ и подпись PKCS#7. Для разработчиков и администраторов.

## 1. Требования к серверу
### 1.1–1.2, 1.4–1.5. Требования к ПО, сертификатам и плагинам
**1.1. Программное обеспечение**

| Компонент | Версия | Назначение |
|-----------|--------|------------|
| .NET | 9.0 x64 | Среда выполнения серверной части 1Формы |
| КриптоПро CSP | 5.0+ | Криптопровайдер ГОСТ (хранилище ключей, CAPI) |
| КриптоПро УЦ | 2.0 | REST API удостоверяющего центра |
| Windows Server | 2016+ | Хостинг IIS, хранилище сертификатов |

**1.2. Сертификаты в хранилище Windows**

В хранилище `LocalMachine\My` (или `CurrentUser\My`) должен быть установлен **сертификат администратора УЦ** с приватным ключом. Этот сертификат используется для:
- **mTLS** — клиентская аутентификация при HTTP-запросах к REST API УЦ
- **PKCS#7 подпись** — подписание запросов на выпуск/отзыв сертификатов (ГОСТ 34.10-2012)

Thumbprint этого сертификата записывается в поле `Certificate` таблицы `CryptoProCertificationCenters`.

**1.4. Библиотека CryptoProCA в SmartScripts**

Lua-библиотека `CryptoProCA` поставляется миграцией `v2.264` и хранится в таблице `SmartScripts` с признаком `IsLibrary = true`.

**1.5. Плагины ЭП на клиенте**

Для операций, выполняемых в браузере (генерация CSR, установка сертификата):
- **КриптоПро ЭЦП Browser plug-in** — работает через cadesplugin
- **Рутокен плагин** — альтернативный вариант (PKCS#11)

Выбор плагина настраивается через настройки ЭП задачи (`EdsPluginType`).

![Выбор плагина ЭП в AdminSPA: плагин по умолчанию, по категориям и группам](https://help.1forma.ru/help-images/signatures/eds_plugin_settings.png)

---
### 1.3. Таблица CryptoProCertificationCenters

Создана миграцией `v2.162`. Содержит настройки подключения к УЦ.

| Поле | Тип | Описание |
|------|-----|----------|
| `Id` | int IDENTITY | PK, используется как `raId` во всех вызовах |
| `Name` | nvarchar(max) | Название УЦ (для отображения) |
| `Address` | nvarchar(max) | Базовый URL REST API УЦ, например `https://ca.example.com` |
| `UsersFolder` | nvarchar(max) | Папка для регистрации пользователей в УЦ |
| `Certificate` | nvarchar(max) | Thumbprint сертификата администратора (из хранилища Windows) |
| `TrustedCertificateThumbprints` | nvarchar(max), NULL | Список SHA-256 отпечатков сертификата серверного УЦ через запятую. Пусто/NULL — проверка не выполняется (как раньше). Непусто — запрос проходит только при совпадении |
| `Guid` | uniqueidentifier | Уникальный идентификатор записи |

![Карточка настройки удостоверяющего центра: имя, адрес, папка пользователей, отпечаток сертификата](https://help.1forma.ru/help-images/signatures/cryptopro_center_list2.png)

Настройка: **Администрирование → CryptoPro УЦ** (через dbadmin-дерево).

![Список удостоверяющих центров КриптоПро в AdminSPA: имя, адрес, папка пользователей, отпечаток сертификата](https://help.1forma.ru/help-images/signatures/cryptopro_center_list.png)

### 1.6. Проверка серверного сертификата УЦ (по отпечаткам)

При обращении к REST API удостоверяющего центра сервер 1Формы устанавливает TLS-соединение с клиентским сертификатом администратора (mTLS). Подлинность самого сервера УЦ проверяется сверкой его сертификата со списком доверенных отпечатков из поля `TrustedCertificateThumbprints` карточки УЦ (Администрирование → CryptoPro УЦ). Отдельного интерфейса у настройки нет — поле лежит в той же карточке, значение читается при каждом запросе.

Запись — SHA-256 отпечаток: после удаления двоеточий и пробелов должно остаться ровно 64 шестнадцатеричных символа, регистр не важен, несколько значений перечисляются через запятую. Сверяются отпечаток самого сертификата сервера и отпечатки каждого доступного элемента его цепочки, поэтому отпечаток центра сертификации распространяет доверие на все выданные им сертификаты — задавайте его, только если это соответствует вашей модели доверия.

Пока список пуст или равен NULL, соединение принимается с любым сертификатом, а в журнал один раз за запуск приложения пишется предупреждение. Непустой список работает строго: несовпадение отпечатка, запись, из которой не разобралось ни одного отпечатка, или отсутствующий сертификат отклоняют запрос, и в журнал попадает ошибка с SHA-256 предъявленного сертификата. Дополнительно отклоняются сертификат с чужим именем узла и сертификат вне срока действия; ошибки цепочки доверия допускаются — благодаря этому работает приватный УЦ, корень которого не установлен в доверенные операционной системы. При замене сертификата добавьте новый отпечаток заранее, не удаляя прежний, — тогда приниматься будут оба.

Проверка действует только на REST-вызовы (`CRYPTO:send_request`). Резервные операции через SOAP (приостановка и возобновление сертификата) это поле не читают.


**Что проверить при разборе.** Если после заполнения списка все обращения к УЦ падают — сверьте длину записи: отпечаток, из которого не разобралось ни одного значения (например, скопированный из свойств сертификата в Windows SHA-1-отпечаток), закрывает соединения целиком, даже когда рядом стоит корректный SHA-256. Отказ проверки возвращается вызывающему коду как ошибка HTTP-запроса и записывается в лог автоматизаций; в журнале виден SHA-256 предъявленного сертификата.

## 2. Архитектура

Общая схема взаимодействия браузера, сервера 1Формы и REST API УЦ:

```
┌─────────────────────────────────────────────────────────────┐
│                        БРАУЗЕР                              │
│                                                             │
│  Кнопка МТФ → JS: tcCryptoLogic.newRequest()               │
│                   tcCryptoLogic.installCertificate()        │
│                   tcCryptoLogic.signFiles()                 │
│                                                             │
│  ┌─────────────────┐    ┌─────────────────┐                │
│  │ CryptoPro ЭЦП   │    │ Рутокен плагин  │                │
│  │ Browser plug-in  │    │                 │                │
│  └─────────────────┘    └─────────────────┘                │
└────────────────────────────┬────────────────────────────────┘
                             │ HTTP API
┌────────────────────────────┼────────────────────────────────┐
│                      СЕРВЕР 1Форма                          │
│                                                             │
│  Lua скрипт (SmartScript)                                   │
│    │                                                        │
│    ├── include("...CryptoProCA...")  ← Lua-библиотека       │
│    │     └── CryptoProCA.issue_certificate(...)              │
│    │                                                        │
│    ├── CRYPTO:send_request()    ← C#-функция CRYPTO         │
│    │     └── HTTP-запрос к REST API УЦ                      │
│    │           ├── mTLS (клиентский сертификат)              │
│    │           └── PKCS#7 подпись через Win32 CAPI           │
│    │                                                        │
│    ├── CRYPTO:parse_cert()      ← парсинг X509              │
│    └── CRYPTO:get_thumbprint()  ← SHA-1 отпечаток           │
│                                                             │
│                        ↓ HTTP/TLS 1.2                       │
│              КриптоПро УЦ 2.0 REST API                      │
│              /api/ra/users                                   │
│              /api/ra/certRequests                             │
│              /api/ra/certificates                             │
│              /api/ra/revRequests                              │
└─────────────────────────────────────────────────────────────┘
```

### 2.1. Слои

Уровни реализации интеграции:

| Слой | Компонент | Ответственность |
|------|-----------|-----------------|
| **Lua** | библиотека `CryptoProCA` | Бизнес-логика: опрос статуса, обработка ошибок, чтение/запись ДП |
| **Lua (низкоуровневые)** | глобальный объект `CRYPTO:*` | HTTP-запросы, парсинг сертификатов, получение thumbprint |
| **Транспорт** | — | HTTP к REST API УЦ: mTLS, PKCS#7 подпись, логирование |
| **JS (МТФ)** | `window.tcCryptoLogic` | Клиентские операции: генерация CSR, установка сертификата, подпись файлов |

---

## 3. Дополнительные параметры (ДП) на форме задачи

Для полного цикла работы с сертификатами нужны следующие ДП:

### 3.1. Обязательные ДП

Дополнительные параметры, необходимые для полного цикла работы с сертификатами:

| ДП | Тип | Заполняется | Описание |
|----|-----|-------------|----------|
| **nameAttributes** | Строка (JSON) | Вручную или скриптом | JSON с атрибутами субъекта сертификата. Формат: `{"2.5.4.3":"Иванов Иван Иванович","1.2.643.3.131.1.1":"000000000000","2.5.4.6":"RU"}` |
| **userId УЦ** | Строка | `CryptoProCA.register_or_update_user()` | UUID пользователя в УЦ. Заполняется автоматически при первой регистрации, далее используется для обновления |
| **PKCS#10 запрос** | Строка (Base64) | `tcCryptoLogic.newRequest()` (браузер) | CSR в формате Base64. Генерируется криптоплагином на клиенте |
| **certRequestId** | Строка | `CryptoProCA.issue_certificate()` | UUID запроса на сертификат в УЦ. Заполняется автоматически |
| **Сертификат** | Строка (Base64) | `CryptoProCA.issue_certificate()` | rawCertificate в Base64. Заполняется автоматически при выпуске |
| **Thumbprint** | Строка | `CryptoProCA.get_thumbprint()` | SHA-1 отпечаток сертификата. Заполняется автоматически |
| **Имя ЦС** | Строка | Вручную (опционально) | authorityName — имя центра сертификации, если в УЦ их несколько |

### 3.2. OID-ы для nameAttributes

> **Важно:** REST API КриптоПро УЦ 2.0 требует обязательное наличие полей **Country** (`2.5.4.6`) и **Locality** (`2.5.4.7`) в `nameAttributes` при регистрации пользователя (`POST /api/ra/users`). Без них запрос вернёт ошибку.

| OID | Атрибут | Обязательность | Пример значения |
|-----|---------|----------------|-----------------|
| `2.5.4.3` | CN (Common Name) | **Обязательный** | `Иванов Иван Иванович` |
| `2.5.4.6` | C (Country) | **Обязательный** (REST API v2.0) | `RU` |
| `2.5.4.7` | L (Locality) | **Обязательный** (REST API v2.0) | `Москва` |
| `2.5.4.4` | SN (Surname) | Рекомендуемый | `Иванов` |
| `2.5.4.42` | GN (Given Name) | Рекомендуемый | `Иван Иванович` |
| `2.5.4.8` | ST (State) | Опционально | `77 Москва` |
| `2.5.4.10` | O (Organization) | Опционально | `ООО "Компания"` |
| `2.5.4.11` | OU (Org Unit) | Опционально | `Отдел разработки` |
| `1.2.643.3.131.1.1` | ИНН | Опционально | `000000000000` |
| `1.2.643.100.1` | ОГРН | Опционально | `0000000000000` |
| `1.2.643.100.3` | СНИЛС | Опционально | `00000000000` |
| `2.5.4.12` | Title | Опционально | `Генеральный директор` |
| `1.2.840.113549.1.9.1` | E (Email) | Опционально | `user@example.com` |

**Минимальный JSON для nameAttributes:**
```json
{
  "2.5.4.3": "Иванов Иван Иванович",
  "2.5.4.6": "RU",
  "2.5.4.7": "Москва"
}
```

**Полный JSON (рекомендуемый):**
```json
{
  "2.5.4.3": "Иванов Иван Иванович",
  "2.5.4.4": "Иванов",
  "2.5.4.42": "Иван Иванович",
  "2.5.4.6": "RU",
  "2.5.4.7": "Москва",
  "2.5.4.8": "77 Москва",
  "2.5.4.10": "ООО \"Компания\"",
  "1.2.643.3.131.1.1": "000000000000",
  "1.2.643.100.1": "0000000000000",
  "1.2.643.100.3": "00000000000",
  "1.2.840.113549.1.9.1": "user@example.com"
}
```

### 3.3. Формат атрибутов CSR для клиентской генерации (кнопка МТФ)

> Поля Country и Locality также должны присутствовать в CSR-атрибутах, иначе сертификат не будет содержать эту информацию.

```json
[
  {"rdn": "commonName",   "cpn": "CN",                  "value": "Иванов Иван Иванович"},
  {"rdn": "country",      "cpn": "C",                   "value": "RU"},
  {"rdn": "localityName", "cpn": "L",                   "value": "Москва"},
  {"rdn": "surname",      "cpn": "SN",                  "value": "Иванов"},
  {"rdn": "givenName",    "cpn": "G",                   "value": "Иван Иванович"},
  {"rdn": "organization", "cpn": "O",                   "value": "ООО Компания"},
  {"rdn": "inn",          "cpn": "1.2.643.3.131.1.1",   "value": "000000000000"},
  {"rdn": "snils",        "cpn": "1.2.643.100.3",       "value": "00000000000"}
]
```
- `rdn` — имя атрибута для Рутокена
- `cpn` — имя атрибута для КриптоПро ЭЦП Browser plug-in

---

## 4. Справочник функций библиотеки CryptoProCA

> Подключение: `include("[cryptopro] CryptoPro CA 2.0 — REST API library")`
> или `include(ID)` где ID — числовой идентификатор скрипта в SmartScripts.

---

### 4.1. CryptoProCA.get_thumbprint

Получает SHA-1 Thumbprint сертификата, записывает в ДП и оставляет silent-комментарий.

**Заменяет C#-смарт** «Get Thumbprint»

```lua
CryptoProCA.get_thumbprint(taskId, epCertificateId, epThumbprintId)
```

| # | Параметр | Тип | Обязательный | Описание |
|---|----------|-----|:---:|----------|
| 1 | `taskId` | number | да | ID задачи. Обычно `CONTEXT.Id` |
| 2 | `epCertificateId` | number | да | ID ДП (тип «Строка»), содержащего сертификат в формате Base64 (rawCertificate) |
| 3 | `epThumbprintId` | number | да | ID ДП (тип «Строка»), куда будет записан SHA-1 Thumbprint |

**Возвращает:** `string` — Thumbprint (SHA-1, hex, uppercase)

**Побочные эффекты:**

- Записывает Thumbprint в ДП `epThumbprintId` от имени системного робота
- Добавляет silent-комментарий в задачу: `Thumbprint: ABC123...`

**Пример:**
```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")

local tp = CryptoProCA.get_thumbprint(CONTEXT.Id, 503, 505)
-- tp = "A1B2C3D4E5F6..."
```

---

### 4.2. CryptoProCA.register_or_update_user

Регистрирует нового или обновляет существующего пользователя в УЦ.

**Заменяет C#-смарт** «CryptoPro CA — new or update user»

```lua
CryptoProCA.register_or_update_user(raId, taskId, epNameAttrsId, epToSaveUserId)
```

| # | Параметр | Тип | Обязательный | Описание |
|---|----------|-----|:---:|----------|
| 1 | `raId` | number | да | ID записи в таблице `CryptoProCertificationCenters` (настройки подключения к УЦ) |
| 2 | `taskId` | number | да | ID задачи. Обычно `CONTEXT.Id` |
| 3 | `epNameAttrsId` | number | да | ID ДП (тип «Строка») с JSON `nameAttributes`. **Обязательные поля JSON:** `2.5.4.3` (CN), `2.5.4.6` (Country), `2.5.4.7` (Locality) — см. раздел 3.2 |
| 4 | `epToSaveUserId` | number | да | ID ДП (тип «Строка») для чтения/записи UUID пользователя в УЦ. Если пуст — создание, если заполнен — обновление |

**Возвращает:** `string` — UUID пользователя в УЦ

**REST API вызовы:**

- Создание: `POST /api/ra/users` с телом `{ nameAttributes: {...}, folder: "..." }`
- Обновление: `POST /api/ra/users/{userId}` с телом `{ nameAttributes: {...} }`

**Пример:**
```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")

-- ДП 200 содержит: {"2.5.4.3":"Иванов Иван","2.5.4.6":"RU","2.5.4.7":"Москва"}
-- ДП 201 пуст (новый пользователь) или содержит UUID (обновление)
local userId = CryptoProCA.register_or_update_user(1, CONTEXT.Id, 200, 201)
```

---

### 4.3. CryptoProCA.issue_certificate

Выпуск сертификата: submit + accept через двойную подпись PKCS#7, опрос, получение rawCertificate.

**Заменяет C#-смарт** «CryptoPro CA — Issue certificate»

```lua
CryptoProCA.issue_certificate(raId, taskId, epUserId, epRequestId,
    epToSaveRequestId, epToSaveCertificateId, epAuthorityId, maxPollAttempts)
```

| # | Параметр | Тип | Обязательный | Описание |
|---|----------|-----|:---:|----------|
| 1 | `raId` | number | да | ID записи в `CryptoProCertificationCenters` |
| 2 | `taskId` | number | да | ID задачи. Обычно `CONTEXT.Id` |
| 3 | `epUserId` | number | да | ID ДП (тип «Строка») с UUID пользователя в УЦ (результат `register_or_update_user`) |
| 4 | `epRequestId` | number | да | ID ДП (тип «Строка») с PKCS#10 запросом в Base64 (результат `tcCryptoLogic.newRequest`) |
| 5 | `epToSaveRequestId` | number | да | ID ДП (тип «Строка») — сюда будет записан `certRequestId` (UUID запроса в УЦ) |
| 6 | `epToSaveCertificateId` | number | да | ID ДП (тип «Строка») — сюда будет записан `rawCertificate` (Base64) |
| 7 | `epAuthorityId` | number | да | ID ДП (тип «Строка») с именем центра сертификации (`authorityName`). **Может быть пустым** — если в УЦ один ЦС |
| 8 | `maxPollAttempts` | number | нет | Количество попыток опрос (по умолчанию `10`). Каждая попытка = пауза 2 сек + GET-запрос |

**Возвращает:** `LuaTable` — полный объект сертификата из УЦ (содержит `rawCertificate`, `id`, `status` и др.)

**REST API вызовы (последовательно):**

1. `POST /api/ra/certRequests` — submit + accept, двойная PKCS#7 подпись
2. `GET /api/ra/certificates?certRequestId=...` — опрос (до `maxPollAttempts` раз)
3. `GET /api/ra/certificates/{id}` — полная форма с `rawCertificate`

**Пример:**
```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")

CryptoProCA.issue_certificate(
    1,            -- raId
    CONTEXT.Id,   -- taskId
    500,          -- ДП: userId в УЦ
    501,          -- ДП: PKCS#10 запрос
    502,          -- ДП: certRequestId (запись)
    503,          -- ДП: rawCertificate (запись)
    504           -- ДП: имя ЦС (может быть пустым)
)
```

---

### 4.4. CryptoProCA.revoke_certificate

Отзыв сертификата через REST API с двойной подписью.

**Заменяет C#-смарт** «CryptoPro CA — revoke certificate»

```lua
CryptoProCA.revoke_certificate(raId, taskId, epCertificateId, revocationReason, fromTimestamp)
```

| # | Параметр | Тип | Обязательный | Описание |
|---|----------|-----|:---:|----------|
| 1 | `raId` | number | да | ID записи в `CryptoProCertificationCenters` |
| 2 | `taskId` | number | да | ID задачи. Обычно `CONTEXT.Id` |
| 3 | `epCertificateId` | number | да | ID ДП (тип «Строка») с сертификатом в Base64 |
| 4 | `revocationReason` | number | нет | Код причины отзыва (0–5, по умолчанию `0`). См. таблицу ниже |
| 5 | `fromTimestamp` | number\|nil | нет | Unix timestamp даты начала отзыва. Если > текущего времени — отложенный отзыв с `RD=yyyy-MM-ddTHH:mm:ss` |

**Возвращает:** `string` — серийный номер отозванного сертификата

**REST API вызов:**

- `POST /api/ra/revRequests` — двойная PKCS#7 подпись, кодировка **UTF-16LE без BOM**

**Коды причин отзыва:**

| Код | Константа | Описание |
|-----|-----------|----------|
| 0 | Unspecified | Не указана |
| 1 | KeyCompromise | Компрометация ключа |
| 2 | CACompromise | Компрометация УЦ |
| 3 | AffiliationChanged | Изменение принадлежности |
| 4 | Superseded | Замена (выпущен новый) |
| 5 | CessationOfOperation | Прекращение деятельности |

**Пример:**
```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")

-- Немедленный отзыв по причине "Замена"
local sn = CryptoProCA.revoke_certificate(1, CONTEXT.Id, 503, 4)

-- Отложенный отзыв (с 01.02.2027)
CryptoProCA.revoke_certificate(1, CONTEXT.Id, 503, 0, 1801526400)
```

---

### 4.5. CryptoProCA.pause_certificate

Приостановка сертификата. **резерв через SOAP** — REST API КриптоПро УЦ 2.0 не поддерживает `RR=6`.

**Заменяет C#-смарт** «CryptoPro CA — pause certificate»

```lua
CryptoProCA.pause_certificate(taskId, raId, epCertificateId, fromTimestamp, toTimestamp, revocationReason)
```

| # | Параметр | Тип | Обязательный | Описание |
|---|----------|-----|:---:|----------|
| 1 | `taskId` | number | да | ID задачи. Обычно `CONTEXT.Id` |
| 2 | `raId` | number | да | ID записи в `CryptoProCertificationCenters` |
| 3 | `epCertificateId` | number | да | ID ДП (тип «Строка») с сертификатом в Base64 |
| 4 | `fromTimestamp` | number | да | Unix timestamp даты начала приостановки |
| 5 | `toTimestamp` | number | да | Unix timestamp даты окончания приостановки |
| 6 | `revocationReason` | number | да | Код причины (обычно `6` — CertificateHold) |

**Реализация:** на текущей версии вызывает старое смарт-действие приостановки. Когда REST API УЦ добавит поддержку — будет переписана на REST без изменения сигнатуры.

**Пример:**
```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")

-- Приостановить на месяц
CryptoProCA.pause_certificate(CONTEXT.Id, 1, 503, 1780000000, 1782000000, 6)
```

---

### 4.6. CryptoProCA.resume_certificate

Возобновление приостановленного сертификата. **резерв через SOAP** — REST API не поддерживает `RR=8`.

**Заменяет C#-смарт** «CryptoPro CA — resume certificate»

```lua
CryptoProCA.resume_certificate(taskId, raId, epCertificateId)
```

| # | Параметр | Тип | Обязательный | Описание |
|---|----------|-----|:---:|----------|
| 1 | `taskId` | number | да | ID задачи. Обычно `CONTEXT.Id` |
| 2 | `raId` | number | да | ID записи в `CryptoProCertificationCenters` |
| 3 | `epCertificateId` | number | да | ID ДП (тип «Строка») с сертификатом в Base64 |

**Реализация:** на текущей версии вызывает старое смарт-действие возобновления.

**Пример:**
```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")

CryptoProCA.resume_certificate(CONTEXT.Id, 1, 503)
```

---

## 5. Справочник низкоуровневых C#-функций CRYPTO:*
Низкоуровневые C#-функции CRYPTO:* доступны в Lua без подключения библиотеки — это глобальный объект `CRYPTO`.

### 5.1. CRYPTO:send_request

Отправка HTTP-запроса к REST API УЦ с mTLS и опциональной PKCS#7 подписью.

```lua
local resp = CRYPTO:send_request(raId, method, path, query, body, options, taskId)
```

| # | Параметр | Тип | Обязательный | Описание |
|---|----------|-----|:---:|----------|
| 1 | `raId` | number | да | ID записи в `CryptoProCertificationCenters` |
| 2 | `method` | string | да | HTTP-метод: `"GET"` или `"POST"` |
| 3 | `path` | string | да | Путь API (должен начинаться с `/api/ra/`), например `/api/ra/users` |
| 4 | `query` | string\|nil | нет | Query string без `?`, например `"certRequestId=abc-123"` |
| 5 | `body` | LuaTable\|nil | нет | Тело POST-запроса (конвертируется в JSON). `nil` для GET |
| 6 | `options` | LuaTable\|nil | нет | Опции подписи и таймаута (см. ниже) |
| 7 | `taskId` | number\|nil | нет | ID задачи (для логирования в AutomationScriptsLog) |

**Поля `options`:**

| Поле | Тип | По умолчанию | Описание |
|------|-----|:---:|----------|
| `sign` | boolean | `false` | Подписать поле `rawRequest` в body PKCS#7 |
| `signMode` | string | `"submitAndAccept"` | `"submitAndAccept"` — двойная подпись, `"submit"`/`"approve"` — одинарная |
| `encoding` | string | `nil` | `"utf16le"` — кодировать строку UTF-16LE перед подписью. `nil` — Base64→бинарные данные |
| `timeoutMs` | number | `30000` | Таймаут HTTP-запроса в миллисекундах |

**Возвращает:** `LuaTable` с полями:

| Поле | Тип | Описание |
|------|-----|----------|
| `StatusCode` | number | HTTP-код ответа (200, 201, 400, 500...) |
| `Body` | string\|nil | Тело ответа (raw string) |
| `Error` | string\|nil | Сообщение об ошибке (`nil` при успехе). Формат: `"ExceptionType: message"` |

> При ошибке HTTP-транспорта (timeout, connection refused) ошибка не выбрасывается исключением, а возвращается в поле `Error`.

### 5.2–5.3. Парсинг сертификата и получение thumbprint
**5.2. CRYPTO:parse_cert**

Парсинг Base64-сертификата с извлечением основных полей.

```lua
local info = CRYPTO:parse_cert(certBase64)
```

**Возвращает:** `LuaTable` с полями:

| Поле | Тип | Описание |
|------|-----|----------|
| `serialNumber` | string | Серийный номер |
| `thumbprint` | string | SHA-1 отпечаток (hex, uppercase) |
| `subject` | string | DN субъекта |
| `issuer` | string | DN издателя |
| `notBefore` | string | Дата начала действия (`yyyy-MM-ddTHH:mm:ss`) |
| `notAfter` | string | Дата окончания действия (`yyyy-MM-ddTHH:mm:ss`) |

**5.3. CRYPTO:get_thumbprint**

```lua
local tp = CRYPTO:get_thumbprint(certBase64)
```

**Возвращает:** `string` — Thumbprint (SHA-1, hex, uppercase)

---
## 6. Процесс выпуска сертификата (полный цикл)
### Шаг 1. Регистрация пользователя в УЦ

**Кто выполняет:** серверный Lua-скрипт (смарт-действие на шаге маршрута)

```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")
CryptoProCA.register_or_update_user(raId, CONTEXT.Id, epNameAttrsId, epToSaveUserId)
```

**Что происходит:**

1. Читает JSON из ДП `epNameAttrsId` (обязательно: CN, Country, Locality)
2. Если ДП `epToSaveUserId` пуст — `POST /api/ra/users` (создание, подставляет `UsersFolder` из настроек УЦ)
3. Если ДП `epToSaveUserId` заполнен — `POST /api/ra/users/{userId}` (обновление атрибутов)
4. Сохраняет UUID пользователя в ДП

### Шаг 2, 4, 5. CSR, установка и thumbprint
**Шаг 2. Генерация CSR (запроса на сертификат)**

**Кто выполняет:** браузер пользователя (кнопка на МТФ)

```
window.tcCryptoLogic.newRequest(CONTEXT.taskId, EP_ATTRS_ID, EP_CSR_SAVE_ID, {})
```

**Что происходит (на клиенте):**

1. Определяет тип плагина (CryptoPro / RuToken) через настройки ЭП задачи
2. Считывает атрибуты из ДП (JSON-массив, формат см. раздел 3.3)
3. Генерирует ключевую пару на клиенте
4. Формирует PKCS#10 (CSR) в Base64
5. Записывает CSR в ДП

**Шаг 4. Установка сертификата на клиенте**

**Кто выполняет:** браузер пользователя (кнопка на МТФ)

```
window.tcCryptoLogic.installCertificate(CONTEXT.taskId, EP_CERTIFICATE_ID)
```

**Что происходит (на клиенте):**

1. Считывает Base64-сертификат из ДП
2. Устанавливает через криптоплагин (`CX509Enrollment.InstallResponse`)
3. Сертификат привязывается к ключевой паре

**Шаг 5. Получение Thumbprint (опционально)**

**Кто выполняет:** серверный Lua-скрипт

```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")
CryptoProCA.get_thumbprint(CONTEXT.Id, epCertificateId, epThumbprintId)
```

---
### Шаг 3. Выпуск сертификата в УЦ

**Кто выполняет:** серверный Lua-скрипт

```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")
CryptoProCA.issue_certificate(raId, CONTEXT.Id, epUserId, epRequestId,
    epToSaveRequestId, epToSaveCertificateId, epAuthorityId)
```

**Что происходит:**

1. Считывает userId и PKCS#10 из ДП
2. Удаляет PEM-заголовки из PKCS#10
3. `POST /api/ra/certRequests` с двойной PKCS#7 подписью
4. Сохраняет `certRequestId` в ДП
5. **Опрос** `GET /api/ra/certificates?certRequestId=...` (до 10 попыток × 2 сек)
6. `GET /api/ra/certificates/{id}` — полная форма с `rawCertificate`
7. Сохраняет `rawCertificate` в ДП

## 7. Отзыв и управление жизненным циклом

**Отзыв (Revoke)**

```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")
CryptoProCA.revoke_certificate(raId, CONTEXT.Id, epCertificateId, revocationReason)
-- или с отложенной датой:
CryptoProCA.revoke_certificate(raId, CONTEXT.Id, epCertificateId, 0, fromTimestamp)
```

**Приостановка (Pause) — резерв через SOAP**

```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")
CryptoProCA.pause_certificate(CONTEXT.Id, raId, epCertificateId, fromTs, toTs, 6)
```

**Возобновление (Resume) — резерв через SOAP**

```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")
CryptoProCA.resume_certificate(CONTEXT.Id, raId, epCertificateId)
```

---
## 8. Настройка кнопок на МТФ

Кнопки МТФ вызывают функции через глобальный объект `window.tcCryptoLogic`.

**8.1. Кнопка «Сформировать запрос на сертификат»**

```js
window.tcCryptoLogic.newRequest(CONTEXT.taskId, EP_ATTRS_ID, EP_CSR_SAVE_ID, {})
```

- `EP_ATTRS_ID` — ID ДП с JSON-массивом атрибутов (формат раздел 3.3)
- `EP_CSR_SAVE_ID` — ID ДП, куда записывается PKCS#10 (Base64)
- Четвёртый параметр опционально принимает `{ extParamsId: { ContainerName: EP_CONTAINER_ID } }`

**8.2. Кнопка «Установить сертификат»**

```js
window.tcCryptoLogic.installCertificate(CONTEXT.taskId, EP_CERTIFICATE_ID)
```

- `EP_CERTIFICATE_ID` — ID ДП с Base64-сертификатом (rawCertificate)
- Требует: плагин КриптоПро ЭЦП Browser plug-in

**8.3. Кнопка «Сменить PIN»**

```js
window.tcCryptoLogic.changePin(EP_THUMBPRINT_ID, { taskId: CONTEXT.taskId })
```

---
## 9. Типовой маршрут и примеры скриптов
### 9.1 и 9.3. Структура маршрута и скрипт отзыва
**9.1. Структура маршрута**

```
[Шаг 1: Заполнение данных]
  ↓ Смарт при переходе: CryptoProCA.register_or_update_user(...)
[Шаг 2: Генерация запроса]
  Кнопка МТФ: tcCryptoLogic.newRequest(...)
  ↓
[Шаг 3: Выпуск сертификата]
  Смарт при переходе: CryptoProCA.issue_certificate(...)
  ↓
[Шаг 4: Установка и использование]
  Кнопка МТФ: tcCryptoLogic.installCertificate(...)
  Смарт при переходе: CryptoProCA.get_thumbprint(...)
```

**9.3. Скрипт отзыва**

```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")

CryptoProCA.revoke_certificate(1, CONTEXT.Id, 503, 4) -- Superseded
```

---
### 9.2. Полный скрипт выпуска

Полный пример скрипта выпуска сертификата:

```lua
include("[cryptopro] CryptoPro CA 2.0 — REST API library")

local raId = 1                    -- ID УЦ из CryptoProCertificationCenters
local taskId = CONTEXT.Id

local epUserId = 500              -- ДП: userId в УЦ
local epRequestId = 501           -- ДП: PKCS#10 запрос (Base64)
local epToSaveRequestId = 502     -- ДП: certRequestId (для записи)
local epToSaveCertificateId = 503 -- ДП: rawCertificate (для записи)
local epAuthorityId = 504         -- ДП: имя ЦС (может быть пустым)

CryptoProCA.issue_certificate(
    raId, taskId,
    epUserId, epRequestId,
    epToSaveRequestId, epToSaveCertificateId,
    epAuthorityId
)
```

## 10. Подпись PKCS#7 — техническая реализация

**10.1. Алгоритм**

Подпись формируется штатными средствами .NET; для ГОСТ-ключей используется механизм Windows CryptoAPI (CAPI), так как стандартный путь .NET с ГОСТ-ключами не работает.

**10.2. Алгоритмы и OID ГОСТ**

Соответствие сигнатурных и хеш-OID применяемым алгоритмам ГОСТ:

| Сигнатурный OID | Хеш OID | Алгоритм |
|-----------------|---------|----------|
| 1.2.643.7.1.1.3.2 | 1.2.643.7.1.1.2.2 | ГОСТ 34.10-2012 (256) → Стрибог-256 |
| 1.2.643.7.1.1.3.3 | 1.2.643.7.1.1.2.3 | ГОСТ 34.10-2012 (512) → Стрибог-512 |
| 1.2.643.2.2.3 | 1.2.643.2.2.9 | ГОСТ 34.10-2001 → ГОСТ 34.11-94 |

**10.3. Режимы подписи (signMode)**

| Режим | Описание | Применение |
|-------|----------|------------|
| `submitAndAccept` | Двойная подпись: Sign(Sign(content)) | Выпуск сертификата, отзыв |
| `submit` / `approve` | Одинарная подпись | Если нужен только submit |

---
## 11. Логирование

Все запросы к REST API логируются в таблицу `AutomationScriptsLog`:

| Поле | Значение |
|------|----------|
| `ObjectKey` | `CRYPTO` |
| `ObjectName` | `POST /api/ra/certRequests`, `GET /api/ra/certificates` и т.д. |
| `ObjectParams` | Тело запроса (усечено до 500 символов) |
| `AdditionalInfo` | `HTTP 201: ...` или `ERROR: сообщение` (ответ усечён до 4000 символов) |
| `TaskId` | ID задачи (если передан) |
| `Duration` | Время запроса в мс |

Фильтр для просмотра: `ObjectKey = 'CRYPTO'`

---

## 12. Сопоставление старых и новых смарт-действий

Какая Lua-функция заменяет какое старое смарт-действие:

| # | Старое C#-действие | Новая Lua-функция | Протокол |
|---|-------------------|-------------------|----------|
| 1 | Get Thumbprint | `CryptoProCA.get_thumbprint()` | Локальный X509 |
| 2 | CryptoPro CA — new or update user | `CryptoProCA.register_or_update_user()` | REST API |
| 3 | CryptoPro CA — Issue certificate | `CryptoProCA.issue_certificate()` | REST API |
| 4 | CryptoPro CA — revoke certificate | `CryptoProCA.revoke_certificate()` | REST API |
| 5 | CryptoPro CA — pause certificate | `CryptoProCA.pause_certificate()` | резерв через SOAP |
| 6 | CryptoPro CA — resume certificate | `CryptoProCA.resume_certificate()` | резерв через SOAP |

**Ключевые отличия от старых смартов:**

- Протокол: SOAP/COM → **REST API** + **Win32 CAPI** для подписи ГОСТ
- Формат данных пользователя: XML → **JSON**
- Запись в ДП выполняется от имени системного робота
- Обработка ошибок: ошибки возвращаются в поле `resp.Error`, а не выбрасываются исключением

---

## 13. Известные ограничения и обходные решения

Ограничения среды исполнения и обходные решения для скриптов:

| Проблема | Обходное решение |
|----------|-----------------|
| Ошибка HTTP-транспорта не выбрасывается исключением | `CRYPTO:send_request()` возвращает её в поле `resp.Error` |
| `UTILS:json_decode()` падает на простых JSON-строках (`"uuid"`) | UUID из Body извлекается через `resp.Body:gsub('"', '')` |
| `SQL:scalar("...", nil)` падает с ошибкой | Всегда передавать `{}` вторым параметром |
| `os.clock()` возвращает CPU-время процесса IIS (тысячи секунд) | Для пауз используется `os.time()` (wall-clock) |
| Стандартный путь .NET не подписывает ГОСТ-ключами | Используется механизм Windows CryptoAPI (CAPI) |
| REST API не поддерживает RR=6 (CertificateHold) | резерв через SOAP через старый C#-смарт |
| REST API не поддерживает RR=8 (RemoveFromCrl) | резерв через SOAP через старый C#-смарт |

