Update X509 cert chain verify

This commit is contained in:
Zhi Guan
2026-06-18 19:25:15 +08:00
parent c5468b4c1b
commit 055d3fe812
5 changed files with 356 additions and 5 deletions

View File

@@ -118,6 +118,7 @@ set(src
src/x509_alg.c src/x509_alg.c
src/x509_cer.c src/x509_cer.c
src/x509_ext.c src/x509_ext.c
src/x509_vrf.c
src/x509_req.c src/x509_req.c
src/x509_crl.c src/x509_crl.c
src/x509_new.c src/x509_new.c
@@ -191,6 +192,7 @@ set(tests
x509_alg x509_alg
x509_str x509_str
x509_ext x509_ext
x509_vrf
x509_req x509_req
x509_crl x509_crl
x509_key x509_key
@@ -818,7 +820,7 @@ endif()
# #
set(CPACK_PACKAGE_NAME "GmSSL") set(CPACK_PACKAGE_NAME "GmSSL")
set(CPACK_PACKAGE_VENDOR "GmSSL develop team") set(CPACK_PACKAGE_VENDOR "GmSSL develop team")
set(CPACK_PACKAGE_VERSION "3.2.0-dev.1087") set(CPACK_PACKAGE_VERSION "3.2.0-dev.1088")
set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md) set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md)
set(CPACK_NSIS_MODIFY_PATH ON) set(CPACK_NSIS_MODIFY_PATH ON)
include(CPack) include(CPack)

View File

