我有一个内容类型为“ signedData”的CMS信封。如何访问签署者证书的信息,例如有效性,主题,颁发者等?
鉴于事实
$>openssl cms -verify -signer foo.pem ...
写出签署者证书,然后我可以通过其他方法从foo.pem中解析所需的信息,我推断它在原则上是可用的。在X509中读取并向下钻取必填信息。但是,必须验证签名以获得单独的X509对象,然后我可以解析该X509对象以获取所需的证书信息,这不是我想要的。
我可以从CMS中提取其他数据,例如signingTime属性:
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
//...
}
按照上面的CLI,我尝试访问CMS_SignerInfo的'signer'成员,因为它的类型为X509(将其替换为“ //...”):
X509 *c = si->signer;
但是c始终为0x0。
我还试图直接从CMS_ContentInfo访问X509,例如:
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);
}
但是STACK_OF(X509)也将为0x0,其计数为cmax 0。
我如何从那里访问X509,从X509_CINF,再到实际数据,我需要(edit :),而不必先验证签名即可获得单独的X509对象?
或者,信息是否隐藏在对象树的其他位置?
答案 0 :(得分:1)
我要回答自己...
解决方案
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
}
上面列出的内容实际上是正确的。
失败,因为摘要表未初始化。
调试到CMS_verify()
时,我发现它最终失败了,因为EVP_get_digestbyobj()
返回了NULL。在网上搜索我从联机帮助页中学到的方法名称
必须使用例如OpenSSL_add_all_digests()初始化摘要表,这些功能才能正常工作。
我在代码之前添加了OpenSSL_add_all_digests()
,它可以正常工作。
编辑:添加了MCVE(对不起的C ++样式注释)。 将DER格式的CMS签名文件作为第一个参数传递。
#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);
}