SUPPORT-9524. Добавлен статус CERT_TRUST_IS_PARTIAL_CHAIN + вывод информации о сертификате в случае ошибки при проверке цепочки сертификатов

This commit is contained in:
Наиля Алашкова 2025-11-10 12:40:53 +03:00
parent 7dbcabb9ff
commit cdef230819
4 changed files with 185 additions and 2 deletions

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

@ -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");
}