From 2d98b5afae052d0329d2562d1c80b3b5f190dd5d Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Fri, 19 Jun 2026 10:44:54 +0800 Subject: [PATCH] Update x509_certs_verify_tlcp --- CMakeLists.txt | 2 +- include/gmssl/version.h | 2 +- include/gmssl/x509_cer.h | 6 + src/x509_cer.c | 86 +++++---- src/x509_vrf.c | 379 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 436 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b3a1257..59cd2a14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -820,7 +820,7 @@ endif() # set(CPACK_PACKAGE_NAME "GmSSL") set(CPACK_PACKAGE_VENDOR "GmSSL develop team") -set(CPACK_PACKAGE_VERSION "3.2.0-dev.1099") +set(CPACK_PACKAGE_VERSION "3.2.0-dev.1100") set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md) set(CPACK_NSIS_MODIFY_PATH ON) include(CPack) diff --git a/include/gmssl/version.h b/include/gmssl/version.h index 6594af3f..f4e864bf 100644 --- a/include/gmssl/version.h +++ b/include/gmssl/version.h @@ -18,7 +18,7 @@ extern "C" { #define GMSSL_VERSION_NUM 30200 -#define GMSSL_VERSION_STR "GmSSL 3.2.0-dev.1099" +#define GMSSL_VERSION_STR "GmSSL 3.2.0-dev.1100" int gmssl_version_num(void); const char *gmssl_version_str(void); diff --git a/include/gmssl/x509_cer.h b/include/gmssl/x509_cer.h index 3d4e9374..021d1e48 100644 --- a/include/gmssl/x509_cer.h +++ b/include/gmssl/x509_cer.h @@ -336,6 +336,8 @@ int x509_cert_check_subject(const uint8_t *cert, size_t certlen, int is_cacert); int x509_cert_check_name_constraints(const uint8_t *cert, size_t certlen, const uint8_t *name_constraints, size_t name_constraints_len); int x509_cert_is_self_issued(const uint8_t *cert, size_t certlen); +int x509_tlcp_cert_pair_entity_match(const uint8_t *sign_cert, size_t sign_certlen, + const uint8_t *kenc_cert, size_t kenc_certlen); /* IssuerAndSerialNumber ::= SEQUENCE { @@ -385,6 +387,10 @@ int x509_certs_check_name_constraints(const uint8_t *cert_chain, size_t cert_cha const uint8_t *rootcacert, size_t rootcacertlen); int x509_certs_check_basic_constraints(const uint8_t *cert_chain, size_t cert_chain_len, const uint8_t *rootcacert, size_t rootcacertlen); +int x509_certs_check_name_constraints_tlcp(const uint8_t *cert_chain, size_t cert_chain_len, + const uint8_t *rootcacert, size_t rootcacertlen); +int x509_certs_check_basic_constraints_tlcp(const uint8_t *cert_chain, size_t cert_chain_len, + const uint8_t *rootcacert, size_t rootcacertlen); int x509_certs_get_subjects(const uint8_t *certs, size_t certslen, uint8_t *names, size_t *nameslen); int x509_certs_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen); diff --git a/src/x509_cer.c b/src/x509_cer.c index c8f4adb9..7274af64 100644 --- a/src/x509_cer.c +++ b/src/x509_cer.c @@ -2003,13 +2003,14 @@ int x509_certs_verify_tlcp(const uint8_t *certs, size_t certslen, int certs_type { int sign_cert_type; int kenc_cert_type; + const uint8_t *cert_chain = certs; + size_t cert_chain_len = certslen; const uint8_t *cert; size_t certlen; const uint8_t *kenc_cert; size_t kenc_certlen; const uint8_t *cacert; size_t cacertlen; - int matched_root = 0; int ret; int path_len = 0; @@ -2047,6 +2048,15 @@ int x509_certs_verify_tlcp(const uint8_t *certs, size_t certslen, int certs_type error_print(); return -1; } + if ((ret = x509_tlcp_cert_pair_entity_match(cert, certlen, + kenc_cert, kenc_certlen)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + error_print(); + return -1; + } while (certslen) { @@ -2067,8 +2077,7 @@ int x509_certs_verify_tlcp(const uint8_t *certs, size_t certslen, int certs_type return -1; } } - if ((path_len_constraint >= 0 && path_len > path_len_constraint) - || path_len > depth) { + if (path_len > depth) { error_print(); return -1; } @@ -2084,42 +2093,45 @@ int x509_certs_verify_tlcp(const uint8_t *certs, size_t certslen, int certs_type path_len++; } - while (rootcertslen) { - if (x509_cert_from_der(&cacert, &cacertlen, &rootcerts, &rootcertslen) != 1) { - error_print(); - return -1; - } - if ((ret = x509_cert_is_signed_by_root_ca_cert(cert, certlen, cacert, cacertlen, - SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH)) < 0) { - error_print(); - return -1; - } - if (ret == 0) { - continue; - } - if (x509_cert_check(cacert, cacertlen, X509_cert_ca, &path_len_constraint) != 1) { - error_print(); - return -1; - } - if ((path_len_constraint >= 0 && path_len > path_len_constraint) - || path_len > depth) { - error_print(); - return -1; - } - - // when no mid CA certs - if (path_len == 0) { - if (x509_cert_verify_by_ca_cert(kenc_cert, kenc_certlen, cacert, cacertlen, - SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH) != 1) { - error_print(); - return -1; - } - } - matched_root = 1; - break; + if ((ret = x509_cert_chain_find_root_ca_cert(cert, certlen, + rootcerts, rootcertslen, &cacert, &cacertlen, + SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + error_print(); + return -1; + } + if (x509_cert_check(cacert, cacertlen, X509_cert_ca, &path_len_constraint) != 1) { + error_print(); + return -1; + } + if (path_len > depth) { + error_print(); + return -1; } - if (!matched_root) { + // when no mid CA certs + if (path_len == 0) { + if (x509_cert_verify_by_ca_cert(kenc_cert, kenc_certlen, cacert, cacertlen, + SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH) != 1) { + error_print(); + return -1; + } + } + + if (x509_certs_check_basic_constraints_tlcp(cert_chain, cert_chain_len, + cacert, cacertlen) != 1) { + error_print(); + return -1; + } + if ((ret = x509_certs_check_name_constraints_tlcp(cert_chain, cert_chain_len, + cacert, cacertlen)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { error_print(); return -1; } diff --git a/src/x509_vrf.c b/src/x509_vrf.c index 1488c0a5..1c85e5e1 100644 --- a/src/x509_vrf.c +++ b/src/x509_vrf.c @@ -2192,3 +2192,382 @@ int x509_cert_chain_find_root_ca_cert(const uint8_t *cert_chain, size_t cert_cha } return found_root; } + +static int x509_tlcp_certs_get_ca_certs(const uint8_t *cert_chain, size_t cert_chain_len, + const uint8_t **ca_certs, size_t *ca_certs_len) +{ + const uint8_t *cert; + size_t certlen; + + if (!cert_chain || !cert_chain_len || !ca_certs || !ca_certs_len) { + error_print(); + return -1; + } + + if (x509_cert_from_der(&cert, &certlen, &cert_chain, &cert_chain_len) != 1 + || x509_cert_from_der(&cert, &certlen, &cert_chain, &cert_chain_len) != 1) { + error_print(); + return -1; + } + *ca_certs = cert_chain; + *ca_certs_len = cert_chain_len; + return 1; +} + +static int x509_tlcp_certs_count_non_self_issued_ca_certs(const uint8_t *cert_chain, + size_t cert_chain_len, size_t *count) +{ + int ret; + const uint8_t *ca_certs; + size_t ca_certs_len; + const uint8_t *cert; + size_t certlen; + + if (!cert_chain || !cert_chain_len || !count) { + error_print(); + return -1; + } + *count = 0; + + if (x509_tlcp_certs_get_ca_certs(cert_chain, cert_chain_len, + &ca_certs, &ca_certs_len) != 1) { + error_print(); + return -1; + } + while (ca_certs_len) { + if (x509_cert_from_der(&cert, &certlen, &ca_certs, &ca_certs_len) != 1) { + error_print(); + return -1; + } + if ((ret = x509_cert_is_self_issued(cert, certlen)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + (*count)++; + } + } + return 1; +} + +int x509_certs_check_basic_constraints_tlcp(const uint8_t *cert_chain, size_t cert_chain_len, + const uint8_t *rootcacert, size_t rootcacertlen) +{ + const uint8_t *chain; + size_t chain_len; + const uint8_t *cert; + size_t certlen; + size_t checked_certs_len; + size_t path_len; + int path_len_constraint; + + if (!cert_chain || !cert_chain_len + || (!rootcacert && rootcacertlen) + || (rootcacert && !rootcacertlen)) { + error_print(); + return -1; + } + + chain = cert_chain; + chain_len = cert_chain_len; + + if (x509_cert_from_der(&cert, &certlen, &chain, &chain_len) != 1) { + error_print(); + return -1; + } + if (x509_cert_check_basic_constraints_by_type(cert, certlen, + X509_cert_server_auth, &path_len_constraint) != 1) { + error_print(); + return -1; + } + checked_certs_len = certlen; + + if (x509_cert_from_der(&cert, &certlen, &chain, &chain_len) != 1) { + error_print(); + return -1; + } + if (x509_cert_check_basic_constraints_by_type(cert, certlen, + X509_cert_server_key_encipher, &path_len_constraint) != 1) { + error_print(); + return -1; + } + checked_certs_len += certlen; + + while (chain_len) { + if (x509_cert_from_der(&cert, &certlen, &chain, &chain_len) != 1) { + error_print(); + return -1; + } + if (x509_cert_check_basic_constraints_by_type(cert, certlen, + X509_cert_ca, &path_len_constraint) != 1) { + error_print(); + return -1; + } + if (x509_tlcp_certs_count_non_self_issued_ca_certs(cert_chain, + checked_certs_len, &path_len) != 1) { + error_print(); + return -1; + } + if (path_len_constraint >= 0 && path_len > (size_t)path_len_constraint) { + error_print(); + return -1; + } + checked_certs_len += certlen; + } + + if (rootcacert) { + if (x509_cert_check_basic_constraints_by_type(rootcacert, rootcacertlen, + X509_cert_root_ca, &path_len_constraint) != 1) { + error_print(); + return -1; + } + if (x509_tlcp_certs_count_non_self_issued_ca_certs(cert_chain, + cert_chain_len, &path_len) != 1) { + error_print(); + return -1; + } + if (path_len_constraint >= 0 && path_len > (size_t)path_len_constraint) { + error_print(); + return -1; + } + } + + return 1; +} + +int x509_certs_check_name_constraints_tlcp(const uint8_t *cert_chain, size_t cert_chain_len, + const uint8_t *rootcacert, size_t rootcacertlen) +{ + int ret; + const uint8_t *chain; + size_t chain_len; + const uint8_t *cert; + size_t certlen; + const uint8_t *sign_cert; + size_t sign_certlen; + const uint8_t *kenc_cert; + size_t kenc_certlen; + const uint8_t *name_constraints; + size_t name_constraints_len; + size_t checked_certs_len; + + if (!cert_chain || !cert_chain_len + || (!rootcacert && rootcacertlen) + || (rootcacert && !rootcacertlen)) { + error_print(); + return -1; + } + + chain = cert_chain; + chain_len = cert_chain_len; + if (x509_cert_from_der(&sign_cert, &sign_certlen, &chain, &chain_len) != 1 + || x509_cert_from_der(&kenc_cert, &kenc_certlen, &chain, &chain_len) != 1) { + error_print(); + return -1; + } + + if (rootcacert) { + if ((ret = x509_cert_get_name_constraints(rootcacert, rootcacertlen, + &name_constraints, &name_constraints_len)) < 0) { + error_print(); + return -1; + } + if (ret) { + if (chain_len) { + if (x509_certs_get_last(chain, chain_len, &cert, &certlen) != 1) { + error_print(); + return -1; + } + if ((ret = x509_cert_check_name_constraints(cert, certlen, + name_constraints, name_constraints_len)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + return 0; + } + } else { + if ((ret = x509_cert_check_name_constraints(sign_cert, sign_certlen, + name_constraints, name_constraints_len)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + return 0; + } + if ((ret = x509_cert_check_name_constraints(kenc_cert, kenc_certlen, + name_constraints, name_constraints_len)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + return 0; + } + } + } + } + + checked_certs_len = sign_certlen + kenc_certlen; + while (chain_len) { + if (x509_cert_from_der(&cert, &certlen, &chain, &chain_len) != 1) { + error_print(); + return -1; + } + if ((ret = x509_cert_get_name_constraints(cert, certlen, + &name_constraints, &name_constraints_len)) < 0) { + error_print(); + return -1; + } + if (ret) { + if ((ret = x509_certs_check_name_constraints_by_name_constraints( + cert_chain, checked_certs_len, + name_constraints, name_constraints_len)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + return 0; + } + } + checked_certs_len += certlen; + } + return 1; +} + +static int x509_cert_get_subject_alt_name_der(const uint8_t *cert, size_t certlen, + int *critical, const uint8_t **subject_alt_name, size_t *subject_alt_name_len) +{ + int has_exts; + int has_subject_alt_name; + const uint8_t *exts; + size_t extslen; + + if (!cert || !certlen || !critical || !subject_alt_name || !subject_alt_name_len) { + error_print(); + return -1; + } + *critical = -1; + *subject_alt_name = NULL; + *subject_alt_name_len = 0; + + if ((has_exts = x509_cert_get_exts(cert, certlen, &exts, &extslen)) < 0) { + error_print(); + return -1; + } + if (!has_exts) { + return 0; + } + if ((has_subject_alt_name = x509_exts_get_ext_by_oid(exts, extslen, + OID_ce_subject_alt_name, critical, subject_alt_name, subject_alt_name_len)) < 0) { + error_print(); + return -1; + } + return has_subject_alt_name; +} + +// TLCP 双证书的名字和算法要求严格匹配,不做 normalized 匹配。 +int x509_tlcp_cert_pair_entity_match(const uint8_t *sign_cert, size_t sign_certlen, + const uint8_t *kenc_cert, size_t kenc_certlen) +{ + int sign_has_subject_alt_name; + int kenc_has_subject_alt_name; + const uint8_t *sign_subject; + size_t sign_subject_len; + const uint8_t *kenc_subject; + size_t kenc_subject_len; + const uint8_t *sign_subject_alt_name; + size_t sign_subject_alt_name_len; + int sign_subject_alt_name_critical; + const uint8_t *kenc_subject_alt_name; + size_t kenc_subject_alt_name_len; + int kenc_subject_alt_name_critical; + const uint8_t *sign_issuer; + size_t sign_issuer_len; + const uint8_t *kenc_issuer; + size_t kenc_issuer_len; + X509_KEY sign_public_key; + X509_KEY kenc_public_key; + int sign_signature_algor; + int kenc_signature_algor; + int match = 0; + + if (!sign_cert || !sign_certlen || !kenc_cert || !kenc_certlen) { + error_print(); + return -1; + } + + if (x509_cert_get_subject(sign_cert, sign_certlen, &sign_subject, &sign_subject_len) != 1 + || x509_cert_get_subject(kenc_cert, kenc_certlen, &kenc_subject, &kenc_subject_len) != 1) { + error_print(); + return -1; + } + if (sign_subject_len != kenc_subject_len) { + return 0; + } + if (sign_subject_len && memcmp(sign_subject, kenc_subject, sign_subject_len) != 0) { + return 0; + } + + if ((sign_has_subject_alt_name = x509_cert_get_subject_alt_name_der(sign_cert, sign_certlen, + &sign_subject_alt_name_critical, + &sign_subject_alt_name, &sign_subject_alt_name_len)) < 0) { + error_print(); + return -1; + } + if ((kenc_has_subject_alt_name = x509_cert_get_subject_alt_name_der(kenc_cert, kenc_certlen, + &kenc_subject_alt_name_critical, + &kenc_subject_alt_name, &kenc_subject_alt_name_len)) < 0) { + error_print(); + return -1; + } + if (sign_has_subject_alt_name != kenc_has_subject_alt_name) { + return 0; + } + if (sign_has_subject_alt_name) { + if (sign_subject_alt_name_critical != kenc_subject_alt_name_critical) { + return 0; + } + if (sign_subject_alt_name_len != kenc_subject_alt_name_len) { + return 0; + } + if (sign_subject_alt_name_len + && memcmp(sign_subject_alt_name, kenc_subject_alt_name, + sign_subject_alt_name_len) != 0) { + return 0; + } + } + + if (x509_cert_get_issuer(sign_cert, sign_certlen, &sign_issuer, &sign_issuer_len) != 1 + || x509_cert_get_issuer(kenc_cert, kenc_certlen, &kenc_issuer, &kenc_issuer_len) != 1) { + error_print(); + return -1; + } + if (sign_issuer_len != kenc_issuer_len) { + return 0; + } + if (sign_issuer_len && memcmp(sign_issuer, kenc_issuer, sign_issuer_len) != 0) { + return 0; + } + + if (x509_cert_get_subject_public_key(sign_cert, sign_certlen, &sign_public_key) != 1 + || x509_cert_get_subject_public_key(kenc_cert, kenc_certlen, &kenc_public_key) != 1) { + error_print(); + return -1; + } + if (sign_public_key.algor != kenc_public_key.algor + || sign_public_key.algor_param != kenc_public_key.algor_param) { + return 0; + } + + if (x509_cert_get_signature_algor(sign_cert, sign_certlen, &sign_signature_algor) != 1 + || x509_cert_get_signature_algor(kenc_cert, kenc_certlen, &kenc_signature_algor) != 1) { + error_print(); + return -1; + } + if (sign_signature_algor != kenc_signature_algor) { + return 0; + } + + match = 1; + return match; +}