Compare commits

...

16 commits

Author SHA1 Message Date
Наиля Алашкова
ee30c9ddc7 В инструкцию по установке добавлено описание Delta CRL (разностные списки отзыва сертификатов) 2025-12-03 15:38:33 +03:00
Наиля Алашкова
d66f049862 В Инструкцию по установке добавлено описание утилиты TSLTool 2025-12-01 16:25:46 +03:00
Zaripov Emil
73f1af565c Merge branch 'release/1.4.2' 2025-11-27 15:50:00 +03:00
Zaripov Emil
13092d7508 Merge branch 'release/1.4.1' 2025-11-27 15:49:02 +03:00
Наиля Алашкова
34217ed9cd версия 1.4.2 2025-11-10 12:43:23 +03:00
Наиля Алашкова
cdef230819 SUPPORT-9524. Добавлен статус CERT_TRUST_IS_PARTIAL_CHAIN + вывод информации о сертификате в случае ошибки при проверке цепочки сертификатов 2025-11-10 12:40:53 +03:00
Наиля Алашкова
7dbcabb9ff Обновлен список сертификатов ЕСИА в Инструкции по установке 2025-11-05 10:13:01 +03:00
Наиля Алашкова
b0a22acf92 nginx.conf: добавлен upstream fastcgi 2025-10-17 08:31:26 +03:00
alashkova
9af14689f0 Версия 1.4.1 2025-10-15 17:01:26 +03:00
alashkova
b09ade7bf4 SUPPORT-9482. Отключено автоматическое добавление кавычек вокруг значений в DN (Distinguished Name) сертификата 2025-10-15 16:59:17 +03:00
Zaripov Emil
2d3e087013 Merge branch 'release/1.3.3' 2025-08-22 13:43:01 +03:00
vladimir.burdin
11a7f14e1b Поменял образ на свежий 2025-07-10 15:24:23 +03:00
vladimir.burdin
ef658a0eb4 Поменял на свежий серт ЕСИА прод 2025-06-16 14:32:35 +03:00
vladimir.burdin
88daddf6b3 Паша сделал новый образ, я добавил его в сборку 2025-05-23 11:22:18 +03:00
vladimir.burdin
fcf1185972 Обновил путь к образу 2025-05-23 10:31:10 +03:00
vladimir.burdin
10c1958055 Поменял серты на актуальные 2025-04-24 14:04:41 +03:00
7 changed files with 299 additions and 20 deletions

View file

@ -2,7 +2,7 @@ CMAKE_MINIMUM_REQUIRED (VERSION 3.0)
SET (CMAKE_C_COMPILER "gcc")
PROJECT (ervu-sign-module VERSION 1.4.0 LANGUAGES C)
PROJECT (ervu-sign-module VERSION 1.4.2 LANGUAGES C)
IF (CMAKE_VERBOSE)
SET (CMAKE_VERBOSE_MAKEFILE 1)

View file

@ -120,6 +120,7 @@ $ /opt/cprocsp/bin/amd64/curl -v http://127.0.0.1/msg/verify_detached \
| `CERT_TRUST_REVOCATION_STATUS_UNKNOWN` | Неизвестен статус отзыва сертификата или одного из сертификатов в цепочке (проблема с CRL) |
| `CERT_TRUST_IS_UNTRUSTED_ROOT` | Сертификат или цепочка сертификатов основана на ненадежном корневом сертификате |
| `CERT_TRUST_IS_NOT_TIME_VALID` | Этот сертификат или один из сертификатов в цепочке сертификатов является недопустимым по времени |
| `CERT_TRUST_IS_PARTIAL_CHAIN` | Цепочка сертификатов не полная |
## Сборка из исходников

View file

