SUPPORT-8703. Добавлена проверка цепочки сертификатов

This commit is contained in:
alashkova 2024-11-14 17:59:16 +03:00
parent 4fa45a1f5e
commit 5ce934287f
4 changed files with 183 additions and 2 deletions

View file

@ -28,6 +28,9 @@ capi_function_list_init(library_t *lib, capi_function_list_t *fl)
LIBRARY_RESOLVE(fl->GetLastError, lib, "GetLastError");
LIBRARY_RESOLVE(fl->CryptImportPublicKeyInfo, lib, "CryptImportPublicKeyInfo");
LIBRARY_RESOLVE(fl->CryptDestroyKey, lib, "CryptDestroyKey");
LIBRARY_RESOLVE(fl->CertGetCertificateChain, lib, "CertGetCertificateChain");
LIBRARY_RESOLVE(fl->CertVerifyCertificateChainPolicy, lib, "CertVerifyCertificateChainPolicy");
LIBRARY_RESOLVE(fl->CertFreeCertificateChain, lib, "CertFreeCertificateChain");
#ifdef UNICODE
LIBRARY_RESOLVE(fl->CryptSignHash, lib, "CryptSignHashW");

View file

@ -175,6 +175,31 @@ DECLARE_FN(WINADVAPI,
CRYPT_DESTROY_KEY,
(HCRYPTKEY hKey));
DECLARE_FN(WINADVAPI,
BOOL,
CERT_GET_CERTIFICATE_CHAIN,
(HCERTCHAINENGINE hChainEngine,
PCCERT_CONTEXT pCertContext,
LPFILETIME pTime,
HCERTSTORE hAdditionalStore,
PCERT_CHAIN_PARA pChainPara,
DWORD dwFlags,
LPVOID pvReserved,
PCCERT_CHAIN_CONTEXT* ppChainContext));
DECLARE_FN(WINADVAPI,
BOOL,
CERT_VERIFY_CERTIFICATE_CHAIN_POLICY,
(LPCSTR pszPolicyOID,
PCCERT_CHAIN_CONTEXT pChainContext,
PCERT_CHAIN_POLICY_PARA pPolicyPara,
PCERT_CHAIN_POLICY_STATUS pPolicyStatus));
DECLARE_FN(WINADVAPI,
VOID,
CERT_FREE_CERTIFICATE_CHAIN,
(PCCERT_CHAIN_CONTEXT pChainContext));
#ifdef UNICODE
#define CRYPT_SIGN_HASH_FN CRYPT_SIGN_HASH_W_FN
#define CRYPT_VERIFY_SIGNATURE_FN CRYPT_VERIFY_SIGNATURE_W_FN
@ -205,6 +230,9 @@ typedef struct {
CRYPT_ACQUIRE_CONTEXT_FN CryptAcquireContext;
CRYPT_IMPORT_PUBLIC_KEY_INFO_FN CryptImportPublicKeyInfo;
CRYPT_DESTROY_KEY_FN CryptDestroyKey;
CERT_GET_CERTIFICATE_CHAIN_FN CertGetCertificateChain;
CERT_VERIFY_CERTIFICATE_CHAIN_POLICY_FN CertVerifyCertificateChainPolicy;
CERT_FREE_CERTIFICATE_CHAIN_FN CertFreeCertificateChain;
} capi_function_list_t;

View file

