SUPPORT-9333. Проверка подписанного сообщения, содержащего отсоединённую подпись

This commit is contained in:
Наиля Алашкова 2025-08-29 13:03:06 +03:00
parent 9d2f2be25a
commit ac08303c90
34 changed files with 2403 additions and 114 deletions

View file

@ -100,13 +100,75 @@ free_cert_chain(PCCERT_CHAIN_CONTEXT chain_ctx)
}
static void
process_trust_status_error(DWORD status, /*out*/ const char **error_code)
print_crl_urls_from_certificate(PCCERT_CONTEXT cert)
{
LOG_TRACE("print_crl_urls_from_certificate enter");
PCERT_EXTENSION extension = cp_function_list.CertFindExtension(
szOID_CRL_DIST_POINTS,
cert->pCertInfo->cExtension,
cert->pCertInfo->rgExtension
);
if (!extension) {
LOG_WARN("CRL Distribution Points extension not found");
goto error;
}
DWORD info_size = 0;
CRL_DIST_POINTS_INFO *crl_dist_points = NULL;
if (!cp_function_list.CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
X509_CRL_DIST_POINTS,
extension->Value.pbData,
extension->Value.cbData,
CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
NULL,
&crl_dist_points,
&info_size
)) {
LOG_ERROR("CryptDecodeObjectEx failed. Desc: 0x%x", cp_function_list.GetLastError());
goto error;
}
if (crl_dist_points && crl_dist_points->cDistPoint > 0) {
for (DWORD i = 0; i < crl_dist_points->cDistPoint; i++) {
PCRL_DIST_POINT pDistPoint = &crl_dist_points->rgDistPoint[i];
if (pDistPoint->DistPointName.dwDistPointNameChoice == CRL_DIST_POINT_FULL_NAME) {
PCERT_ALT_NAME_ENTRY pAltNameEntry = pDistPoint->DistPointName._empty_union_.FullName.rgAltEntry;
for (DWORD j = 0; j < pDistPoint->DistPointName._empty_union_.FullName.cAltEntry; j++) {
if (pAltNameEntry[j].dwAltNameChoice == CERT_ALT_NAME_URL) {
LOG_INFO("CRL URL: %ls", pAltNameEntry[j]._empty_union_.pwszURL);
}
}
}
}
}
if (crl_dist_points) {
cp_function_list.LocalFree(crl_dist_points);
}
LOG_TRACE("print_crl_urls_from_certificate exit");
return;
error:
LOG_ERROR("print_crl_urls_from_certificate exit with error");
return;
}
static void
process_trust_status_error(PCCERT_CONTEXT cert, DWORD status, /*out*/ const char **error_code)
{
LOG_TRACE("process_trust_status_error enter");
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;
@ -153,7 +215,7 @@ get_cert_chain(PCCERT_CONTEXT certificate, /*out*/ const char **error_code)
}
if (chain_ctx->TrustStatus.dwErrorStatus) {
process_trust_status_error(chain_ctx->TrustStatus.dwErrorStatus, error_code);
process_trust_status_error(certificate, chain_ctx->TrustStatus.dwErrorStatus, error_code);
goto error;
}
@ -220,6 +282,8 @@ verify_cert_chain(PCCERT_CONTEXT certificate, timer_context_t *timer_ctx, /*out*
LOG_TRACE("verify_cert_chain enter");
timer_on_verify_cert_chain_enter(timer_ctx);
timer_on_get_cert_chain_enter(timer_ctx);
PCCERT_CHAIN_CONTEXT chain_ctx = get_cert_chain(certificate, error_code);
@ -240,6 +304,8 @@ exit:
LOG_DEBUG("cert_chain: %s", is_verified ? "verified" : "failed");
timer_on_verify_cert_chain_exit(timer_ctx);
LOG_DEBUG("verify_cert_chain exit");
return is_verified;
}
@ -293,7 +359,6 @@ open_signer_cert(cryptopro_context_t *ctx)
goto error;
}
LOG_TRACE("open_signer_cert exit");
return 0;
@ -353,14 +418,10 @@ sign_hash_data(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t
BYTE *pbSignedMessageBlob;
DWORD cbSignedMessageBlob;
timer_on_verify_cert_chain_enter(ctx->timer_ctx);
if (!verify_cert_chain(ctx->signer_cert, ctx->timer_ctx, error_code)) {
goto exit;
}
timer_on_verify_cert_chain_exit(ctx->timer_ctx);
timer_on_acquire_private_key_enter(ctx->timer_ctx);
if (!cp_function_list.CryptAcquireCertificatePrivateKey(
@ -394,6 +455,10 @@ sign_hash_data(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t
}
pbSignedMessageBlob = malloc(cbSignedMessageBlob * sizeof(BYTE));
if (pbSignedMessageBlob == NULL) {
LOG_ERROR("Could not allocate memory for pbSignedMessageBlob (%zd bytes)", cbSignedMessageBlob * sizeof(BYTE));
goto exit;
}
if (!cp_function_list.CryptSignHash(hash, AT_KEYEXCHANGE, NULL, 0, pbSignedMessageBlob, &cbSignedMessageBlob)) {
LOG_ERROR("CryptSignHash() failed");
@ -703,6 +768,8 @@ get_verify_error(char** verify_error)
return;
}
LOG_DEBUG("verify error: %s", *verify_error);
LOG_TRACE("get_verify_error exit");
}
@ -743,14 +810,10 @@ cryptopro_verify(cryptopro_context_t* ctx, const str_t* alg, const str_t* data,
goto exit;
}
timer_on_verify_cert_chain_enter(ctx->timer_ctx);
if (!verify_cert_chain(certificate, ctx->timer_ctx, error_code)) {
goto exit;
}
timer_on_verify_cert_chain_exit(ctx->timer_ctx);
LOG_DEBUG("provider: '%s', prov_type: %u", ctx->provider, ctx->prov_type);
if (!cp_function_list.CryptAcquireContext(&hCryptProv, NULL, ctx->provider, ctx->prov_type,
@ -842,6 +905,224 @@ exit:
return rc;
}
static bool
get_subject_name(PCCERT_CONTEXT cert, /*out*/ char **subject_name)
{
LOG_TRACE("get_subject_name enter");
DWORD len = cp_function_list.CertNameToStr(
cert->dwCertEncodingType,
&cert->pCertInfo->Subject,
CERT_X500_NAME_STR,
NULL,
0
);
*subject_name = malloc(len);
if (*subject_name == NULL) {
LOG_ERROR("Could not allocate memory for Subject Name str (%d bytes)", len);
goto error;
}
cp_function_list.CertNameToStr(
cert->dwCertEncodingType,
&cert->pCertInfo->Subject,
CERT_X500_NAME_STR,
*subject_name,
len
);
LOG_DEBUG("Subject Name: %s", *subject_name);
LOG_TRACE("get_subject_name exit");
return true;
error:
free(*subject_name);
LOG_ERROR("get_subject_name exit with error");
return false;
}
static bool
get_issuer_name(PCCERT_CONTEXT cert, /*out*/ char **issuer_name)
{
LOG_TRACE("get_issuer_name enter");
DWORD len = cp_function_list.CertNameToStr(
cert->dwCertEncodingType,
&cert->pCertInfo->Issuer,
CERT_X500_NAME_STR,
NULL,
0
);
*issuer_name = malloc(len);
if (*issuer_name == NULL) {
LOG_ERROR("Could not allocate memory for Issuer Name str (%d bytes)", len);
goto error;
}
cp_function_list.CertNameToStr(
cert->dwCertEncodingType,
&cert->pCertInfo->Issuer,
CERT_X500_NAME_STR,
*issuer_name,
len
);
LOG_DEBUG("Issuer Name: %s", *issuer_name);
LOG_TRACE("get_issuer_name exit");
return true;
error:
free(*issuer_name);
LOG_ERROR("get_issuer_name exit with error");
return false;
}
static bool
get_signer_cert_info(PCCERT_CONTEXT signer_cert, cert_info_t *signer_cert_info)
{
LOG_TRACE("get_signer_cert_info enter");
if (!get_subject_name(signer_cert, &signer_cert_info->subject)) {
goto error;
}
if (!get_issuer_name(signer_cert, &signer_cert_info->issuer)) {
goto error;
}
LOG_TRACE("get_signer_cert_info exit");
return true;
error:
LOG_ERROR("get_signer_cert_info exit with error");
return false;
}
/*
* Проверка количества подписантов сообщения
* Предполагается, что количество подписантов должно быть равно 1
*/
static bool
check_message_signer_count(const str_t *sign)
{
int signer_count = cp_function_list.CryptGetMessageSignerCount(
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
(const BYTE *) sign->data,
(DWORD) sign->len);
LOG_DEBUG("signer count = %d", signer_count);
if (signer_count == 1) {
LOG_DEBUG("signer count is ok");
return true;
}
if (signer_count < 0) {
LOG_ERROR("Could not get signer count. Last error code: 0x%08x",
cp_function_list.GetLastError());
} else {
LOG_ERROR("Invalid signer count: %d", signer_count);
}
return false;
}
/*
* Проверка подписи и извлечение сертификата подписанта
* Предполагается, что количество подписантов проверено и равно 1
*/
static bool
verify_detached_message_signature(const str_t *data, const str_t *sign,
timer_context_t *timer_ctx,
/*out*/ PCCERT_CONTEXT *signer_cert)
{
LOG_TRACE("verify_detached_message_signature enter");
timer_on_verify_detached_message_signature_enter(timer_ctx);
CRYPT_VERIFY_MESSAGE_PARA verifyParams = {
.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA),
.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING
};
BOOL r = cp_function_list.CryptVerifyDetachedMessageSignature(
&verifyParams,
0, // индекс проверяемой подписи
(CONST BYTE *) sign->data,
sign->len,
1, // количество подписанных сообщений
(const BYTE **) &(data->data),
(DWORD*) &(data->len),
signer_cert
);
timer_on_verify_detached_message_signature_exit(timer_ctx);
LOG_TRACE("verify_detached_message_signature exit");
return r;
}
int
cryptopro_verify_detached(cryptopro_context_t *ctx, const str_t *data, const str_t *sign,
bool *is_verified, /*out*/ cert_info_t *signer_cert_info,
/*out*/ char **verify_error, /*out*/ const char **error_code)
{
LOG_TRACE("cryptopro_verify_detached enter");
timer_on_cryptopro_verify_detached_enter(ctx->timer_ctx);
*is_verified = false;
PCCERT_CONTEXT signer_cert = NULL;
if (!check_message_signer_count(sign)) {
goto error;
}
if (verify_detached_message_signature(data, sign, ctx->timer_ctx, &signer_cert)) {
LOG_DEBUG("sign is valid");
if (!check_cert_key_usage(signer_cert)) {
goto error;
}
if (!verify_cert_chain(signer_cert, ctx->timer_ctx, error_code)) {
goto error;
}
if (!get_signer_cert_info(signer_cert, signer_cert_info)) {
goto error;
}
*is_verified = true;
} else {
LOG_DEBUG("sign is invalid");
get_verify_error(verify_error);
}
if (signer_cert != NULL) {
cp_function_list.CertFreeCertificateContext(signer_cert);
}
timer_on_cryptopro_verify_detached_exit(ctx->timer_ctx);
LOG_TRACE("cryptopro_verify_detached exit");
return 0;
error:
if (signer_cert != NULL) {
cp_function_list.CertFreeCertificateContext(signer_cert);
}
LOG_ERROR("cryptopro_verify_detached exit with error. Last error code: 0x%08x",
cp_function_list.GetLastError());
return -1;
}
int
cryptopro_gen_random(const cryptopro_context_t *ctx, unsigned char* data, size_t len)
{