Используя OpenSSL API, как я могу получить доступ к информации сертификата подписавшего из подписанного файла? - PullRequest
0 голосов
/ 12 апреля 2019

У меня есть конверт CMS с типом контента «signatureData».Как я могу получить доступ к информации сертификата подписавшего, такой как действительность, тема, издатель и т. Д.?

Учитывая тот факт, что

$>openssl cms -verify -signer foo.pem ...

записывает сертификат подписывающего лица) и я могу проанализировать требуемую информацию из foo.pem другими способами, я считаю, что она в принципе доступна. прочитайте в X509 и перейдите к необходимой информации.Однако мне не нужно делать проверку подписи для получения отдельного объекта X509, который я могу затем проанализировать для получения необходимой информации сертификата.

Я могу извлечь другие данные из CMS, такие какатрибут signatureTime:

    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.

Я также пытался получить доступ к X509 напрямую из CMS_ContentInfo, например:

        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 и его число iscmax 0.

Как я могу получить доступ к X509, оттуда к X509_CINF и оттуда к фактическим данным, которые мне нужны (edit :) без необходимостисначала проверьте подпись, чтобы получить отдельный объект X509 ?

В качестве альтернативы, информация скрыта в другом месте в дереве объектов?

1 Ответ

1 голос
/ 16 апреля 2019

Я собираюсь ответить самому себе ...

Решение

        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() перед своим кодом, и он заработал.

edit: Добавлено MCVE (извините, комментарии в стиле C ++).Передайте файл со знаком CMS в формате DER в качестве первого параметра, чтобы это работало.

#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);
}
...