@ -79,6 +79,127 @@ error:
return -1;
}
static PCCERT_CHAIN_CONTEXT
get_cert_chain(PCCERT_CONTEXT certificate)
{
LOG_TRACE("get_cert_chain enter");
CERT_CHAIN_PARA chain_para = {0};
chain_para.cbSize = sizeof(CERT_CHAIN_PARA);
PCCERT_CHAIN_CONTEXT chain_ctx = NULL;
if (!cp_function_list.CertGetCertificateChain(NULL,
certificate,
NULL,
NULL,
&chain_para,
(CERT_CHAIN_CACHE_END_CERT | CERT_CHAIN_REVOCATION_CHECK_END_CERT),
NULL,
&chain_ctx)) {
LOG_ERROR("CertGetCertificateChain() failed");
LOG_ERROR("get_cert_chain exit with error");
return NULL;
}
LOG_TRACE("get_cert_chain exit");
return chain_ctx;
}
static const char*
get_cert_chain_policy_status_error_desc(DWORD err)
{
// TODO-8703
switch(err) {
case CERT_E_UNTRUSTEDROOT:
return "CERT_E_UNTRUSTEDROOT";
case CERT_E_CHAINING:
return "CERT_E_CHAINING";
default:
break;
}
return "Unknown error";
}
static bool
check_cert_chain_policy(PCCERT_CHAIN_CONTEXT chain_ctx)
{
LOG_TRACE("check_cert_chain_policy enter");
CERT_CHAIN_POLICY_PARA policy_para = {0};
policy_para.cbSize = sizeof(policy_para);
CERT_CHAIN_POLICY_STATUS status = {0};
status.cbSize = sizeof(status);
CPSIGNATURE_EXTRA_CERT_CHAIN_POLICY_STATUS extraStatus = {0};
extraStatus.cbSize = sizeof(extraStatus);
status.pvExtraPolicyStatus = &extraStatus;
if (!cp_function_list.CertVerifyCertificateChainPolicy(
CERT_CHAIN_POLICY_BASE,
// TODO-8703: CPCERT_CHAIN_POLICY_SIGNATURE
chain_ctx,
&policy_para,
&status)) {
LOG_ERROR("CertVerifyCertificateChainPolicy() failed");
LOG_ERROR("check_cert_chain_policy exit with error");
return false;
}
if (status.dwError != 0) {
LOG_WARN("The certificate chain cannot be validated. "
"CertVerifyCertificateChainPolicy status: %s('0x%08x')",
get_cert_chain_policy_status_error_desc(status.dwError), status.dwError);
LOG_TRACE("check_cert_chain_policy exit");
return false;
}
LOG_TRACE("check_cert_chain_policy exit");
return true;
}
static void
free_cert_chain(PCCERT_CHAIN_CONTEXT chain_ctx)
{
LOG_TRACE("free_cert_chain enter");
if (chain_ctx) {
cp_function_list.CertFreeCertificateChain(chain_ctx);
}
LOG_TRACE("free_cert_chain exit");
}
static bool
verify_cert_chain(PCCERT_CONTEXT certificate)
{
bool is_verified = false;
LOG_TRACE("verify_cert_chain enter");
PCCERT_CHAIN_CONTEXT chain_ctx = get_cert_chain(certificate);
if (chain_ctx == NULL) {
goto exit;
}
if (!check_cert_chain_policy(chain_ctx)) {
goto exit;
}
is_verified = true;
exit:
free_cert_chain(chain_ctx);
LOG_DEBUG("cert_chain: %s", is_verified ? "verified" : "failed");
LOG_DEBUG("verify_cert_chain exit");
return is_verified;
}
static int
sign_hash_data(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t *sign)
{
@ -101,6 +222,10 @@ sign_hash_data(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t
goto exit;
}
if (!verify_cert_chain(pSignerCert)) {
goto exit;
}
if (!cp_function_list.CryptAcquireCertificatePrivateKey(
pSignerCert,
CRYPT_ACQUIRE_SILENT_FLAG,
@ -425,6 +550,10 @@ cryptopro_verify(const str_t* cert_thumbprint, const str_t* alg, const str_t* da
goto exit;
}
if (!verify_cert_chain(certificate)) {
goto exit;
}
if (!cp_function_list.CryptAcquireContext(&hCryptProv, NULL, CP_KC1_GR3410_2001_PROV,
PROV_GOST_2001_DH, CRYPT_VERIFYCONTEXT)) {
LOG_ERROR("CryptAcquireContext() failed");

View file

@ -58,11 +58,32 @@ chown -R ervu:ervu /var/opt/cprocsp/keys/ervu/key_cont.000/
Получить следующие значения:
- Поле "SHA1 отпечаток": это значение нужно будет указать в конфигурационном файле модуля подписания в качестве настройки \<sign_cert_thumbprint\>
- Поле "Ссылка на ключ": убедиться, что значение равно "Есть" (иначе необходимо проверить выполнение предыдущих шагов)
- Поля "URL сертификата УЦ": по ссылкам, указанным в этом значении, скачать сертификаты УЦ и установить их (описано далее)
- Поля "URL списка отзыва": по ссылкам, указанным в этом значении, скачать CRL и установить их (описано далее)
4. (опционально) Сделать тестовую подпись с помощью установленного контейнера и полученных значений \<sign_cert_thumbprint\> и \<sign_cert_password\>:
4. Установить сертификаты УЦ (поля "URL сертификата УЦ" в свойствах сертификата). Для каждого сертификата выполнить команду от имени **root**:
``` bash
/opt/cprocsp/bin/amd64/certmgr -install -store uRoot -file <sign_ca_cert>.cer
```
5. Установить CRL (поле "URL списка отзыва" в свойствах сертификата). Для каждого CRL выполнить команду от имени **ervu**:
``` bash
/opt/cprocsp/bin/amd64/certmgr -install -store uCA -crl -file <sign_ca_crl>.crl
```
6. Проверить цепочку сертификатов (команда выполняется от имени **ervu**):
``` bash
/opt/cprocsp/bin/amd64/certmgr -list -thumbprint <sign_cert_thumbprint> -chain
```
Результатом команды должна быть строка "Цепочка сертификатов: Успешно проверена."
Подробнее о проверке цепочки сертификатов здесь: https://www.altlinux.org/%D0%9A%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%9F%D1%80%D0%BE#%D0%9F%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D0%BA%D0%B0_%D1%86%D0%B5%D0%BF%D0%BE%D1%87%D0%BA%D0%B8_%D1%81%D0%B5%D1%80%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%82%D0%BE%D0%B2
7. (опционально) Сделать тестовую подпись с помощью установленного контейнера и полученных значений \<sign_cert_thumbprint\> и \<sign_cert_password\>:
``` bash
touch test.txt
/opt/cprocsp/bin/amd64/cryptcp -signf -thumbprint <sign_cert_thumbprint> ./test.txt
/opt/cprocsp/bin/amd64/cryptcp -signf -thumbprint <sign_cert_thumbprint> -errchain ./test.txt
rm -f test.txt test.txt.sgn
```