@ -27,7 +27,7 @@ http {
default_type application/octet-stream;
sendfile on;
gzip on;
# text/html doesn't need to be defined there, it's compressed always
@ -36,13 +36,27 @@ http {
# gzip_comp_level 9;
include /etc/nginx/sites-enabled.d/*.conf;
upstream fastcgi {
server 127.0.0.1:9009;
server 127.0.0.1:9010;
server 127.0.0.1:9011;
server 127.0.0.1:9012;
server 127.0.0.1:9013;
server 127.0.0.1:9014;
server 127.0.0.1:9015;
server 127.0.0.1:9016;
server 127.0.0.1:9017;
server 127.0.0.1:9018;
}
server {
listen 80;
server_name localhost;
client_max_body_size 10M;
location / {
fastcgi_pass localhost:9009;
include fastcgi_params;
}
}
location / {
fastcgi_pass fastcgi;
include fastcgi_params;
}
}
}

View file

@ -37,6 +37,7 @@ capi_function_list_init(library_t *lib, capi_function_list_t *fl)
LIBRARY_RESOLVE(fl->CertFindExtension, lib, "CertFindExtension");
LIBRARY_RESOLVE(fl->CryptDecodeObjectEx, lib, "CryptDecodeObjectEx");
LIBRARY_RESOLVE(fl->LocalFree, lib, "LocalFree");
LIBRARY_RESOLVE(fl->FileTimeToSystemTime, lib, "FileTimeToSystemTime");
#ifdef UNICODE
LIBRARY_RESOLVE(fl->CryptSignHash, lib, "CryptSignHashW");

View file

@ -266,6 +266,12 @@ DECLARE_FN(WINADVAPI,
LOCAL_FREE,
(HLOCAL hMem));
DECLARE_FN(WINADVAPI,
BOOL,
FILE_TIME_TO_SYSTEM_TIME,
(CONST FILETIME *lpFileTime,
LPSYSTEMTIME lpSystemTime));
#ifdef UNICODE
#define CRYPT_SIGN_HASH_FN CRYPT_SIGN_HASH_W_FN
#define CRYPT_VERIFY_SIGNATURE_FN CRYPT_VERIFY_SIGNATURE_W_FN
@ -308,6 +314,7 @@ typedef struct {
CERT_FIND_EXTENSION_FN CertFindExtension;
CRYPT_DECODE_OBJECT_EX_FN CryptDecodeObjectEx;
LOCAL_FREE_FN LocalFree;
FILE_TIME_TO_SYSTEM_TIME_FN FileTimeToSystemTime;
} capi_function_list_t;

View file

@ -5,9 +5,12 @@
#include "library.h"
#include "logger.h"
#define SHA1_SIZE 20
static const char* CERT_TRUST_IS_UNTRUSTED_ROOT_ERR_CODE = "CERT_TRUST_IS_UNTRUSTED_ROOT";
static const char* CERT_TRUST_REVOCATION_STATUS_UNKNOWN_ERR_CODE = "CERT_TRUST_REVOCATION_STATUS_UNKNOWN";
static const char* CERT_TRUST_IS_NOT_TIME_VALID_ERR_CODE = "CERT_TRUST_IS_NOT_TIME_VALID";
static const char* CERT_TRUST_IS_PARTIAL_CHAIN_ERR_CODE = "CERT_TRUST_IS_PARTIAL_CHAIN";
static capi_function_list_t cp_function_list;
static library_t libcapi;
@ -24,6 +27,10 @@ static PCCERT_CONTEXT get_signer_cert(const cryptopro_context_t *ctx, HCERTSTORE
static int set_password(PCCERT_CONTEXT cert_ctx, const str_t *password);
static bool get_subject_name(PCCERT_CONTEXT cert, /*out*/ char **subject_name);
static bool get_issuer_name(PCCERT_CONTEXT cert, /*out*/ char **issuer_name);
bool
cryptopro_init(const char* cp_file)
{
@ -128,7 +135,7 @@ print_crl_urls_from_certificate(PCCERT_CONTEXT cert)
&crl_dist_points,
&info_size
)) {
LOG_ERROR("CryptDecodeObjectEx failed. Desc: 0x%x", cp_function_list.GetLastError());
LOG_ERROR("CryptDecodeObjectEx failed. Error code: 0x%08x", cp_function_list.GetLastError());
goto error;
}
@ -160,6 +167,169 @@ error:
return;
}
static void
print_crt_urls_from_certificate(PCCERT_CONTEXT cert)
{
LOG_TRACE("print_crt_urls_from_certificate enter");
PCERT_EXTENSION extension = cp_function_list.CertFindExtension(
szOID_AUTHORITY_INFO_ACCESS,
cert->pCertInfo->cExtension,
cert->pCertInfo->rgExtension
);
if (!extension) {
LOG_WARN("Authority Info Access extension not found");
goto error;
}
DWORD info_size = 0;
PCERT_AUTHORITY_INFO_ACCESS info_access = NULL;
if (!cp_function_list.CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
X509_AUTHORITY_INFO_ACCESS,
extension->Value.pbData,
extension->Value.cbData,
CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
NULL,
&info_access,
&info_size
)) {
LOG_ERROR("CryptDecodeObjectEx failed. Error code: 0x%08x", cp_function_list.GetLastError());
goto error;
}
if (info_access && info_access->cAccDescr > 0) {
for (DWORD i = 0; i < info_access->cAccDescr; i++) {
if (strcmp(info_access->rgAccDescr[i].pszAccessMethod, szOID_PKIX_CA_ISSUERS) == 0) {
if (info_access->rgAccDescr[i].AccessLocation.dwAltNameChoice == CERT_ALT_NAME_URL) {
LOG_INFO("CA Issuers URL: %ls", info_access->rgAccDescr[i].AccessLocation._empty_union_.pwszURL);
}
}
}
}
if (info_access) {
cp_function_list.LocalFree(info_access);
}
LOG_TRACE("print_crt_urls_from_certificate exit");
return;
error:
LOG_ERROR("print_crt_urls_from_certificate exit with error");
return;
}
static char*
bin_to_hex(const BYTE *bytes, DWORD len)
{
char* hex = NULL;
hex = malloc(len * 2 + 1);
if (hex == NULL) {
LOG_ERROR("bin_to_hex exit with error: could not allocate memory for hex (%d bytes)", len * 2 + 1);
return NULL;
}
for (DWORD i = 0; i < len; i++) {
sprintf(hex + i * 2, "%02x", bytes[i]);
}
return hex;
}
static bool
get_thumbprint(PCCERT_CONTEXT cert, /*out*/ char** thumbprint)
{
LOG_TRACE("get_thumbprint enter");
BYTE sha1_hash[SHA1_SIZE];
DWORD sha1_hash_size = sizeof(sha1_hash);
if (!cp_function_list.CertGetCertificateContextProperty(
cert,
CERT_SHA1_HASH_PROP_ID,
sha1_hash,
&sha1_hash_size)) {
LOG_ERROR("CertGetCertificateContextProperty(CERT_SHA1_HASH_PROP_ID) failed. Error code: 0x%08x",
cp_function_list.GetLastError());
goto error;
}
*thumbprint = bin_to_hex(sha1_hash, sha1_hash_size);
if (*thumbprint == NULL) {
goto error;
}
LOG_DEBUG("thumbprint: '%s'", *thumbprint);
LOG_TRACE("get_thumbprint exit");
return true;
error:
LOG_ERROR("get_thumbprint exit with error");
return false;
}
static void
print_cert_time_validity(PCCERT_CONTEXT cert)
{
SYSTEMTIME st;
LOG_TRACE("print_cert_time_validity enter");
if (cp_function_list.FileTimeToSystemTime(&cert->pCertInfo->NotBefore, &st)) {
LOG_INFO("Not valid before: %02d.%02d.%04d", st.wDay, st.wMonth, st.wYear);
}
if (cp_function_list.FileTimeToSystemTime(&cert->pCertInfo->NotAfter, &st)) {
LOG_INFO("Not valid after: %02d.%02d.%04d", st.wDay, st.wMonth, st.wYear);
}
LOG_TRACE("print_cert_time_validity exit");
}
static void
print_common_cert_info(PCCERT_CONTEXT cert)
{
char* subject = NULL;
char* issuer = NULL;
char* thumbprint = NULL;
LOG_TRACE("print_common_cert_info enter");
if (get_thumbprint(cert, &thumbprint)) {
LOG_INFO("SHA1 Hash: %s", thumbprint);
}
if (get_subject_name(cert, &subject)) {
LOG_INFO("Subject: %s", subject);
}
if (get_issuer_name(cert, &issuer)) {
LOG_INFO("Issuer: %s", issuer);
}
free(subject);
free(issuer);
free(thumbprint);
LOG_TRACE("print_common_cert_info exit");
}
static void
print_cert_info(PCCERT_CONTEXT cert)
{
LOG_INFO("=========== Certificate Info: ===========");
print_common_cert_info(cert);
print_cert_time_validity(cert);
print_crt_urls_from_certificate(cert);
print_crl_urls_from_certificate(cert);
LOG_INFO("=========================================");
}
static void
process_trust_status_error(PCCERT_CONTEXT cert, DWORD status, /*out*/ const char **error_code)
{
@ -168,7 +338,6 @@ process_trust_status_error(PCCERT_CONTEXT cert, DWORD status, /*out*/ const char
switch (status) {
case CERT_TRUST_REVOCATION_STATUS_UNKNOWN:
*error_code = CERT_TRUST_REVOCATION_STATUS_UNKNOWN_ERR_CODE;
print_crl_urls_from_certificate(cert);
break;
case CERT_TRUST_IS_UNTRUSTED_ROOT:
*error_code = CERT_TRUST_IS_UNTRUSTED_ROOT_ERR_CODE;
@ -176,6 +345,9 @@ process_trust_status_error(PCCERT_CONTEXT cert, DWORD status, /*out*/ const char
case CERT_TRUST_IS_NOT_TIME_VALID:
*error_code = CERT_TRUST_IS_NOT_TIME_VALID_ERR_CODE;
break;
case CERT_TRUST_IS_PARTIAL_CHAIN:
*error_code = CERT_TRUST_IS_PARTIAL_CHAIN_ERR_CODE;
break;
default:
*error_code = NULL;
break;
@ -188,6 +360,8 @@ process_trust_status_error(PCCERT_CONTEXT cert, DWORD status, /*out*/ const char
LOG_WARN("The certificate is not trusted. CERT_TRUST_STATUS: '0x%08x'", status);
}
print_cert_info(cert);
LOG_TRACE("process_trust_status_error exit");
}
@ -910,10 +1084,12 @@ get_subject_name(PCCERT_CONTEXT cert, /*out*/ char **subject_name)
{
LOG_TRACE("get_subject_name enter");
DWORD dwFlags = CERT_X500_NAME_STR | CERT_NAME_STR_NO_QUOTING_FLAG;
DWORD len = cp_function_list.CertNameToStr(
cert->dwCertEncodingType,
&cert->pCertInfo->Subject,
CERT_X500_NAME_STR,
dwFlags,
NULL,
0
);
@ -927,7 +1103,7 @@ get_subject_name(PCCERT_CONTEXT cert, /*out*/ char **subject_name)
cp_function_list.CertNameToStr(
cert->dwCertEncodingType,
&cert->pCertInfo->Subject,
CERT_X500_NAME_STR,
dwFlags,
*subject_name,
len
);
@ -948,10 +1124,12 @@ get_issuer_name(PCCERT_CONTEXT cert, /*out*/ char **issuer_name)
{
LOG_TRACE("get_issuer_name enter");
DWORD dwFlags = CERT_X500_NAME_STR | CERT_NAME_STR_NO_QUOTING_FLAG;
DWORD len = cp_function_list.CertNameToStr(
cert->dwCertEncodingType,
&cert->pCertInfo->Issuer,
CERT_X500_NAME_STR,
dwFlags,
NULL,
0
);
@ -965,7 +1143,7 @@ get_issuer_name(PCCERT_CONTEXT cert, /*out*/ char **issuer_name)
cp_function_list.CertNameToStr(
cert->dwCertEncodingType,
&cert->pCertInfo->Issuer,
CERT_X500_NAME_STR,
dwFlags,
*issuer_name,
len
);

View file

@ -70,18 +70,31 @@ chown -R ervu:ervu /var/opt/cprocsp/keys/ervu/key_cont.000/
- Поля "URL сертификата УЦ": по ссылкам, указанным в этом значении, скачать сертификаты УЦ и установить их (описано далее)
- Поля "URL списка отзыва": в случае отсутствия доступа к УЦ, по ссылкам, указанным в этом значении, скачать CRL и установить их (описано далее)
4. Установить сертификаты УЦ (поля "URL сертификата УЦ" в свойствах сертификата). Для каждого сертификата выполнить команду от имени **root**:
```bash
/opt/cprocsp/bin/amd64/certmgr -install -store mRoot -file <sign_ca_cert>.cer
```
5. В случае отсутствия доступа к УЦ, установить CRL (поле "URL списка отзыва" в свойствах сертификата). Для каждого CRL выполнить команду от имени **ervu**:
5. В случае отсутствия доступа к УЦ, установить CRL (поле "URL списка отзыва" в свойствах сертификата).
5.1. Для каждого CRL выполнить команду от имени **ervu**:
```bash
/opt/cprocsp/bin/amd64/certmgr -install -store uCA -crl -file <sign_ca_crl>.crl
```
5.2. Для каждого CRL проверить наличие Delta CRL (разностного списка отозванных сертификатов). Для этого:
5.2.1. Проверить свойства CRL, выполнив команду:
```bash
/opt/cprocsp/bin/amd64/certmgr -list -crl -file <sign_ca_crl>.crl
```
5.2.2. Если среди отображенных свойств есть поле "URL дельта CRL", то по указанным ссылкам скачать разностные CRL и установить их так же, как описано в `п. 5.1.`
6. (опционально) Сделать тестовую подпись с помощью установленного контейнера и полученных значений \<sign_cert_thumbprint\> и \<sign_cert_password\>:
```bash
@ -99,12 +112,12 @@ chown -R ervu:ervu /var/opt/cprocsp/keys/ervu/key_cont.000/
| Среда \ Алгоритм подписания | GOST3410_2012_256 |
| ----------------------------- | ----------------------------------------------------- |
| | TESIA GOST 2012 new.cer |
| Тестовая | Отпечаток: e654f6114dc19bae84c5748794a4fd7797726e71 |
| | Срок действия: по 24.04.2025 |
| Тестовая | Отпечаток: b3a7890493bdd95d47c6871627b475e276962bf1 |
| | Срок действия: по 21.04.2026 |
| ----------------------------- | ----------------------------------------------------- |
| | ГОСТ+ПРОД+24-25.cer |
| Продуктивная | Отпечаток: 20ad21785b9161ef46f075d4dc23c34e1e349228 |
| | Срок действия: по 08.06.2025 |
| | ГОСТ_PROD_25_26.cer |
| Продуктивная | Отпечаток: 2583cca4e2aad8d8170ef73fecc592ccac46821d |
| | Срок действия: по 17.06.2026 |
-------------------------------------------------------------------------------------
@ -125,18 +138,31 @@ chown -R ervu:ervu /var/opt/cprocsp/keys/ervu/key_cont.000/
- Поля "URL сертификата УЦ": по ссылкам, указанным в этом значении, скачать сертификаты УЦ и установить их (описано далее)
- Поля "URL списка отзыва": в случае отсутствия доступа к УЦ, по ссылкам, указанным в этом значении, скачать CRL и установить их (описано далее)
4. Установить сертификаты УЦ (поля "URL сертификата УЦ" в свойствах сертификата). Для каждого сертификата выполнить команду от имени **root**:
```bash
/opt/cprocsp/bin/amd64/certmgr -install -store mRoot -file <esia_ca_cert>.cer
```
5. В случае отсутствия доступа к УЦ, установить CRL (поле "URL списка отзыва" в свойствах сертификата). Для каждого CRL выполнить команду от имени **ervu**:
5. В случае отсутствия доступа к УЦ, установить CRL (поле "URL списка отзыва" в свойствах сертификата).
5.1. Для каждого CRL выполнить команду от имени **ervu**:
```bash
/opt/cprocsp/bin/amd64/certmgr -install -store uCA -crl -file <esia_ca_crl>.crl
```
5.2. Для каждого CRL проверить наличие Delta CRL (разностного списка отозванных сертификатов). Для этого:
5.2.1. Проверить свойства CRL, выполнив команду:
```bash
/opt/cprocsp/bin/amd64/certmgr -list -crl -file <esia_ca_crl>.crl
```
5.2.2. Если среди отображенных свойств есть поле "URL дельта CRL", то по указанным ссылкам скачать разностные CRL и установить их так же, как описано в `п. 5.1.`
## Установка и настройка nginx
Дальнейшие шаги выполняются от имени root:
@ -293,6 +319,58 @@ apt-get install glib2 libfcgi libjson-glib libuuid
где \<provtype\> - это тип криптопровайдера.
### Утилита TSLTool
Утилита TSLTool предназначена для выполнения следующих действий:
- установка корневого сертификата Головного удостоверяющего центра;
- загрузка TSL (списка аккредитованных удостоверяющих центров, Trustservice Status List);
- установка сертификатов подчинённых удостоверяющих центров;
- загрузка и установка CRL аккредитованных удостоверяющих центров;
- удаление CRL и сертификатов аккредитованных удостоверяющих центров с истекшим сроком действия.
**Порядок установки утилиты TSLTool**
1. Скачать дистрибутив утилиты TSLTool по ссылке: https://cryptopro.ru/sites/default/files/products/svs/linux/2/1157/cprotools-tsl_1.0.1157-1_amd64.deb
2. Установить необходимые инструменты для установки deb-пакета:
```
apt-get install -y dpkg
```
3. Установить улититу TSLTool (указать корректный путь к файлу, если он отличается):
```
mkdir -p /var/lib/dpkg/ && touch /var/lib/dpkg/status
dpkg -i /cprotools-tsl_1.0.1157-1_amd64.deb
```
> **Примечание:** если файл находится не в текущей директории, укажите полный путь к нему.
**Порядок установки сертификатов аккредитованных удостоверяющих центров**
1. Загрузить TSL в файл `tsl.xml` (указать корректную ссылку для скачивания, если она отличается):
```
/opt/cprocsp/bin/amd64/curl -o tsl.xml https://e-trust.gosuslugi.ru/app/scc/portal/api/v1/portal/ca/getxml
```
2. Установить сертификаты аккредитованных удостоверяющих центров из `tsl.xml` (команда выполняется от имени **root**):
```
/opt/cprotools/tsltool/tsltool --skiproot --tslfile tsl.xml --skipcrl --onlyvalidca
```
> **Примечание:** На экран будет выведено предупреждение о том, что установка большого количества объектов приводит к серьёзной деградации производительности TSLсервера и операционной системы в целом. Необходимо подтвердить данное действие.
3. Настроить регулярное обновление сертификатов с помощью **CRON**.
**Дополнительная информация**
- В случае возникновения ошибок проверьте логи утилиты TSLTool. По умолчанию они сохраняются по пути: `/var/log/cprotools/tsltool/log.clef`.
- Подробнее об утилите TSLTool см. по ссылке: https://dss.cryptopro.ru/docs/svs/adminguide/set/tsltool.html
## Примечания