From 3173fb45f33c666e3dd43a968aaa80b445fe7fc7 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Fri, 19 Jun 2026 12:14:38 +0800 Subject: [PATCH] Add ocspget --- CMakeLists.txt | 3 +- include/gmssl/http.h | 3 + include/gmssl/ocsp.h | 3 + include/gmssl/version.h | 2 +- src/http.c | 91 ++++++++++++++ src/http_win.c | 99 +++++++++++++++ src/ocsp.c | 37 ++++++ tools/gmssl.c | 4 + tools/ocspget.c | 273 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 513 insertions(+), 2 deletions(-) create mode 100644 tools/ocspget.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fd519ef..95b8a1f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,7 @@ set(tools tools/certverify.c tools/certrevoke.c tools/ocspreq.c + tools/ocspget.c tools/ocspsign.c tools/ocspverify.c tools/reqgen.c @@ -820,7 +821,7 @@ endif() # set(CPACK_PACKAGE_NAME "GmSSL") set(CPACK_PACKAGE_VENDOR "GmSSL develop team") -set(CPACK_PACKAGE_VERSION "3.2.0-dev.1103") +set(CPACK_PACKAGE_VERSION "3.2.0-dev.1104") set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md) set(CPACK_NSIS_MODIFY_PATH ON) include(CPack) diff --git a/include/gmssl/http.h b/include/gmssl/http.h index cda8ad7b..6be1e671 100644 --- a/include/gmssl/http.h +++ b/include/gmssl/http.h @@ -21,6 +21,9 @@ extern "C" { int http_parse_uri(const char *uri, char host[128], int *port, char path[256]); int http_parse_response(char *buf, size_t buflen, uint8_t **content, size_t *contentlen, size_t *left); int http_get(const char *uri, uint8_t *buf, size_t *contentlen, size_t buflen); +int http_post(const char *uri, const char *content_type, + const uint8_t *req, size_t reqlen, + uint8_t *buf, size_t *contentlen, size_t buflen); #ifdef __cplusplus diff --git a/include/gmssl/ocsp.h b/include/gmssl/ocsp.h index 5baf41e5..cee8727b 100644 --- a/include/gmssl/ocsp.h +++ b/include/gmssl/ocsp.h @@ -316,6 +316,9 @@ int ocsp_verify(OCSP_SIGN_CTX *ctx, int ocsp_response_get_signer_cert(const uint8_t *resp, size_t resplen, const uint8_t *certs, size_t certs_len, const uint8_t **signer_cert, size_t *signer_cert_len); +int ocsp_response_get_from_uri(const char *uri, size_t uri_len, + const uint8_t *req, size_t req_len, + uint8_t *resp, size_t *resp_len, size_t max_resp_len); #ifdef __cplusplus diff --git a/include/gmssl/version.h b/include/gmssl/version.h index 81dec00a..163f2ac3 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.1103" +#define GMSSL_VERSION_STR "GmSSL 3.2.0-dev.1104" int gmssl_version_num(void); const char *gmssl_version_str(void); diff --git a/src/http.c b/src/http.c index aaa5422c..a20bd52b 100644 --- a/src/http.c +++ b/src/http.c @@ -107,6 +107,12 @@ int http_parse_response(char *buf, size_t buflen, uint8_t **content, size_t *con #define HTTP_GET_TEMPLATE "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "\r\n\r\n" +#define HTTP_POST_TEMPLATE \ + "POST %s HTTP/1.1\r\n" \ + "Host: %s\r\n" \ + "Content-Type: %s\r\n" \ + "Content-Length: %zu\r\n" \ + "\r\n" int http_get(const char *uri, uint8_t *buf, size_t *contentlen, size_t buflen) { @@ -183,3 +189,88 @@ end: if (tls_socket_is_valid(sock)) tls_socket_close(sock); return ret; } + +int http_post(const char *uri, const char *content_type, + const uint8_t *req, size_t reqlen, + uint8_t *buf, size_t *contentlen, size_t buflen) +{ + int ret = -1; + char host[128]; + int port; + char path[256]; + struct hostent *hp; + struct sockaddr_in server; + tls_socket_t sock = tls_socket_invalid(); + char post[1024]; + int postlen; + char response[1024]; + uint8_t *p; + tls_ret_t len; + size_t left; + + if (!content_type || !req || !reqlen || !contentlen) { + error_print(); + return -1; + } + if (http_parse_uri(uri, host, &port, path) != 1) { + error_print(); + return -1; + } + if ((postlen = snprintf(post, sizeof(post), HTTP_POST_TEMPLATE, + path, host, content_type, reqlen)) <= 0 + || postlen >= (int)sizeof(post)) { + error_print(); + return -1; + } + + if (!(hp = gethostbyname(host))) { + error_print(); + return -1; + } + server.sin_addr = *((struct in_addr *)hp->h_addr_list[0]); + server.sin_family = AF_INET; + server.sin_port = htons(port); + + if (tls_socket_create(&sock, AF_INET, SOCK_STREAM, 0) != 1) { + error_print(); + return -1; + } + if (tls_socket_connect(sock, &server) != 1) { + error_print(); + goto end; + } + if (tls_socket_send(sock, post, strlen(post), 0) != postlen) { + error_print(); + goto end; + } + if (tls_socket_send(sock, req, reqlen, 0) != (tls_ret_t)reqlen) { + error_print(); + goto end; + } + if ((len = tls_socket_recv(sock, response, sizeof(response) - 1, 0)) <= 0) { + error_print(); + goto end; + } + response[len] = 0; + + if (http_parse_response(response, (size_t)len, &p, contentlen, &left) != 1) { + error_print(); + goto end; + } + if (!buf || buflen < *contentlen) { + ret = 0; + goto end; + } + memcpy(buf, p, *contentlen - left); + if (left) { + if (socket_recv_all(sock, buf + *contentlen - left, left) != 1) { + error_print(); + goto end; + } + } + ret = 1; + +end: + if (tls_socket_is_valid(sock)) tls_socket_close(sock); + return ret; +} diff --git a/src/http_win.c b/src/http_win.c index 8d7f1711..ae3147fa 100644 --- a/src/http_win.c +++ b/src/http_win.c @@ -107,6 +107,12 @@ int http_parse_response(char *buf, size_t buflen, uint8_t **content, size_t *con #define HTTP_GET_TEMPLATE "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "\r\n\r\n" +#define HTTP_POST_TEMPLATE \ + "POST %s HTTP/1.1\r\n" \ + "Host: %s\r\n" \ + "Content-Type: %s\r\n" \ + "Content-Length: %zu\r\n" \ + "\r\n" int http_get(const char *uri, uint8_t *buf, size_t *contentlen, size_t buflen) { @@ -192,3 +198,96 @@ end: if (socket_lib_inited) tls_socket_lib_cleanup(); return ret; } + +int http_post(const char *uri, const char *content_type, + const uint8_t *req, size_t reqlen, + uint8_t *buf, size_t *contentlen, size_t buflen) +{ + int ret = -1; + char host[128]; + int port; + char path[256]; + struct hostent *hp; + struct sockaddr_in server; + tls_socket_t sock = tls_socket_invalid(); + char post[1024]; + int postlen; + char response[1024]; + uint8_t *p; + tls_ret_t len; + size_t left; + int socket_lib_inited = 0; + + if (!content_type || !req || !reqlen || !contentlen) { + error_print(); + return -1; + } + if (http_parse_uri(uri, host, &port, path) != 1) { + error_print(); + return -1; + } + if ((postlen = snprintf(post, sizeof(post), HTTP_POST_TEMPLATE, + path, host, content_type, reqlen)) <= 0 + || postlen >= (int)sizeof(post)) { + error_print(); + return -1; + } + + if (tls_socket_lib_init() != 1) { + error_print(); + return -1; + } + socket_lib_inited = 1; + + if (!(hp = gethostbyname(host))) { + error_print(); + goto end; + } + server.sin_addr = *((struct in_addr *)hp->h_addr_list[0]); + server.sin_family = AF_INET; + server.sin_port = htons(port); + + if (tls_socket_create(&sock, AF_INET, SOCK_STREAM, 0) != 1) { + error_print(); + goto end; + } + if (tls_socket_connect(sock, &server) != 1) { + error_print(); + goto end; + } + if (tls_socket_send(sock, post, strlen(post), 0) != postlen) { + error_print(); + goto end; + } + if (tls_socket_send(sock, req, reqlen, 0) != (tls_ret_t)reqlen) { + error_print(); + goto end; + } + if ((len = tls_socket_recv(sock, response, sizeof(response) - 1, 0)) <= 0) { + error_print(); + goto end; + } + response[len] = 0; + + if (http_parse_response(response, (size_t)len, &p, contentlen, &left) != 1) { + error_print(); + goto end; + } + if (!buf || buflen < *contentlen) { + ret = 0; + goto end; + } + memcpy(buf, p, *contentlen - left); + if (left) { + if (socket_recv_all(sock, buf + *contentlen - left, left) != 1) { + error_print(); + goto end; + } + } + ret = 1; + +end: + if (tls_socket_is_valid(sock)) tls_socket_close(sock); + if (socket_lib_inited) tls_socket_lib_cleanup(); + return ret; +} diff --git a/src/ocsp.c b/src/ocsp.c index 3b265492..3971503e 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -2669,3 +2670,39 @@ int ocsp_response_get_signer_cert(const uint8_t *resp, size_t resplen, } return 0; } + +int ocsp_response_get_from_uri(const char *uri, size_t uri_len, + const uint8_t *req, size_t req_len, + uint8_t *resp, size_t *resp_len, size_t max_resp_len) +{ + char uri_buf[2048]; + const uint8_t *p; + size_t len; + int response_status; + const uint8_t *basic_response; + size_t basic_response_len; + int ret; + + if (!uri || !uri_len || uri_len >= sizeof(uri_buf) + || !req || !req_len || !resp || !resp_len || !max_resp_len) { + error_print(); + return -1; + } + memcpy(uri_buf, uri, uri_len); + uri_buf[uri_len] = 0; + + if ((ret = http_post(uri_buf, "application/ocsp-request", + req, req_len, resp, resp_len, max_resp_len)) != 1) { + if (ret < 0) error_print(); + return ret; + } + p = resp; + len = *resp_len; + if (ocsp_response_from_der(&response_status, &basic_response, &basic_response_len, + &p, &len) != 1 + || asn1_length_is_zero(len) != 1) { + error_print(); + return -1; + } + return 1; +} diff --git a/tools/gmssl.c b/tools/gmssl.c index 400faece..fa4ce6d4 100644 --- a/tools/gmssl.c +++ b/tools/gmssl.c @@ -20,6 +20,7 @@ extern int certparse_main(int argc, char **argv); extern int certverify_main(int argc, char **argv); extern int certrevoke_main(int argc, char **argv); extern int ocspreq_main(int argc, char **argv); +extern int ocspget_main(int argc, char **argv); extern int ocspsign_main(int argc, char **argv); extern int ocspverify_main(int argc, char **argv); extern int crlget_main(int argc, char **argv); @@ -197,6 +198,7 @@ static const char *options = " certverify Verify certificate chain\n" " certrevoke Revoke certificate and output RevokedCertificate record\n" " ocspreq Generate OCSPRequest\n" + " ocspget Download OCSPResponse from OCSP responder\n" " ocspsign Sign OCSPResponse\n" " ocspverify Verify OCSPResponse\n" #ifdef ENABLE_CMS @@ -292,6 +294,8 @@ int main(int argc, char **argv) return certrevoke_main(argc, argv); } else if (!strcmp(*argv, "ocspreq")) { return ocspreq_main(argc, argv); + } else if (!strcmp(*argv, "ocspget")) { + return ocspget_main(argc, argv); } else if (!strcmp(*argv, "ocspsign")) { return ocspsign_main(argc, argv); } else if (!strcmp(*argv, "ocspverify")) { diff --git a/tools/ocspget.c b/tools/ocspget.c new file mode 100644 index 00000000..f3522f38 --- /dev/null +++ b/tools/ocspget.c @@ -0,0 +1,273 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define OCSP_RESPONSE_MAX_SIZE 131072 + +static const char *options = "-cert pem [-url str] [-digest name] [-out der] [-verbose]"; + +static const char *help = +"Options\n" +"\n" +" -cert pem Input certificate chain file in PEM format\n" +" The first certificate is the certificate to be checked\n" +" The second certificate is its issuer certificate\n" +" -url str OCSP responder URL, overrides the OCSP URI in certificate AIA\n" +" -digest name Digest algorithm for CertID, default sm3\n" +" -out der | stdout Output OCSPResponse in DER format\n" +" -verbose Print AuthorityInfoAccess, OCSPRequest and OCSPResponse to stderr\n" +"\n" +"Examples\n" +"\n" +" gmssl ocspget -cert chain.pem -out resp.der\n" +" gmssl ocspget -cert chain.pem -url http://ocsp.example.com -out resp.der -verbose\n" +"\n"; + +static int ocsp_request_der_print(FILE *fp, const uint8_t *req, size_t reqlen) +{ + const uint8_t *p = req; + size_t len = reqlen; + const uint8_t *d; + size_t dlen; + + if (asn1_sequence_from_der(&d, &dlen, &p, &len) != 1 + || asn1_length_is_zero(len) != 1 + || ocsp_request_print(fp, 0, 0, "OCSPRequest", d, dlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int ocsp_response_der_print(FILE *fp, const uint8_t *resp, size_t resplen) +{ + const uint8_t *p = resp; + size_t len = resplen; + const uint8_t *d; + size_t dlen; + + if (asn1_sequence_from_der(&d, &dlen, &p, &len) != 1 + || asn1_length_is_zero(len) != 1 + || ocsp_response_print(fp, 0, 0, "OCSPResponse", d, dlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int x509_cert_get_ocsp_uri(const uint8_t *cert, size_t certlen, + const char **uri, size_t *urilen, int verbose) +{ + int ret; + const uint8_t *exts; + size_t extslen; + int critical; + const uint8_t *val; + size_t vlen; + const char *ca_issuers_uri; + size_t ca_issuers_urilen; + const uint8_t *p; + size_t len; + + if (!cert || !certlen || !uri || !urilen) { + error_print(); + return -1; + } + *uri = NULL; + *urilen = 0; + + if ((ret = x509_cert_get_exts(cert, certlen, &exts, &extslen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (!exts || !extslen) { + return 0; + } + if ((ret = x509_exts_get_ext_by_oid(exts, extslen, OID_pe_authority_info_access, + &critical, &val, &vlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (verbose) { + p = val; + len = vlen; + if (x509_authority_info_access_print(stderr, 0, 0, + "AuthorityInfoAccess", p, len) != 1) { + error_print(); + return -1; + } + } + if (x509_authority_info_access_from_der( + &ca_issuers_uri, &ca_issuers_urilen, + uri, urilen, &val, &vlen) != 1 + || asn1_length_is_zero(vlen) != 1) { + error_print(); + return -1; + } + return *uri ? 1 : 0; +} + +int ocspget_main(int argc, char **argv) +{ + int ret = 1; + char *prog = argv[0]; + char *certfile = NULL; + char *outfile = NULL; + char *url = NULL; + char *digest_name_str = "sm3"; + FILE *certfp = NULL; + FILE *outfp = stdout; + const DIGEST *digest; + int verbose = 0; + const char *ocsp_uri = NULL; + size_t ocsp_uri_len = 0; + + uint8_t cert[OCSP_MAX_CERT_SIZE]; + size_t certlen = 0; + uint8_t issuer_cert[OCSP_MAX_CERT_SIZE]; + size_t issuer_certlen = 0; + uint8_t req[OCSP_MAX_REQUEST_SIZE]; + size_t reqlen = 0; + uint8_t resp[OCSP_RESPONSE_MAX_SIZE]; + size_t resplen = 0; + + argc--; + argv++; + + while (argc > 0) { + if (!strcmp(*argv, "-help")) { + printf("usage: %s %s\n", prog, options); + printf("%s\n", help); + ret = 0; + goto end; + } else if (!strcmp(*argv, "-cert")) { + if (--argc < 1) goto bad; + certfile = *(++argv); + if (!(certfp = fopen(certfile, "rb"))) { + fprintf(stderr, "%s: open '%s' failure : %s\n", prog, certfile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-url")) { + if (--argc < 1) goto bad; + url = *(++argv); + } else if (!strcmp(*argv, "-digest")) { + if (--argc < 1) goto bad; + digest_name_str = *(++argv); + } else if (!strcmp(*argv, "-out")) { + if (--argc < 1) goto bad; + outfile = *(++argv); + if (!(outfp = fopen(outfile, "wb"))) { + fprintf(stderr, "%s: open '%s' failure : %s\n", prog, outfile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-verbose")) { + verbose = 1; + } else { + fprintf(stderr, "%s: illegal option `%s`\n", prog, *argv); + goto end; +bad: + fprintf(stderr, "%s: `%s` option value missing\n", prog, *argv); + goto end; + } + + argc--; + argv++; + } + + if (!certfile) { + fprintf(stderr, "%s: `-cert` option required\n", prog); + goto end; + } + if (!(digest = digest_from_name(digest_name_str))) { + fprintf(stderr, "%s: invalid `-digest` value\n", prog); + goto end; + } + if (x509_cert_from_pem(cert, &certlen, sizeof(cert), certfp) != 1) { + fprintf(stderr, "%s: read certificate failure\n", prog); + goto end; + } + if (x509_cert_from_pem(issuer_cert, &issuer_certlen, sizeof(issuer_cert), certfp) != 1) { + fprintf(stderr, "%s: read issuer certificate failure\n", prog); + goto end; + } + if (url) { + if (verbose) { + const char *cert_ocsp_uri; + size_t cert_ocsp_uri_len; + if ((ret = x509_cert_get_ocsp_uri(cert, certlen, + &cert_ocsp_uri, &cert_ocsp_uri_len, verbose)) < 0) { + error_print(); + ret = 1; + goto end; + } + if (ret == 0) { + fprintf(stderr, "%s: no OCSP URI found in certificate\n", prog); + } + } + ocsp_uri = url; + ocsp_uri_len = strlen(url); + } else { + if ((ret = x509_cert_get_ocsp_uri(cert, certlen, + &ocsp_uri, &ocsp_uri_len, verbose)) != 1) { + if (ret < 0) error_print(); + else fprintf(stderr, "%s: no OCSP URI found in certificate\n", prog); + ret = 1; + goto end; + } + } + if (verbose) { + fprintf(stderr, "OCSP responder: %.*s\n", (int)ocsp_uri_len, ocsp_uri); + } + + if (ocsp_request_generate(req, &reqlen, sizeof(req), + cert, certlen, issuer_cert, issuer_certlen, digest) != 1) { + fprintf(stderr, "%s: generate OCSPRequest failure\n", prog); + goto end; + } + if (verbose && ocsp_request_der_print(stderr, req, reqlen) != 1) { + fprintf(stderr, "%s: print OCSPRequest failure\n", prog); + goto end; + } + + if ((ret = ocsp_response_get_from_uri(ocsp_uri, ocsp_uri_len, + req, reqlen, resp, &resplen, sizeof(resp))) != 1) { + if (ret < 0) error_print(); + else fprintf(stderr, "%s: OCSPResponse too large\n", prog); + ret = 1; + goto end; + } + if (verbose && ocsp_response_der_print(stderr, resp, resplen) != 1) { + fprintf(stderr, "%s: print OCSPResponse failure\n", prog); + goto end; + } + + if (fwrite(resp, 1, resplen, outfp) != resplen) { + fprintf(stderr, "%s: output failure : %s\n", prog, strerror(errno)); + goto end; + } + + ret = 0; + +end: + if (certfp) fclose(certfp); + if (outfile && outfp) fclose(outfp); + return ret; +}