From 8397280779e179f8fa9a7d23eeeb41807a23f09a Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Tue, 31 Jan 2023 22:14:41 +0800 Subject: [PATCH] Add `-check_crl` option to `certverify` --- include/gmssl/x509.h | 3 +- include/gmssl/x509_crl.h | 4 +- src/x509_cer.c | 25 +++++++++ src/x509_crl.c | 112 +++++++++++++++++++++++++++++++++++++++ tools/certverify.c | 42 +++++++++++++-- tools/crlget.c | 19 +++++-- 6 files changed, 194 insertions(+), 11 deletions(-) diff --git a/include/gmssl/x509.h b/include/gmssl/x509.h index b857a3dc..ae3c29fa 100644 --- a/include/gmssl/x509.h +++ b/include/gmssl/x509.h @@ -340,8 +340,7 @@ int x509_cert_get_issuer_and_serial_number(const uint8_t *a, size_t alen, int x509_cert_get_issuer(const uint8_t *a, size_t alen, const uint8_t **name, size_t *namelen); int x509_cert_get_subject(const uint8_t *a, size_t alen, const uint8_t **subj, size_t *subj_len); int x509_cert_get_subject_public_key(const uint8_t *a, size_t alen, SM2_KEY *public_key); -#define x509_cert_get_exts(a,alen,d,dlen) x509_cert_get_details(a,alen,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,d,dlen,NULL,NULL,NULL) - +int x509_cert_get_exts(const uint8_t *a, size_t alen, const uint8_t **d, size_t *dlen); int x509_certs_to_pem(const uint8_t *d, size_t dlen, FILE *fp); int x509_certs_from_pem(uint8_t *d, size_t *dlen, size_t maxlen, FILE *fp); diff --git a/include/gmssl/x509_crl.h b/include/gmssl/x509_crl.h index 1de3b6a3..6b1675e7 100644 --- a/include/gmssl/x509_crl.h +++ b/include/gmssl/x509_crl.h @@ -304,7 +304,9 @@ int x509_crls_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t int x509_crl_new_from_uri(uint8_t **crl, size_t *crl_len, const char *uri, size_t urilen); - +int x509_crl_new_from_cert(uint8_t **crl, size_t *crl_len, const uint8_t *cert, size_t certlen); +int x509_cert_check_crl(const uint8_t *cert, size_t certlen, const uint8_t *cacert, size_t cacertlen, + const char *ca_signer_id, size_t ca_signer_id_len); #ifdef __cplusplus diff --git a/src/x509_cer.c b/src/x509_cer.c index f884339e..9dcdfdfa 100644 --- a/src/x509_cer.c +++ b/src/x509_cer.c @@ -1495,6 +1495,31 @@ int x509_cert_get_issuer(const uint8_t *a, size_t alen, const uint8_t **d, size_ NULL, NULL); // signature } +int x509_cert_get_exts(const uint8_t *a, size_t alen, const uint8_t **d, size_t *dlen) +{ + if (x509_cert_get_details(a, alen, + NULL, // version + NULL, NULL, // serial + NULL, // signature_algor + NULL, NULL, // issuer + NULL, NULL, // validity + NULL, NULL, // subject + NULL, // subject_public_key + NULL, NULL, // issuer_unique_id + NULL, NULL, // subject_unique_id + d, dlen, // extensions + NULL, // signature_algor + NULL, NULL // signature + ) != 1) { + error_print(); + return -1; + } + if (!d || !dlen) { + return 0; + } + return 1; +} + int x509_certs_to_pem(const uint8_t *d, size_t dlen, FILE *fp) { const uint8_t *a; diff --git a/src/x509_crl.c b/src/x509_crl.c index db26b018..e59df376 100644 --- a/src/x509_crl.c +++ b/src/x509_crl.c @@ -1732,3 +1732,115 @@ end: if (buf) free(buf); return ret; } + +int x509_crl_new_from_cert(uint8_t **crl, size_t *crl_len, const uint8_t *cert, size_t certlen) +{ + int ret; + const uint8_t *exts; + size_t extslen; + + int critical; + const uint8_t *val; + size_t vlen; + + const char *uri; + size_t urilen; + int reason; + const uint8_t *crl_issuer; + size_t crl_issuer_len; + + if ((ret = x509_cert_get_exts(cert, certlen, &exts, &extslen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if ((ret = x509_exts_get_ext_by_oid(exts, extslen, + OID_ce_crl_distribution_points, &critical, &val, &vlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (x509_uri_as_distribution_points_from_der(&uri, &urilen, + &reason, &crl_issuer, &crl_issuer_len, &val, &vlen) != 1 + || asn1_length_is_zero(vlen) != 1) { + error_print(); + return -1; + } + if (!uri) { + *crl = NULL; + *crl_len = 0; + return 0; + } + if (x509_crl_new_from_uri(crl, crl_len, uri, urilen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int x509_cert_check_crl(const uint8_t *cert, size_t certlen, const uint8_t *cacert, size_t cacertlen, + const char *ca_signer_id, size_t ca_signer_id_len) +{ + int ret = -1; + uint8_t *crl = NULL; + size_t crl_len = 0; + const uint8_t *crl_issuer; + size_t crl_issuer_len; + + const uint8_t *issuer; + size_t issuer_len; + const uint8_t *serial; + size_t serial_len; + + time_t revoke_date; + const uint8_t *crl_entry_exts; + size_t crl_entry_exts_len; + + // download CRL and do basic validation + if (x509_crl_new_from_cert(&crl, &crl_len, cert, certlen) != 1) { + error_print(); + return -1; + } + if (x509_crl_validate(crl, crl_len, time(NULL)) != 1) { + error_print(); + goto end; + } + + if (x509_cert_get_issuer_and_serial_number(cert, certlen, &issuer, &issuer_len, &serial, &serial_len) != 1) { + error_print(); + goto end; + } + + // make sure CRL's issuer is the certificate issuer + if (x509_crl_get_issuer(crl, crl_len, &crl_issuer, &crl_issuer_len) != 1) { + error_print(); + goto end; + } + if (x509_name_equ(issuer, issuer_len, crl_issuer, crl_issuer_len) != 1) { + error_print(); + goto end; + } + + // verify CRL + if (x509_crl_verify_by_ca_cert(crl, crl_len, cacert, cacertlen, ca_signer_id, ca_signer_id_len) != 1) { + error_print(); + goto end; + } + + // check if the certificate in the CRL + if ((ret = x509_crl_find_revoked_cert_by_serial_number(crl, crl_len, serial, serial_len, + &revoke_date, &crl_entry_exts, &crl_entry_exts_len)) < 0) { + error_print(); + goto end; + } + if (ret == 1) { + ret = -1; + error_print(); + goto end; + } + ret = 1; + +end: + if (crl) free(crl); + return ret; +} + + diff --git a/tools/certverify.c b/tools/certverify.c index ae72e563..49a1651d 100644 --- a/tools/certverify.c +++ b/tools/certverify.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 The GmSSL Project. All Rights Reserved. + * Copyright 2014-2023 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. @@ -13,9 +13,24 @@ #include #include #include +#include -static const char *options = "-in pem [-double_certs] -cacert pem\n"; +static const char *usage = + " -in pem [-double_certs]" + " [-check_crl]" + " -cacert pem" + "\n"; + +static const char *options = +"Options\n" +"\n" +" -in pem Input certificate chain file in PEM format\n" +" -double_certs The first two certificates are SM2 signing and encryption entity certificate\n" +" -check_crl If the entity certificate has CRLDistributionPoints extension, Download and check againt the CRL\n" +" -cacert pem CA certificate\n" +"\n"; + int certverify_main(int argc, char **argv) { @@ -39,6 +54,8 @@ int certverify_main(int argc, char **argv) size_t enc_cert_len; int rv; + int check_crl = 0; + argc--; argv++; @@ -49,7 +66,8 @@ int certverify_main(int argc, char **argv) while (argc > 0) { if (!strcmp(*argv, "-help")) { - printf("usage: %s %s\n", prog, options); + printf("usage: %s %s\n", prog, usage); + printf("%s\n", options); ret = 0; goto end; } else if (!strcmp(*argv, "-in")) { @@ -61,6 +79,8 @@ int certverify_main(int argc, char **argv) } } else if (!strcmp(*argv, "-double_certs")) { double_certs = 1; + } else if (!strcmp(*argv, "-check_crl")) { + check_crl = 1; } else if (!strcmp(*argv, "-cacert")) { if (--argc < 1) goto bad; cacertfile = *(++argv); @@ -127,6 +147,12 @@ bad: } printf("Verification %s\n", rv ? "success" : "failure"); + if (check_crl) { + if (x509_cert_check_crl(cert, certlen, cacert, cacertlen, SM2_DEFAULT_ID, strlen(SM2_DEFAULT_ID)) < 0) { + fprintf(stderr, "%s: certificate has been revoked\n", prog); + goto end; + } + } if (double_certs) { x509_name_print(stdout, 0, 0, "Certificate", subj, subj_len); @@ -136,9 +162,19 @@ bad: } printf("Verification %s\n", rv ? "success" : "failure"); double_certs = 0; + + if (check_crl) { + if (x509_cert_check_crl(enc_cert, enc_cert_len, cacert, cacertlen, SM2_DEFAULT_ID, strlen(SM2_DEFAULT_ID)) < 0) { + fprintf(stderr, "%s: certificate has been revoked\n", prog); + goto end; + } + } + } x509_name_print(stdout, 0, 0, "Signed by", subject, subject_len); + check_crl = 0; // only check the entity CRL + memcpy(cert, cacert, cacertlen); certlen = cacertlen; } diff --git a/tools/crlget.c b/tools/crlget.c index 655f045a..97d160a9 100644 --- a/tools/crlget.c +++ b/tools/crlget.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -42,9 +43,8 @@ int crlget_main(int argc, char **argv) size_t certlen = 0; char *outfile = NULL; FILE *outfp = stdout; - - const uint8_t *exts; - size_t extslen; + uint8_t *crl = NULL; + size_t crl_len = 0; argc--; argv++; @@ -86,6 +86,9 @@ bad: goto end; } + /* + const uint8_t *exts; + size_t extslen; if (x509_cert_get_exts(cert, certlen, &exts, &extslen) != 1) { error_print(); goto end; @@ -120,8 +123,6 @@ bad: goto end; } - uint8_t *crl = NULL; - size_t crl_len = 0; if (http_get(uristr, NULL, &crl_len, 0) < 0) { error_print(); @@ -135,6 +136,13 @@ bad: error_print(); goto end; } + */ + + + if (x509_crl_new_from_cert(&crl, &crl_len, cert, certlen) != 1) { + error_print(); + goto end; + } fwrite(crl, crl_len, 1, outfp); @@ -145,3 +153,4 @@ end: if (outfile && outfp) fclose(outfp); return ret; } +