2013-10-16 15 views
8

Sto tentando di implementare la verifica della firma per i PDF. È un argomento importante, quindi lo sto prendendo un passo alla volta, prima di tutto sto cercando di restituire un risultato positivo nel caso di un PDF che ho firmato da solo, utilizzando tutti i valori predefiniti con l'Acrobat corrente - che dovrebbe essere SHA256 per il digest e una firma separata PKCS7. Quindi, apro openssl, e leggendo l'intervallo di byte indicato nel PDF e chiamando le funzioni SHA256_* ho un hash da confrontare. Così ora ho bisogno di leggere i dati del certificato, ecc, e utilizzare le funzioni PKCS7_*. Questo sembra essere quello che voglio:PKCS # 7 Verifica della firma

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

come found in the documentation. Tranne che la documentazione non mi dice come costruire una di queste cose. Ok, quindi penso che il BIO *indata possa essere fatto con alcune delle funzioni in here e l'array di certs che usa these (nonostante non abbia elaborato i dettagli precisi), ma per quanto riguarda lo PKCS7 *p7 o il STACK_OF(x) richiesto. Non riesco a trovare alcun modo documentato per inizializzare queste strutture. Ci sono alcune funzioni pkcs7_ctrl nell'intestazione 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); 

ma senza alcune linee guida questo non sembra come una foresta che sarebbe efficace per iniziare alla cieca rovistando in

Ho perso qualcosa di ovvio. ? Come faccio a chiamare questa funzione con i valori dei dati che ho analizzato dal PDF?

risposta

7

Ok, ho trovato tutto questo nel modo (molto) difficile. Ecco come lo fai, in modo che altri possano apprendere più facilmente.

Diciamo che abbiamo la firma char* sig di lunghezza int sig_length, e dati di verifica char* data, int data_length. (Ci sono alcune sottigliezze qui per le firme in formato PDF, ma questi sono ben documentati nelle specifiche 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); 

La funzione che fa il lavoro è in realtà PKCS7_dataVerify, e non è necessario eseguire alcun digest te stesso.

Ma aspetta, se ci provi, non funzionerà! Perché? Perché la verifica ha fiducia e integrità. Oltre a questo, dovrai anche stabilire una fiducia aggiungendo certs allo store, che è anche complicato e non documentato. Se si desidera che i risultati a grana fine per voi vorrà impostare una richiamata sulla verifica tramite l'archivio di certificati in questo modo:

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

dove

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; 
} 

È possibile impostare l'errore di ok e raccontarla per verificare, ad esempio se si desidera ignorare i certificati scaduti:

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; 
}