2013-10-16 74 views
8

我正在尝试对PDF执行签名验证。这是一个很大的话题,所以我一次只采取一步,首先我试图在我自己签名的PDF的情况下实际返回一个积极的结果,使用当前Acrobat的所有默认值 - 应该是SHA256摘要和PKCS7分离签名。所以,我破解了openssl,通过阅读PDF中给出的字节范围并调用SHA256_*函数,我有一个散列用于比较。所以现在我需要读取证书数据等,并使用PKCS7_*函数。这看起来是我想要的一个:PKCS#7签名验证

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

as found in the documentation。除了说文档没有告诉我如何构建这些东西。好的,所以我认为BIO *indata可以用here中的一些功能和使用these(尽管还没有计算出确切的细节)的证书组成,但PKCS7 *p7STACK_OF(x)要求。我无法找到任何记录的方式来初始化这些结构。有在pkcs7.h头有些pkcs7_ctrl功能: -

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解析的数据值来调用此函数?

回答

7

好的,发现所有这些(非常)困难的方式。这就是你怎么做的,以便其他人可以更容易地学习。

比方说,我们有长int sig_length的签名char* sig,并验证数据char* dataint 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; 
}