1
0
mirror of https://github.com/mtrojnar/osslsigncode.git synced 2025-04-11 03:28:04 -05:00

Merge pull request from olszomal/crl

Fix timestamp and CRL verification
This commit is contained in:
Michał Trojnara 2020-08-02 20:26:38 +02:00 committed by GitHub
commit 01b3fb5b54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 215 additions and 75 deletions

@ -239,6 +239,7 @@ typedef struct {
#endif /* ENABLE_CURL */
int addBlob;
int nest;
int timestamp_expiration;
int verbose;
#ifdef WITH_GSF
int add_msi_dse;
@ -246,6 +247,7 @@ typedef struct {
char *cafile;
char *crlfile;
char *untrusted;
char *crluntrusted;
char *leafhash;
int jp;
} GLOBAL_OPTIONS;
@ -1026,6 +1028,7 @@ static void usage(const char *argv0, const char *cmd)
printf("%12s[ -CAfile <infile> ]\n", "");
printf("%12s[ -CRLfile <infile> ]\n", "");
printf("%12s[ -untrusted <infile> ]\n", "");
printf("%12s[ -CRLuntrusted <infile> ]\n", "");
printf("%12s[ -nest ]\n", "");
printf("%12s[ -in ] <infile> [ -out ] <outfile>\n\n", "");
}
@ -1040,7 +1043,9 @@ static void usage(const char *argv0, const char *cmd)
printf("%12s[ -CAfile <infile> ]\n", "");
printf("%12s[ -CRLfile <infile> ]\n", "");
printf("%12s[ -untrusted <infile> ]\n", "");
printf("%12s[ -CRLuntrusted <infile> ]\n", "");
printf("%12s[ -require-leaf-hash {md5,sha1,sha2(56),sha384,sha512}:XXXXXXXXXXXX... ]\n", "");
printf("%12s[ -timestamp-expiration ]\n", "");
printf("%12s[ -verbose ]\n\n", "");
}
cleanup_lib_state();
@ -1068,6 +1073,7 @@ static void help_for(const char *argv0, const char *cmd)
const char *cmds_certs[] = {"sign", NULL};
const char *cmds_comm[] = {"sign", NULL};
const char *cmds_CRLfile[] = {"attach-signature", "verify", NULL};
const char *cmds_CRLuntrusted[] = {"attach-signature", "verify", NULL};
const char *cmds_h[] = {"sign", NULL};
const char *cmds_i[] = {"sign", NULL};
const char *cmds_in[] = {"add", "attach-signature", "extract-signature", "remove-signature", "sign", "verify", NULL};
@ -1092,6 +1098,7 @@ static void help_for(const char *argv0, const char *cmd)
const char *cmds_require_leaf_hash[] = {"verify", NULL};
const char *cmds_sigin[] = {"attach-signature", NULL};
const char *cmds_st[] = {"sign", NULL};
const char *cmds_timestamp_expiration[] = {"verify", NULL};
#ifdef ENABLE_CURL
const char *cmds_t[] = {"add", "sign", NULL};
const char *cmds_ts[] = {"add", "sign", NULL};
@ -1170,6 +1177,8 @@ static void help_for(const char *argv0, const char *cmd)
printf("%-24s= set commercial purpose (default: individual purpose)\n", "-comm");
if (on_list(cmd, cmds_CRLfile))
printf("%-24s= the file containing one or more CRLs in PEM format\n", "-CRLfile");
if (on_list(cmd, cmds_CRLuntrusted))
printf("%-24s= the file containing one or more additional untrusted CRLs in PEM format\n", "-CRLuntrusted");
if (on_list(cmd, cmds_h)) {
printf("%-24s= {md5|sha1|sha2(56)|sha384|sha512}\n", "-h");
printf("%26sset of cryptographic hash functions\n", "");
@ -1223,6 +1232,8 @@ static void help_for(const char *argv0, const char *cmd)
printf("%-24s= a file containing the signature to be attached\n", "-sigin");
if (on_list(cmd, cmds_st))
printf("%-24s= the unix-time to set the signing time\n", "-st");
if (on_list(cmd, cmds_timestamp_expiration))
printf("%-24s= verify a finite lifetime of the TSA private key\n", "-st");
#ifdef ENABLE_CURL
if (on_list(cmd, cmds_t)) {
printf("%-24s= specifies that the digital signature will be timestamped\n", "-t");
@ -1741,10 +1752,10 @@ static int print_cert(X509 *cert, int i)
return 1; /* OK */
}
static int find_signer(PKCS7 *p7, char *leafhash, int *leafok)
static X509 *find_signer(PKCS7 *p7, char *leafhash, int *leafok)
{
STACK_OF(X509) *signers;
X509 *cert;
X509 *cert = NULL;
int ret = 0;
/*
@ -1768,7 +1779,7 @@ out:
if (!ret)
ERR_print_errors_fp(stderr);
sk_X509_free(signers);
return ret;
return cert;
}
static int print_certs(PKCS7 *p7)
@ -1835,31 +1846,26 @@ static time_t cms_get_time(CMS_ContentInfo *cms)
return posix_time;
}
static int load_crlfile_lookup(X509_STORE *store, char *crl)
static int verify_callback(int ok, X509_STORE_CTX *ctx)
{
X509_LOOKUP *lookup;
X509_VERIFY_PARAM *param;
int error = X509_STORE_CTX_get_error(ctx);
int depth = X509_STORE_CTX_get_error_depth(ctx);
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (!lookup)
return 0; /* FAILED */
if (!X509_load_crl_file(lookup, crl, X509_FILETYPE_PEM)) {
fprintf(stderr, "\nError: no CRL found in %s\n", crl);
return 0; /* FAILED */
if (!ok && error == X509_V_ERR_CERT_HAS_EXPIRED) {
if (depth == 0) {
printf("\nWarning: Ignoring expired signer certificate for CRL validation\n");
return 1;
} else {
X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx);
printf("\nErrror: Expired CA certificate:\n");
print_cert(current_cert, 0);
printf("\n");
}
}
param = X509_STORE_get0_param(store);
if (param == NULL)
return 0; /* FAILED */
if (!X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK))
return 0; /* FAILED */
if (!X509_VERIFY_PARAM_set_purpose(param, X509_PURPOSE_CRL_SIGN))
return 0; /* FAILED */
if (!X509_STORE_set1_param(store, param))
return 0; /* FAILED */
return 1; /* OK */
return ok;
}
static int load_file_lookup(X509_STORE *store, char *certs, int purpose)
static int load_crlfile_lookup(X509_STORE *store, char *certs, char *crl)
{
X509_LOOKUP *lookup;
X509_VERIFY_PARAM *param;
@ -1871,13 +1877,45 @@ static int load_file_lookup(X509_STORE *store, char *certs, int purpose)
fprintf(stderr, "\nError: no certificate found\n");
return 0; /* FAILED */
}
if (!X509_load_crl_file(lookup, crl, X509_FILETYPE_PEM)) {
fprintf(stderr, "\nError: no CRL found in %s\n", crl);
return 0; /* FAILED */
}
param = X509_STORE_get0_param(store);
if (param == NULL)
return 0; /* FAILED */
if (!X509_VERIFY_PARAM_set_purpose(param, purpose))
/* enable CRL checking for the certificate chain leaf certificate */
if (!X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK))
return 0; /* FAILED */
if (!X509_STORE_set1_param(store, param))
return 0; /* FAILED */
X509_STORE_set_verify_cb(store, verify_callback);
return 1; /* OK */
}
static int load_file_lookup(X509_STORE *store, char *certs)
{
X509_LOOKUP *lookup;
X509_VERIFY_PARAM *param;
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (!lookup)
return 0; /* FAILED */
if (!X509_load_cert_file(lookup, certs, X509_FILETYPE_PEM)) {
fprintf(stderr, "\nError: no certificate found\n");
return 0; /* FAILED */
}
param = X509_STORE_get0_param(store);
if (param == NULL)
return 0; /* FAILED */
if (!X509_VERIFY_PARAM_set_purpose(param, X509_PURPOSE_ANY))
return 0; /* FAILED */
if (!X509_STORE_set1_param(store, param))
return 0; /* FAILED */
return 1; /* OK */
}
@ -1885,15 +1923,12 @@ static int set_store_time(X509_STORE *store, time_t time)
{
X509_VERIFY_PARAM *param;
param = X509_VERIFY_PARAM_new();
param = X509_STORE_get0_param(store);
if (param == NULL)
return 0; /* FAILED */
X509_VERIFY_PARAM_set_time(param, time);
if (!X509_STORE_set1_param(store, param)) {
X509_VERIFY_PARAM_free(param);
if (!X509_STORE_set1_param(store, param))
return 0; /* FAILED */
}
X509_VERIFY_PARAM_free(param);
return 1; /* OK */
}
@ -2227,40 +2262,141 @@ static int pkcs7_set_nested_signature(PKCS7 *p7, PKCS7 *p7nest, time_t signing_t
return 1;
}
static const char *get_clrdp_url(X509 *cert)
{
STACK_OF(DIST_POINT) *crldp;
DIST_POINT *dp;
GENERAL_NAMES *gens;
GENERAL_NAME *gen;
int i, j, gtype;
ASN1_STRING *uri;
const char *url;
crldp = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL);
if (!crldp)
return NULL;
for (i = 0; i < sk_DIST_POINT_num(crldp); i++) {
dp = sk_DIST_POINT_value(crldp, i);
if (!dp->distpoint || dp->distpoint->type != 0)
return NULL;
gens = dp->distpoint->name.fullname;
for (j = 0; j < sk_GENERAL_NAME_num(gens); j++) {
gen = sk_GENERAL_NAME_value(gens, j);
uri = GENERAL_NAME_get0_value(gen, &gtype);
if (gtype == GEN_URI && ASN1_STRING_length(uri) > 6) {
url = (const char *)ASN1_STRING_get0_data(uri);
if (strncmp(url, "http://", 7) == 0)
return url;
}
}
}
return NULL;
}
static int verify_crl(char *ca_file, char *crl_file, X509 *signer, STACK_OF(X509) *chain)
{
X509_STORE *store = NULL;
X509_STORE_CTX *ctx = NULL;
int verok = 0;
ctx = X509_STORE_CTX_new();
if (!ctx)
goto out;
store = X509_STORE_new();
if (!store)
goto out;
if (!load_crlfile_lookup(store, ca_file, crl_file))
goto out;
/* initialise an X509_STORE_CTX structure for subsequent use by X509_verify_cert()*/
if (!X509_STORE_CTX_init(ctx, store, signer, chain))
goto out;
if (X509_verify_cert(ctx) <= 0) {
int error = X509_STORE_CTX_get_error(ctx);
printf("\nX509_verify_cert: certificate verify error: %s\n",
X509_verify_cert_error_string(error));
goto out;
}
verok = 1; /* OK */
out:
if (!verok)
ERR_print_errors_fp(stderr);
/* NULL is a valid parameter value for X509_STORE_free() and X509_STORE_CTX_free() */
X509_STORE_free(store);
X509_STORE_CTX_free(ctx);
return verok;
}
static int verify_timestamp(SIGNATURE *signature, GLOBAL_OPTIONS *options)
{
X509_STORE *store;
STACK_OF(X509) *signers;
STACK_OF(CMS_SignerInfo) *sinfos;
CMS_SignerInfo *cmssi;
X509 *signer;
const char *url;
PKCS7_SIGNER_INFO *si;
int verok = 0;
store = X509_STORE_new();
if (!store)
goto out;
if (load_file_lookup(store, options->untrusted, X509_PURPOSE_ANY))
printf("TSA's certificates file: %s\n", options->untrusted);
else {
if (load_file_lookup(store, options->untrusted)) {
/*
* The TSA signing key MUST be of a sufficient length to allow for a sufficiently
* long lifetime. Even if this is done, the key will have a finite lifetime.
* Thus, any token signed by the TSA SHOULD be time-stamped again or notarized
* at a later date to renew the trust that exists in the TSA's signature.
* https://tools.ietf.org/html/rfc3161
*/
if (!options->timestamp_expiration)
/* verify timestamp against the time of its creation */
if (!set_store_time(store, signature->time)) {
fprintf(stderr, "Failed to set store time\n");
X509_STORE_free(store);
goto out;
}
} else {
printf("Use the \"-untrusted\" option to add the CA cert bundle to verify timestamp server.\n");
X509_STORE_free(store);
goto out;
}
/* verify a CMS SignedData structure */
if (!CMS_verify(signature->timestamp, NULL, store, 0, NULL, 0)) {
fprintf(stderr, "\nCMS_verify error\n");
X509_STORE_free(store);
goto out;
}
X509_STORE_free(store);
sinfos = CMS_get0_SignerInfos(signature->timestamp);
cmssi = sk_CMS_SignerInfo_value(sinfos, 0);
CMS_SignerInfo_get0_algs(cmssi, NULL, &signer, NULL, NULL);
url = get_clrdp_url(signer);
if (url)
printf("TSA's CRL distribution point: %s\n", url);
printf("\n");
/* verify a Certificate Revocation List */
if (options->crluntrusted) {
STACK_OF(X509) *chain = CMS_get1_certs(signature->timestamp);
int crlok = verify_crl(options->untrusted, options->crluntrusted, signer, chain);
sk_X509_pop_free(chain, X509_free);
printf("Timestamp Server Signature CRL verification: %s\n", crlok ? "ok" : "failed");
if (!crlok)
goto out;
} else
printf("\n");
/* check extended key usage flag XKU_TIMESTAMP */
signers = CMS_get0_signers(signature->timestamp);
if (!signers) {
fprintf(stderr, "\nCMS_get0_signers error\n");
goto out;
}
if (!(X509_get_extended_key_usage(sk_X509_value(signers, 0)) & XKU_TIMESTAMP)) {
if (!(X509_get_extended_key_usage(signer) & XKU_TIMESTAMP)) {
fprintf(stderr, "Unsupported Signer's certificate purpose XKU_TIMESTAMP\n");
goto out;
}
sk_X509_free(signers);
/* verify the hash provided from the trusted timestamp */
si = sk_PKCS7_SIGNER_INFO_value(signature->p7->d.sign->signer_info, 0);
@ -2271,28 +2407,27 @@ static int verify_timestamp(SIGNATURE *signature, GLOBAL_OPTIONS *options)
out:
if (!verok)
ERR_print_errors_fp(stderr);
/* NULL is a valid parameter value for X509_STORE_free() */
X509_STORE_free(store);
return verok;
}
static int verify_authenticode(SIGNATURE *signature, GLOBAL_OPTIONS *options)
static int verify_authenticode(SIGNATURE *signature, GLOBAL_OPTIONS *options, X509 *signer)
{
X509_STORE *store = NULL;
X509_STORE *store;
size_t seqhdrlen;
BIO *bio = NULL;
STACK_OF(X509) *signers;
int verok = 0;
store = X509_STORE_new();
if (!store)
goto out;
if (!load_file_lookup(store, options->cafile, X509_PURPOSE_ANY)) {
if (!load_file_lookup(store, options->cafile)) {
fprintf(stderr, "Failed to add store lookup file\n");
X509_STORE_free(store);
goto out;
}
if (signature->time != INVALID_TIME && !set_store_time(store, signature->time)) {
fprintf(stderr, "Failed to set store time\n");
X509_STORE_free(store);
goto out;
}
@ -2303,56 +2438,46 @@ static int verify_authenticode(SIGNATURE *signature, GLOBAL_OPTIONS *options)
signature->p7->d.sign->contents->d.other->value.sequence->length - seqhdrlen);
if (!PKCS7_verify(signature->p7, NULL, store, bio, NULL, 0)) {
fprintf(stderr, "\nPKCS7_verify error\n");
X509_STORE_free(store);
BIO_free(bio);
goto out;
}
X509_STORE_free(store);
BIO_free(bio);
/* verify a Certificate Revocation List */
if (options->crlfile) {
if (!load_crlfile_lookup(store, options->crlfile)) {
fprintf(stderr, "Failed to add CRL store lookup file\nCRL verification: failed\n");
BIO_free(bio);
STACK_OF(X509) *chain = signature->p7->d.sign->cert;
int crlok = verify_crl(options->cafile, options->crlfile, signer, chain);
printf("Signature CRL verification: %s\n", crlok ? "ok" : "failed");
if (!crlok)
goto out;
}
if (PKCS7_verify(signature->p7, NULL, store, bio, NULL, 0))
printf("CRL verification: ok\n");
else {
printf("CRL verification: failed\n");
BIO_free(bio);
goto out;
}
}
BIO_free(bio);
/* check extended key usage flag XKU_CODE_SIGN */
signers = PKCS7_get0_signers(signature->p7, NULL, 0);
if (!signers || sk_X509_num(signers) != 1) {
fprintf(stderr, "PKCS7_get0_signers error\n");
sk_X509_free(signers);
goto out;
}
if (!(X509_get_extended_key_usage(sk_X509_value(signers, 0)) & XKU_CODE_SIGN)) {
if (!(X509_get_extended_key_usage(signer) & XKU_CODE_SIGN)) {
fprintf(stderr, "Unsupported Signer's certificate purpose XKU_CODE_SIGN\n");
sk_X509_free(signers);
goto out;
}
sk_X509_free(signers);
verok = 1; /* OK */
out:
if (!verok)
ERR_print_errors_fp(stderr);
/* NULL is a valid parameter value for X509_STORE_free() */
X509_STORE_free(store);
return verok;
}
static int verify_signature(SIGNATURE *signature, GLOBAL_OPTIONS *options)
{
int leafok = 0, verok;
X509 *signer;
const char *url;
if (!find_signer(signature->p7, options->leafhash, &leafok))
printf("Find signers error\n");
signer = find_signer(signature->p7, options->leafhash, &leafok);
if (!signer) {
printf("Find signer error\n");
return 1; /* FAILED */
}
if (!print_certs(signature->p7))
printf("Print certs error\n");
if (!print_attributes(signature, options->verbose))
@ -2367,15 +2492,23 @@ static int verify_signature(SIGNATURE *signature, GLOBAL_OPTIONS *options)
printf("\nCAfile: %s\n", options->cafile);
if (options->crlfile)
printf("CRLfile: %s\n", options->crlfile);
if (options->untrusted)
printf("TSA's certificates file: %s\n", options->untrusted);
if (options->crluntrusted)
printf("TSA's CRL file: %s\n", options->crluntrusted);
url = get_clrdp_url(signer);
if (url)
printf("CRL distribution point: %s\n", url);
if (signature->timestamp) {
int timeok = verify_timestamp(signature, options);
printf("\nTimestamp Server Signature verification: %s\n", timeok ? "ok" : "failed");
printf("Timestamp Server Signature verification: %s\n", timeok ? "ok" : "failed");
if (!timeok) {
signature->time = INVALID_TIME;
}
} else
printf("\nTimestamp is not available\n\n");
verok = verify_authenticode(signature, options);
verok = verify_authenticode(signature, options, signer);
printf("Signature verification: %s\n\n", verok ? "ok" : "failed");
if (!verok)
return 1; /* FAILED */
@ -4674,6 +4807,8 @@ static void free_options(GLOBAL_OPTIONS *options)
OPENSSL_free(options->untrusted);
if (options->crlfile)
OPENSSL_free(options->crlfile);
if (options->crluntrusted)
OPENSSL_free(options->crluntrusted);
if (options->pass)
OPENSSL_free(options->pass);
}
@ -5047,6 +5182,8 @@ static int main_configure(int argc, char **argv, cmd_type_t *cmd, GLOBAL_OPTIONS
options->addBlob = 1;
} else if ((*cmd == CMD_SIGN || *cmd == CMD_ATTACH) && !strcmp(*argv, "-nest")) {
options->nest = 1;
} else if ((*cmd == CMD_VERIFY) && !strcmp(*argv, "-timestamp-expiration")) {
options->timestamp_expiration = 1;
} else if ((*cmd == CMD_SIGN || *cmd == CMD_ADD || *cmd == CMD_VERIFY) && !strcmp(*argv, "-verbose")) {
options->verbose = 1;
#ifdef WITH_GSF
@ -5060,6 +5197,9 @@ static int main_configure(int argc, char **argv, cmd_type_t *cmd, GLOBAL_OPTIONS
} else if ((*cmd == CMD_VERIFY || *cmd == CMD_ATTACH) && !strcmp(*argv, "-CRLfile")) {
if (--argc < 1) usage(argv0, "all");
options->crlfile = OPENSSL_strdup(*++argv);
} else if ((*cmd == CMD_VERIFY || *cmd == CMD_ATTACH) && !strcmp(*argv, "-CRLuntrusted")) {
if (--argc < 1) usage(argv0, "all");
options->crluntrusted = OPENSSL_strdup(*++argv);
} else if ((*cmd == CMD_VERIFY || *cmd == CMD_ATTACH) && !strcmp(*argv, "-untrusted")) {
if (--argc < 1) usage(argv0, "all");
OPENSSL_free(options->untrusted);

@ -45,7 +45,7 @@ make_certs() {
script_path=$(pwd)
OPENSSL=openssl
CONF="${script_path}/openssltest.cnf"
$OPENSSL req -config $CONF -new -x509 -days 1800 -key demoCA/CA.key -out tmp/CACert.pem \
$OPENSSL req -config $CONF -new -x509 -days 3600 -key demoCA/CA.key -out tmp/CACert.pem \
-subj "/C=PL/O=osslsigncode/OU=Root CA/CN=CA/emailAddress=CA@example.com" \
2>> "makecerts.log" 1>&2'
test_result $?

@ -19,7 +19,7 @@ x509_extensions = usr_cert
private_key = $dir/demoCA/CA.key
certificate = $dir/tmp/CACert.pem
default_startdate = 180101000000Z
default_enddate = 280101000000Z
default_enddate = 210101000000Z
[ req ]
encrypt_key = no