@@ -18,7 +18,7 @@ extern "C" {
#define GMSSL_VERSION_NUM 30200 #define GMSSL_VERSION_NUM 30200
#define GMSSL_VERSION_STR "GmSSL 3.2.0-dev.1087" #define GMSSL_VERSION_STR "GmSSL 3.2.0-dev.1088"
int gmssl_version_num(void); int gmssl_version_num(void);
const char *gmssl_version_str(void); const char *gmssl_version_str(void);

View File

@@ -1833,7 +1833,6 @@ int x509_cert_check(const uint8_t *cert, size_t certlen, int cert_type,
size_t extslen; size_t extslen;
int sig_algor; int sig_algor;
if (x509_cert_get_details(cert, certlen, if (x509_cert_get_details(cert, certlen,
&version, // version &version, // version
&serial, &serial_len, // serial &serial, &serial_len, // serial
@@ -1871,12 +1870,12 @@ int x509_cert_check(const uint8_t *cert, size_t certlen, int cert_type,
return -1; return -1;
} }
// check issuer and subject not empty // check issuer and subject name requirements
if (x509_name_check(issuer, issuer_len) != 1) { if (x509_name_check(issuer, issuer_len) != 1) {
error_print(); error_print();
return -1; return -1;
} }
if (x509_name_check(subject, subject_len) != 1) { if (x509_cert_check_subject(cert, certlen, cert_type) != 1) {
error_print(); error_print();
return -1; return -1;
} }

218
src/x509_vrf.c Normal file
View File

@@ -0,0 +1,218 @@
/*
* Copyright 2014-2026 The GmSSL Project. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <gmssl/asn1.h>
#include <gmssl/oid.h>
#include <gmssl/x509_ext.h>
#include <gmssl/x509_cer.h>
#include <gmssl/error.h>
static int x509_general_name_check(int choice, const uint8_t *d, size_t dlen)
{
const uint8_t *p;
size_t len;
const uint8_t *q;
size_t qlen;
uint32_t nodes[32];
size_t nodes_cnt;
int tag;
int ret;
if (!d || !dlen) {
error_print();
return -1;
}
switch (choice) {
case X509_gn_other_name:
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1
|| asn1_length_is_zero(dlen) != 1
|| asn1_object_identifier_from_der(nodes, &nodes_cnt, &p, &len) != 1
|| asn1_explicit_from_der(0, &q, &qlen, &p, &len) != 1
|| asn1_length_is_zero(len) != 1) {
error_print();
return -1;
}
if (!qlen) {
error_print();
return -1;
}
break;
case X509_gn_rfc822_name:
case X509_gn_dns_name:
case X509_gn_uniform_resource_identifier:
if (asn1_string_is_ia5_string((const char *)d, dlen) != 1) {
error_print();
return -1;
}
break;
case X509_gn_x400_address:
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1
|| asn1_length_is_zero(dlen) != 1
|| !len) {
error_print();
return -1;
}
break;
case X509_gn_directory_name:
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1
|| asn1_length_is_zero(dlen) != 1
|| x509_name_check(p, len) != 1) {
error_print();
return -1;
}
break;
case X509_gn_edi_party_name:
if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
if ((ret = x509_explicit_directory_name_from_der(0, &tag, &q, &qlen, &p, &len)) < 0) {
error_print();
return -1;
}
if (ret && x509_directory_name_check(tag, q, qlen) != 1) {
error_print();
return -1;
}
if (x509_explicit_directory_name_from_der(1, &tag, &q, &qlen, &p, &len) != 1
|| x509_directory_name_check(tag, q, qlen) != 1
|| asn1_length_is_zero(len) != 1) {
error_print();
return -1;
}
break;
case X509_gn_ip_address:
if (dlen != 4 && dlen != 16) {
error_print();
return -1;
}
break;
case X509_gn_registered_id:
if (asn1_object_identifier_from_octets(nodes, &nodes_cnt, d, dlen) != 1) {
error_print();
return -1;
}
break;
default:
error_print();
return -1;
}
return 1;
}
static int x509_general_names_check(const uint8_t *d, size_t dlen)
{
int choice;
const uint8_t *name;
size_t namelen;
if (!d || !dlen) {
error_print();
return -1;
}
while (dlen) {
if (x509_general_name_from_der(&choice, &name, &namelen, &d, &dlen) != 1
|| x509_general_name_check(choice, name, namelen) != 1) {
error_print();
return -1;
}
}
return 1;
}
int x509_cert_check_subject(const uint8_t *cert, size_t certlen, int cert_type)
{
int ret;
int is_cacert;
const uint8_t *subject;
size_t subject_len;
const uint8_t *exts;
size_t extslen;
const uint8_t *val;
size_t vlen;
const uint8_t *gns;
size_t gnslen;
int critical;
if (!cert || !certlen) {
error_print();
return -1;
}
switch (cert_type) {
case X509_cert_server_auth:
case X509_cert_client_auth:
case X509_cert_server_key_encipher:
case X509_cert_client_key_encipher:
is_cacert = 0;
break;
case X509_cert_ca:
case X509_cert_root_ca:
case X509_cert_crl_sign:
is_cacert = 1;
break;
default:
error_print();
return -1;
}
if (x509_cert_get_subject(cert, certlen, &subject, &subject_len) != 1) {
error_print();
return -1;
}
if ((ret = x509_name_check(subject, subject_len)) < 0) {
error_print();
return -1;
}
if (ret == 0) {
if (is_cacert) {
error_print();
return -1;
}
if (x509_cert_get_exts(cert, certlen, &exts, &extslen) != 1) {
error_print();
return -1;
}
if (x509_exts_get_ext_by_oid(exts, extslen, OID_ce_subject_alt_name, &critical, &val, &vlen) != 1) {
error_print();
return -1;
}
if (critical != X509_critical) {
error_print();
return -1;
}
if (asn1_sequence_from_der(&gns, &gnslen, &val, &vlen) != 1
|| asn1_length_is_zero(vlen) != 1
|| x509_general_names_check(gns, gnslen) != 1) {
error_print();
return -1;
}
}
return 1;
}

132
tests/x509_vrftest.c Normal file
View File

@@ -0,0 +1,132 @@
/*
* Copyright 2014-2026 The GmSSL Project. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gmssl/oid.h>
#include <gmssl/x509_alg.h>
#include <gmssl/x509_ext.h>
#include <gmssl/x509.h>
#include <gmssl/rand.h>
#include <gmssl/error.h>
static int set_x509_name(uint8_t *name, size_t *namelen, size_t maxlen)
{
*namelen = 0;
if (x509_name_add_country_name(name, namelen, maxlen, "CN") != 1
|| x509_name_add_locality_name(name, namelen, maxlen, ASN1_TAG_PrintableString, (uint8_t *)"Haidian", strlen("Haidian")) != 1
|| x509_name_add_state_or_province_name(name, namelen, maxlen, ASN1_TAG_PrintableString, (uint8_t *)"Beijing", strlen("Beijing")) != 1
|| x509_name_add_organization_name(name, namelen, maxlen, ASN1_TAG_PrintableString, (uint8_t *)"PKU", strlen("PKU")) != 1
|| x509_name_add_organizational_unit_name(name, namelen, maxlen, ASN1_TAG_PrintableString, (uint8_t *)"CS", strlen("CS")) != 1
|| x509_name_add_common_name(name, namelen, maxlen, ASN1_TAG_PrintableString, (uint8_t *)"CA", strlen("CA")) != 1) {
error_print();
return -1;
}
return 1;
}
static int test_x509_cert_check_subject(void)
{
int algor = OID_ec_public_key;
int algor_param = OID_sm2;
uint8_t serial[20] = { 0x01, 0x00 };
uint8_t issuer[256];
size_t issuer_len = 0;
time_t not_before, not_after;
uint8_t empty_subject[1] = {0};
X509_KEY x509_key;
uint8_t gns[256];
size_t gnslen;
uint8_t exts[512];
size_t extslen;
uint8_t cert[1024];
uint8_t *p;
size_t certlen;
int path_len_constraint;
set_x509_name(issuer, &issuer_len, sizeof(issuer));
time(&not_before);
x509_validity_add_days(&not_after, not_before, 365);
if (x509_key_generate(&x509_key, algor, &algor_param, sizeof(algor_param)) != 1) {
error_print();
return -1;
}
gnslen = 0;
extslen = 0;
p = cert;
certlen = 0;
if (x509_general_names_add_dns_name(gns, &gnslen, sizeof(gns), "www.example.com") != 1
|| x509_exts_add_subject_alt_name(exts, &extslen, sizeof(exts),
X509_critical, gns, gnslen) != 1
|| x509_cert_sign_to_der(
X509_version_v3,
serial, sizeof(serial),
OID_sm2sign_with_sm3,
issuer, issuer_len,
not_before, not_after,
empty_subject, 0,
&x509_key,
NULL, 0,
NULL, 0,
exts, extslen,
&x509_key, SM2_DEFAULT_ID, strlen(SM2_DEFAULT_ID),
&p, &certlen) != 1
|| x509_cert_check_subject(cert, certlen, 0) != 1
|| x509_cert_check(cert, certlen, X509_cert_server_auth, &path_len_constraint) != 1) {
error_print();
return -1;
}
gnslen = 0;
extslen = 0;
p = cert;
certlen = 0;
if (x509_general_names_add_dns_name(gns, &gnslen, sizeof(gns), "www.example.com") != 1
|| x509_exts_add_subject_alt_name(exts, &extslen, sizeof(exts),
X509_non_critical, gns, gnslen) != 1
|| x509_cert_sign_to_der(
X509_version_v3,
serial, sizeof(serial),
OID_sm2sign_with_sm3,
issuer, issuer_len,
not_before, not_after,
empty_subject, 0,
&x509_key,
NULL, 0,
NULL, 0,
exts, extslen,
&x509_key, SM2_DEFAULT_ID, strlen(SM2_DEFAULT_ID),
&p, &certlen) != 1) {
error_print();
return -1;
}
if (x509_cert_check_subject(cert, certlen, 0) == 1) {
error_print();
return -1;
}
printf("%s() ok\n", __FUNCTION__);
return 1;
}
int main(void)
{
if (test_x509_cert_check_subject() != 1) goto err;
printf("%s all tests passed\n", __FILE__);
return 0;
err:
error_print();
return 1;
}