mirror of
https://github.com/guanzhi/GmSSL.git
synced 2026-06-19 19:33:38 +08:00
Add ocspverify command
This commit is contained in:
@@ -150,6 +150,7 @@ set(tools
|
||||
tools/certrevoke.c
|
||||
tools/ocspreq.c
|
||||
tools/ocspsign.c
|
||||
tools/ocspverify.c
|
||||
tools/reqgen.c
|
||||
tools/reqparse.c
|
||||
tools/reqsign.c
|
||||
|
||||
@@ -21,6 +21,7 @@ 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 ocspsign_main(int argc, char **argv);
|
||||
extern int ocspverify_main(int argc, char **argv);
|
||||
extern int crlget_main(int argc, char **argv);
|
||||
extern int crlgen_main(int argc, char **argv);
|
||||
extern int crlparse_main(int argc, char **argv);
|
||||
@@ -196,6 +197,7 @@ static const char *options =
|
||||
" certrevoke Revoke certificate and output RevokedCertificate record\n"
|
||||
" ocspreq Generate OCSPRequest\n"
|
||||
" ocspsign Sign OCSPResponse\n"
|
||||
" ocspverify Verify OCSPResponse\n"
|
||||
#ifdef ENABLE_CMS
|
||||
" cmsparse Parse CMS (cryptographic message syntax) file\n"
|
||||
" cmsencrypt Generate CMS EnvelopedData\n"
|
||||
@@ -290,6 +292,8 @@ int main(int argc, char **argv)
|
||||
return ocspreq_main(argc, argv);
|
||||
} else if (!strcmp(*argv, "ocspsign")) {
|
||||
return ocspsign_main(argc, argv);
|
||||
} else if (!strcmp(*argv, "ocspverify")) {
|
||||
return ocspverify_main(argc, argv);
|
||||
} else if (!strcmp(*argv, "crlget")) {
|
||||
return crlget_main(argc, argv);
|
||||
} else if (!strcmp(*argv, "crlgen")) {
|
||||
|
||||
349
tools/ocspverify.c
Normal file
349
tools/ocspverify.c
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <gmssl/hex.h>
|
||||
#include <gmssl/asn1.h>
|
||||
#include <gmssl/x509.h>
|
||||
#include <gmssl/ocsp.h>
|
||||
#include <gmssl/error.h>
|
||||
|
||||
|
||||
#define OCSP_RESPONSE_MAX_SIZE 131072
|
||||
|
||||
static const char *options =
|
||||
"-reqin der -respin der -cacert pem -signer pem"
|
||||
" [-time time] [-clock_skew seconds]"
|
||||
" [-sm2_id str | -sm2_id_hex hex]"
|
||||
" [-certs pem] [-verbose]";
|
||||
|
||||
static const char *help =
|
||||
"Options\n"
|
||||
"\n"
|
||||
" -reqin der Input OCSPRequest in DER format\n"
|
||||
" -respin der Input OCSPResponse in DER format\n"
|
||||
" -cacert pem Issuer CA certificate of the requested certificate\n"
|
||||
" -signer pem OCSPResponse signer certificate\n"
|
||||
" -time time Verification time, default current time\n"
|
||||
" -clock_skew seconds Allowed clock skew in seconds, default 300\n"
|
||||
" -sm2_id str Signer's ID in SM2 signature algorithm\n"
|
||||
" -sm2_id_hex hex Signer's ID in hex format\n"
|
||||
" -certs pem Extra certificates for verification context\n"
|
||||
" -verbose Print OCSPRequest and OCSPResponse to stderr\n"
|
||||
"\n"
|
||||
"Examples\n"
|
||||
"\n"
|
||||
" gmssl ocspverify -reqin req.der -respin resp.der -cacert cacert.pem -signer ocspcert.pem -verbose\n"
|
||||
"\n";
|
||||
|
||||
static const char *ocsp_verify_reason_name(int reason)
|
||||
{
|
||||
switch (reason) {
|
||||
case OCSP_VERIFY_REASON_NONE:
|
||||
return "none";
|
||||
case OCSP_VERIFY_REASON_REVOKED:
|
||||
return "revoked";
|
||||
case OCSP_VERIFY_REASON_UNKNOWN:
|
||||
return "unknown";
|
||||
case OCSP_VERIFY_REASON_MALFORMED_RESPONSE:
|
||||
return "malformedResponse";
|
||||
case OCSP_VERIFY_REASON_RESPONSE_STATUS_NOT_SUCCESSFUL:
|
||||
return "responseStatusNotSuccessful";
|
||||
case OCSP_VERIFY_REASON_UNSUPPORTED_RESPONSE_TYPE:
|
||||
return "unsupportedResponseType";
|
||||
case OCSP_VERIFY_REASON_BAD_SIGNATURE:
|
||||
return "badSignature";
|
||||
case OCSP_VERIFY_REASON_BAD_RESPONDER_ID:
|
||||
return "badResponderID";
|
||||
case OCSP_VERIFY_REASON_NO_MATCHING_SINGLE_RESPONSE:
|
||||
return "noMatchingSingleResponse";
|
||||
case OCSP_VERIFY_REASON_THIS_UPDATE_IN_FUTURE:
|
||||
return "thisUpdateInFuture";
|
||||
case OCSP_VERIFY_REASON_NEXT_UPDATE_EXPIRED:
|
||||
return "nextUpdateExpired";
|
||||
default:
|
||||
return "unknownReason";
|
||||
}
|
||||
}
|
||||
|
||||
static int read_der_file(const char *file, uint8_t *buf, size_t *buflen, size_t maxlen)
|
||||
{
|
||||
FILE *fp;
|
||||
size_t len;
|
||||
|
||||
if (!(fp = fopen(file, "rb"))) {
|
||||
return -1;
|
||||
}
|
||||
len = fread(buf, 1, maxlen, fp);
|
||||
if (ferror(fp) || (len == maxlen && fgetc(fp) != EOF)) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
fclose(fp);
|
||||
*buflen = len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int read_certs_from_pem(const char *file, uint8_t *certs, size_t *certs_len, size_t maxlen)
|
||||
{
|
||||
FILE *fp;
|
||||
uint8_t cert[OCSP_MAX_CERT_SIZE];
|
||||
size_t certlen;
|
||||
size_t len = 0;
|
||||
int ret;
|
||||
|
||||
if (!(fp = fopen(file, "rb"))) {
|
||||
return -1;
|
||||
}
|
||||
while ((ret = x509_cert_from_pem(cert, &certlen, sizeof(cert), fp)) == 1) {
|
||||
if (certlen > maxlen - len) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
memcpy(certs + len, cert, certlen);
|
||||
len += certlen;
|
||||
}
|
||||
fclose(fp);
|
||||
*certs_len = len;
|
||||
return len ? 1 : -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int ocspverify_main(int argc, char **argv)
|
||||
{
|
||||
int ret = 1;
|
||||
char *prog = argv[0];
|
||||
char *reqfile = NULL;
|
||||
char *respfile = NULL;
|
||||
char *cacertfile = NULL;
|
||||
char *signerfile = NULL;
|
||||
char *certsfile = NULL;
|
||||
FILE *cacertfp = NULL;
|
||||
FILE *signerfp = NULL;
|
||||
char *str;
|
||||
int verbose = 0;
|
||||
|
||||
uint8_t req[OCSP_MAX_REQUEST_SIZE];
|
||||
size_t reqlen = 0;
|
||||
uint8_t resp[OCSP_RESPONSE_MAX_SIZE];
|
||||
size_t resplen = 0;
|
||||
uint8_t cacert[OCSP_MAX_CERT_SIZE];
|
||||
size_t cacertlen = 0;
|
||||
uint8_t signer_cert[OCSP_MAX_CERT_SIZE];
|
||||
size_t signer_cert_len = 0;
|
||||
uint8_t certs[OCSP_MAX_CERTS_SIZE];
|
||||
size_t certs_len = 0;
|
||||
|
||||
OCSP_SIGN_CTX ocsp_ctx;
|
||||
char signer_id[SM2_MAX_ID_LENGTH + 1] = {0};
|
||||
size_t signer_id_len = 0;
|
||||
time_t verify_time = (time_t)-1;
|
||||
int clock_skew = -1;
|
||||
int reason = OCSP_VERIFY_REASON_NONE;
|
||||
int rv;
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
while (argc > 0) {
|
||||
if (!strcmp(*argv, "-help")) {
|
||||
printf("usage: %s %s\n\n", prog, options);
|
||||
printf("%s\n", help);
|
||||
ret = 0;
|
||||
goto end;
|
||||
} else if (!strcmp(*argv, "-reqin")) {
|
||||
if (--argc < 1) goto bad;
|
||||
reqfile = *(++argv);
|
||||
} else if (!strcmp(*argv, "-respin")) {
|
||||
if (--argc < 1) goto bad;
|
||||
respfile = *(++argv);
|
||||
} else if (!strcmp(*argv, "-cacert")) {
|
||||
if (--argc < 1) goto bad;
|
||||
cacertfile = *(++argv);
|
||||
} else if (!strcmp(*argv, "-signer")) {
|
||||
if (--argc < 1) goto bad;
|
||||
signerfile = *(++argv);
|
||||
} else if (!strcmp(*argv, "-time")) {
|
||||
if (--argc < 1) goto bad;
|
||||
str = *(++argv);
|
||||
if (asn1_time_from_str(0, &verify_time, str) != 1) {
|
||||
fprintf(stderr, "%s: invalid time '%s' for `-time`\n", prog, str);
|
||||
goto end;
|
||||
}
|
||||
} else if (!strcmp(*argv, "-clock_skew")) {
|
||||
if (--argc < 1) goto bad;
|
||||
str = *(++argv);
|
||||
clock_skew = atoi(str);
|
||||
if (clock_skew < 0) {
|
||||
fprintf(stderr, "%s: invalid `-clock_skew` value\n", prog);
|
||||
goto end;
|
||||
}
|
||||
} else if (!strcmp(*argv, "-sm2_id")) {
|
||||
if (--argc < 1) goto bad;
|
||||
str = *(++argv);
|
||||
if (strlen(str) > sizeof(signer_id) - 1) {
|
||||
fprintf(stderr, "%s: invalid `-sm2_id` length\n", prog);
|
||||
goto end;
|
||||
}
|
||||
strncpy(signer_id, str, sizeof(signer_id));
|
||||
signer_id_len = strlen(str);
|
||||
} else if (!strcmp(*argv, "-sm2_id_hex")) {
|
||||
if (--argc < 1) goto bad;
|
||||
str = *(++argv);
|
||||
if (strlen(str) > (sizeof(signer_id) - 1) * 2
|
||||
|| hex_to_bytes(str, strlen(str), (uint8_t *)signer_id, &signer_id_len) != 1) {
|
||||
fprintf(stderr, "%s: invalid `-sm2_id_hex` value\n", prog);
|
||||
goto end;
|
||||
}
|
||||
} else if (!strcmp(*argv, "-certs")) {
|
||||
if (--argc < 1) goto bad;
|
||||
certsfile = *(++argv);
|
||||
} 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 (!reqfile) {
|
||||
fprintf(stderr, "%s: `-reqin` option required\n", prog);
|
||||
goto end;
|
||||
}
|
||||
if (!respfile) {
|
||||
fprintf(stderr, "%s: `-respin` option required\n", prog);
|
||||
goto end;
|
||||
}
|
||||
if (!cacertfile) {
|
||||
fprintf(stderr, "%s: `-cacert` option required\n", prog);
|
||||
goto end;
|
||||
}
|
||||
if (!signerfile) {
|
||||
fprintf(stderr, "%s: `-signer` option required\n", prog);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (read_der_file(reqfile, req, &reqlen, sizeof(req)) != 1) {
|
||||
fprintf(stderr, "%s: read OCSPRequest '%s' failure\n", prog, reqfile);
|
||||
goto end;
|
||||
}
|
||||
if (read_der_file(respfile, resp, &resplen, sizeof(resp)) != 1) {
|
||||
fprintf(stderr, "%s: read OCSPResponse '%s' failure\n", prog, respfile);
|
||||
goto end;
|
||||
}
|
||||
if (verbose && ocsp_request_der_print(stderr, req, reqlen) != 1) {
|
||||
fprintf(stderr, "%s: print OCSPRequest failure\n", prog);
|
||||
goto end;
|
||||
}
|
||||
if (verbose && ocsp_response_der_print(stderr, resp, resplen) != 1) {
|
||||
fprintf(stderr, "%s: print OCSPResponse failure\n", prog);
|
||||
goto end;
|
||||
}
|
||||
if (!(cacertfp = fopen(cacertfile, "rb"))) {
|
||||
fprintf(stderr, "%s: open '%s' failure : %s\n", prog, cacertfile, strerror(errno));
|
||||
goto end;
|
||||
}
|
||||
if (x509_cert_from_pem(cacert, &cacertlen, sizeof(cacert), cacertfp) != 1) {
|
||||
fprintf(stderr, "%s: read CA certificate '%s' failure\n", prog, cacertfile);
|
||||
goto end;
|
||||
}
|
||||
if (!(signerfp = fopen(signerfile, "rb"))) {
|
||||
fprintf(stderr, "%s: open '%s' failure : %s\n", prog, signerfile, strerror(errno));
|
||||
goto end;
|
||||
}
|
||||
if (x509_cert_from_pem(signer_cert, &signer_cert_len, sizeof(signer_cert), signerfp) != 1) {
|
||||
fprintf(stderr, "%s: read signer certificate '%s' failure\n", prog, signerfile);
|
||||
goto end;
|
||||
}
|
||||
if (certsfile && read_certs_from_pem(certsfile, certs, &certs_len, sizeof(certs)) != 1) {
|
||||
fprintf(stderr, "%s: read certificates '%s' failure\n", prog, certsfile);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (ocsp_verify_init(&ocsp_ctx, req, reqlen, cacert, cacertlen) != 1) {
|
||||
fprintf(stderr, "%s: initialize OCSP verification context failure\n", prog);
|
||||
goto end;
|
||||
}
|
||||
if (verify_time != (time_t)-1
|
||||
&& ocsp_verify_set_time(&ocsp_ctx, verify_time) != 1) {
|
||||
fprintf(stderr, "%s: set verification time failure\n", prog);
|
||||
goto end;
|
||||
}
|
||||
if (clock_skew >= 0
|
||||
&& ocsp_verify_set_clock_skew(&ocsp_ctx, clock_skew) != 1) {
|
||||
fprintf(stderr, "%s: set clock skew failure\n", prog);
|
||||
goto end;
|
||||
}
|
||||
if (certs_len
|
||||
&& ocsp_verify_set_certs(&ocsp_ctx, certs, certs_len) != 1) {
|
||||
fprintf(stderr, "%s: set verification certificates failure\n", prog);
|
||||
goto end;
|
||||
}
|
||||
|
||||
rv = ocsp_verify(&ocsp_ctx, resp, resplen,
|
||||
signer_cert, signer_cert_len,
|
||||
signer_id_len ? signer_id : NULL, signer_id_len,
|
||||
&reason);
|
||||
if (rv == 1) {
|
||||
printf("Verification success\n");
|
||||
ret = 0;
|
||||
} else if (rv == 0) {
|
||||
printf("Verification failure: %s\n", ocsp_verify_reason_name(reason));
|
||||
ret = 1;
|
||||
} else {
|
||||
fprintf(stderr, "%s: Verification error: %s\n", prog, ocsp_verify_reason_name(reason));
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
if (cacertfp) fclose(cacertfp);
|
||||
if (signerfp) fclose(signerfp);
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user