Reputation: 71
I have a CMS envelope with content type 'signedData'. How can I access the signer certificate's information such as validity, subject, issuer and so on?
Given the fact that
$>openssl cms -verify -signer foo.pem ...
writes the signer certificate(s) and I can parse the required info from foo.pem by other means, I deduce that it is available in principle. read in the X509 and drill down to the required info. However, to have to verify the signature in order to obtain a separate X509 object which I can then parse for the required cert info is not what I like to do.
I can extract other data from the CMS such as the signingTime attribute:
BIO *in = NULL;
CMS_ContentInfo *cms = NULL;
STACK_OF(CMS_SignerInfo) *ssi = NULL;
CMS_SignerInfo *si = NULL;
int ret = 1;
in = BIO_new_file(argv[1], "r");
if (!in)
goto err;
// cms = PEM_read_bio_CMS(in, NULL, NULL, NULL); //PEM
cms = d2i_CMS_bio(in, NULL); //DER
if (!cms)
goto err;
ssi = CMS_get0_SignerInfos(cms);
if (!ssi)
goto err;
int issimax = sk_CMS_SignerInfo_num(ssi);
for (int issi = 0; issi < issimax; ++issi) {
si = sk_CMS_SignerInfo_value(ssi, issi);
//signing time
int ist = CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1);
X509_ATTRIBUTE *xa = CMS_signed_get_attr(si, ist);
ASN1_TYPE *at = sk_ASN1_TYPE_value(xa->value.set, 0);
printTime("signing time", at); //just a fancy reformat
//...
}
Following the CLI above I tried to access CMS_SignerInfo's 'signer' member as it is of type X509 (put this in place of the "//..."):
X509 *c = si->signer;
However c would always be 0x0.
I also tried to get access to the X509 directly from CMS_ContentInfo like:
STACK_OF(X509) *sc = NULL;
sc = CMS_get0_signers(cms);
if (!sc)
continue;
int iscmax = sk_X509_num(sc);
for (int isc = 0; isc < iscmax; ++isc) {
X509 *c = NULL;
c = sk_X509_value(sc, isc);
}
But the STACK_OF(X509) would also be 0x0 and its count iscmax 0.
How can I get access to the X509, from there to the X509_CINF, and from there to the actual data I need (edit:) without to have to verify the signature first in order to obtain a separate X509 object?
Alternatively, is the information hidden elsewhere in the object tree?
Upvotes: 1
Views: 2367
Reputation: 71
I'm going to answer myself...
The solution
STACK_OF(X509) *signers = CMS_get0_signers(cms);
for (int i = 0; i < sk_X509_num(signers); ++i) {
X509 *signer = sk_X509_value(signers, i);
//do something with signer
}
sketched out above is actually correct.
It failed because the digest table was not initialized.
Debugging into CMS_verify()
I found that it ultimately failed because EVP_get_digestbyobj()
returned NULL. Searching the net for that method name I learned from the manpage that
The digest table must be initialized using, for example, OpenSSL_add_all_digests() for these functions to work.
I added OpenSSL_add_all_digests()
before my code and it worked.
edit: Added MCVE (sorry C++ style comments). Pass CMS signed file in DER format as first parameter for this to work.
#include <openssl/bio.h>
#include <openssl/cms.h>
#include "../crypto/cms/cms_lcl.h" //CMS_SignerInfo_st
int main(int argc, char **argv)
{
if (argc < 2)
return -1;
BIO *in = NULL;
CMS_ContentInfo *cms = NULL;
int flags = CMS_NO_SIGNER_CERT_VERIFY; /* compare -noverify CLI switch */
STACK_OF(CMS_SignerInfo) *signer_infos = NULL;
CMS_SignerInfo *si = NULL;
int ret = 1;
OpenSSL_add_all_digests();
in = BIO_new_file(argv[1], "r");
if (!in)
goto err;
// cms = PEM_read_bio_CMS(in, NULL, NULL, NULL); //PEM
cms = d2i_CMS_bio(in, NULL);
if (!cms)
goto err;
// We do not need to set up a store in case of CMS_NO_SIGNER_CERT_VERIFY.
// If you do, copy it here from apps.c.
// X509_STORE *store = setup_verify(/*bio_err*/NULL, /*CAfile*/NULL, /*CApath*/NULL);
// if (!store)
// goto err;
// ...and neither do we need an output just for the cert info
// BIO *out = BIO_new(BIO_s_mem());
// if (!out)
// goto err;
//Initialize si->signer (see below).
if (!CMS_verify(cms, /*certs*/NULL, /*store*/NULL, /*dcont*/NULL, /*out*/NULL, flags))
goto err;
signer_infos = CMS_get0_SignerInfos(cms);
if (!signer_infos)
goto err;
for (int i = 0; i < sk_CMS_SignerInfo_num(signer_infos); ++i) {
printf("%i:\n", i + 1);
si = sk_CMS_SignerInfo_value(signer_infos, i);
//signing time
int iattr = CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1);
X509_ATTRIBUTE *attr = CMS_signed_get_attr(si, iattr);
ASN1_TYPE *atype = sk_ASN1_TYPE_value(attr->value.set, 0);
printf("signed time: %s\n", atype->value.asn1_string->data); //nvm UTC vs. generalized
//signer certificate info
X509 *signer = si->signer;
if (!signer) {
printf("no signer certificate; continue\n");
continue;
}
X509_CINF *cinfo = signer->cert_info;
if (!cinfo) {
printf("no cert info with signer certificate; continue\n");
continue;
}
printf("signer valid not before: %s\n", cinfo->validity->notBefore->data);
printf("signer valid not after: %s\n", cinfo->validity->notAfter->data);
}
ret = 0;
err:
if (ret) {
ERR_print_errors_fp(stderr);
return -1;
}
}
Upvotes: 4