Проверка подписи PKCS#7

Я пытаюсь реализовать проверку подписи для документов PDF. Это большая тема, поэтому я делаю это шаг за шагом, сначала я пытаюсь фактически вернуть позитив в случае PDF, который я подписал сам, используя все значения по умолчанию с текущим Acrobat - это должен быть SHA256 для дайджеста и отдельная подпись PKCS7. Итак, я взломаю openssl, и, прочитав диапазон байтов, указанный в PDF, и вызвав SHA256_* функции у меня есть хэш для сравнения. Так что теперь мне нужно чтобы прочитать данные сертификата и т. д., и использовать PKCS7_* функции. Этот, похоже, тот, который я хочу:

int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, BIO *indata, BIO *out, int flags);

as найти в документации. За исключением того, что упомянутая документация не говорит мне, как построить любую из этих вещей. Хорошо, я думаю, что BIO *indata можно сделать с некоторыми из функций в здесь и массив сертификатов с использованием эти (несмотря на то, что не разработал точные детали), но как насчет PKCS7 *p7, или STACK_OF(x) призвали. Я не могу найти никакого документированного способа инициализации этих структур. Есть некоторые функции pkcs7_ctrl в pkcs7.h заголовок:-

long PKCS7_ctrl(PKCS7 *p7, int cmd, long larg, char *parg);

int PKCS7_set_type(PKCS7 *p7, int type);
int PKCS7_set0_type_other(PKCS7 *p7, int type, ASN1_TYPE *other);
int PKCS7_set_content(PKCS7 *p7, PKCS7 *p7_data);
int PKCS7_SIGNER_INFO_set(PKCS7_SIGNER_INFO *p7i, X509 *x509, EVP_PKEY *pkey, const EVP_MD *dgst);
int PKCS7_SIGNER_INFO_sign(PKCS7_SIGNER_INFO *si);
int PKCS7_add_signer(PKCS7 *p7, PKCS7_SIGNER_INFO *p7i);
int PKCS7_add_certificate(PKCS7 *p7, X509 *x509);
int PKCS7_add_crl(PKCS7 *p7, X509_CRL *x509);
int PKCS7_content_new(PKCS7 *p7, int nid);
int PKCS7_dataVerify(X509_STORE *cert_store, X509_STORE_CTX *ctx,
    BIO *bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si); 
int PKCS7_signatureVerify(BIO *bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si, X509 *x509);

BIO *PKCS7_dataInit(PKCS7 *p7, BIO *bio);
int PKCS7_dataFinal(PKCS7 *p7, BIO *bio);
BIO *PKCS7_dataDecode(PKCS7 *p7, EVP_PKEY *pkey, BIO *in_bio, X509 *pcert);

но без некоторых рекомендаций это не похоже на лес, было бы эффективно начать слепо ковырять.

я пропустил что-то очевидное? Как я могу вызвать эту функцию со значениями данных, которые я проанализировал из PDF?

1 ответов


ОК, нашел все это (очень) трудный путь. Вот как вы это делаете, чтобы другим было легче учиться.

допустим, у нас есть подпись char* sig длиной int sig_length, и данные проверки char* data, int data_length. (Здесь есть некоторые тонкости для подписей PDF, но они хорошо документированы в спецификации PDF.)

OpenSSL_add_all_algorithms();
OpenSSL_add_all_digests();
EVP_add_digest(EVP_md5());
EVP_add_digest(EVP_sha1());
EVP_add_digest(EVP_sha256());

BIO* sig_BIO = BIO_new_mem_buf(sig, sig_length)
PKCS7* sig_pkcs7 = d2i_PKCS7_bio(sig_BIO, NULL);

BIO* data_BIO = BIO_new_mem_buf(data, data_length)
BIO* data_pkcs7_BIO = PKCS7_dataInit(sig_pkcs7, data_BIO);

// Goto this place in the BIO. Why? No idea!
char unneeded[1024*4];
while (BIO_read(dataPKCS7_BIO, unneeded, sizeof(buffer)) > 0);

int result;
X509_STORE *certificateStore = X509_STORE_new();
X509_STORE_CTX certificateContext;
STACK_OF(PKCS7_SIGNER_INFO) *signerStack = PKCS7_get_signer_info(sig_pkcs7);
int numSignerInfo = sk_PKCS7_SIGNER_INFO_num(signerStack);
for (int i=0; i<numSignerInfo; ++i) {
    PKCS7_SIGNER_INFO *signerInfo = sk_PKCS7_SIGNER_INFO_value(signerStack, i);
    result = PKCS7_dataVerify(certificateStore, &certificateContext, data_pkcs7_BIO, sig_pkcs7, signerInfo);
}

X509_STORE_CTX_cleanup(&certificateContext);
BIO_free(sig_BIO);
BIO_free(data_BIO);
BIO_free(data_pkcs7_BIO);
PKCS7_free(sig_pkcs7);
X509_STORE_free(certificateStore);

функция, которая выполняет работу, на самом деле PKCS7_dataVerify, и вам не нужно запускать какие-либо дайджесты самостоятельно.

но ждать, если вы попробуете это, это не сработает! Почему? Потому что проверка делает и доверие и целостность. В дополнение к этому Вам также нужно будет либо установить доверие, добавив сертификаты в магазин, который также является сложным и недокументированным. Если вам нужны результаты с мелким зерном, вы захотите установить обратный вызов для проверки через хранилище сертификатов следующим образом:

X509_VERIFY_PARAM_set_flags(certificateStore->param, X509_V_FLAG_CB_ISSUER_CHECK);
X509_STORE_set_verify_cb_func(certificateStore, verificationCallback);

здесь

static int verificationCallback(int ok, X509_STORE_CTX *ctx) {
    switch (ctx->error)
    {
        case X509_V_ERR_INVALID_PURPOSE: //...
        case X509_V_ERR_CERT_HAS_EXPIRED: //...
        case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: //... 
        case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: //...
        // ... etc
        default: break;
    }
    return ok;
}

вы можете установить ошибку в ok и сказать, чтобы проверить, например, если вы хотите игнорировать просроченные сертификаты:

static int verificationCallback(int ok, X509_STORE_CTX *ctx) {
    switch (ctx->error)
    {
        case X509_V_ERR_CERT_HAS_EXPIRED: 
            X509_STORE_CTX_set_error(ctx, X509_V_OK);
            ok = 1;
            break;
    }
    return ok;
}