使用OpenSSL API,如何从已签名的文件访问签署者的证书信息?

时间:2019-04-12 14:58:11

标签: openssl digital-signature

我有一个内容类型为“ 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对象

或者,信息是否隐藏在对象树的其他位置?

1 个答案:

答案 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);
}