2013-10-16 15 views
8

Ich versuche die Signaturverifizierung für PDFs zu implementieren. Es ist ein großes Thema, also gehe ich Schritt für Schritt vor, zuerst versuche ich, im Fall eines PDFs, das ich selbst unterschrieben habe, ein positives Ergebnis zu liefern, indem ich alle Standardwerte mit dem aktuellen Acrobat verwende - das sollte SHA256 sein für den Digest und eine PKCS7-abgelöste Signatur. Also knacke ich openssl, und indem ich den Byte-Bereich lese, der in der PDF angegeben ist und die SHA256_* Funktionen aufruft, habe ich einen Hash, mit dem ich vergleichen kann. Jetzt muss ich die Zertifikatsdaten usw. lesen und die PKCS7_* Funktionen verwenden. Dieses schaut derjenige sein, ich will:PKCS # 7 Signaturverifizierung

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

als found in the documentation. Außer dieser Dokumentation sagt mir nicht, wie ich eines dieser Dinge konstruiere. Ok, also ich denke, die BIO *indata kann mit einigen der Funktionen in here und dem Array von certs mit these gemacht werden (obwohl nicht die genauen Details ausgearbeitet), aber was ist mit der PKCS7 *p7, oder die STACK_OF(x) genannt. Ich kann keinen dokumentierten Weg finden, diese Strukturen zu initialisieren. Es gibt einige pkcs7_ctrl Funktionen im pkcs7.h Header: -

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

aber ohne einige Richtlinien dies nicht wie ein Wald scheint wäre es wirksam zu sein blind zu beginnen Stochern in

Habe ich etwas verpasst offensichtlich. ? Wie gehe ich vor, um diese Funktion mit den Datenwerten aufzurufen, die ich aus der PDF analysiert habe?

Antwort

7

Ok, fand das alles auf (sehr) harte Weise. So machen Sie das, damit andere leichter lernen können.

Können sagen wir die Unterschrift char* sig der Länge haben int sig_length und Verifikationsdaten char* data, int data_length. (Es gibt einige Feinheiten hier für PDF-Signaturen, aber diese sind in der PDF-Spezifikation gut dokumentiert.)

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

Die Funktion, die die Arbeit tut, ist tatsächlich PKCS7_dataVerify, und Sie brauchen keine verdaut selbst laufen.

Aber warten Sie, wenn Sie dies versuchen, wird es nicht funktionieren! Warum? Weil die Überprüfung sowohl Vertrauen als auch Integrität schafft. Darüber hinaus müssen Sie auch Vertrauen schaffen, indem Sie dem Store Zertifikate hinzufügen, was ebenfalls kompliziert und nicht dokumentiert ist.

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

wo

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

Sie können einstellen, um den Fehler zu ok und es sagen: Wenn Sie feinkörnig Ergebnisse für Sie einen Rückruf über die Prüfung über den Zertifikatspeicher wie folgt festgelegt werden wollen B. verifizieren, wenn Sie abgelaufene Zertifikate ignorieren möchten:

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