p7decode.c
来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 2,088 行 · 第 1/4 页
C
2,088 行
* XXX If the decryption key callback is set, we want to start * the decryption. If the callback is not set, we will treat the * content as plain data, since we do not have the key. * * Is this the proper thing to do? */ if (before && dest == &(encd->encContentInfo.encContent)) { /* * Start the encryption process if the decryption key callback * is present. Otherwise, treat the content like plain data. */ rv = SECSuccess; if (p7dcx->dkcb != NULL) { rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, NULL, &(encd->encContentInfo), NULL); } if (rv != SECSuccess) SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); break; } /* * Are we done? */ if (after && dest == &(encd->encContentInfo.encContent)) { /* * Close out the decryption context. We ignore any error * because we are stopping anyway; the error status left * behind in p7dcx will be seen by outer functions. */ (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, &(encd->encContentInfo)); /* * Stop notify. */ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); } break; case SEC_OID_PKCS7_DATA: /* * If a output callback has been specified, we want to set the filter * to call the callback. This is taken care of in * sec_pkcs7_decoder_start_decrypt() or * sec_pkcs7_decoder_start_digests() for the other content types. */ if (before && dest == &(cinfo->content.data)) { /* * Set the filter proc up. */ SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, sec_pkcs7_decoder_filter, p7dcx, (PRBool)(p7dcx->cb != NULL)); break; } if (after && dest == &(cinfo->content.data)) { /* * Time to clean up after ourself, stop the Notify and Filter * procedures. */ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); } break; default: SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); break; }}SEC_PKCS7DecoderContext *SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback cb, void *cb_arg, SECKEYGetPasswordKey pwfn, void *pwfn_arg, SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg, SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb){ SEC_PKCS7DecoderContext *p7dcx; SEC_ASN1DecoderContext *dcx; SEC_PKCS7ContentInfo *cinfo; PRArenaPool *poolp; poolp = PORT_NewArena (1024); /* XXX what is right value? */ if (poolp == NULL) return NULL; cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo)); if (cinfo == NULL) { PORT_FreeArena (poolp, PR_FALSE); return NULL; } cinfo->poolp = poolp; cinfo->pwfn = pwfn; cinfo->pwfn_arg = pwfn_arg; cinfo->created = PR_FALSE; cinfo->refCount = 1; p7dcx = (SEC_PKCS7DecoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7DecoderContext)); if (p7dcx == NULL) { PORT_FreeArena (poolp, PR_FALSE); return NULL; } p7dcx->tmp_poolp = PORT_NewArena (1024); /* XXX what is right value? */ if (p7dcx->tmp_poolp == NULL) { PORT_Free (p7dcx); PORT_FreeArena (poolp, PR_FALSE); return NULL; } dcx = SEC_ASN1DecoderStart (poolp, cinfo, sec_PKCS7ContentInfoTemplate); if (dcx == NULL) { PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE); PORT_Free (p7dcx); PORT_FreeArena (poolp, PR_FALSE); return NULL; } SEC_ASN1DecoderSetNotifyProc (dcx, sec_pkcs7_decoder_notify, p7dcx); p7dcx->dcx = dcx; p7dcx->cinfo = cinfo; p7dcx->cb = cb; p7dcx->cb_arg = cb_arg; p7dcx->pwfn = pwfn; p7dcx->pwfn_arg = pwfn_arg; p7dcx->dkcb = decrypt_key_cb; p7dcx->dkcb_arg = decrypt_key_cb_arg; p7dcx->decrypt_allowed_cb = decrypt_allowed_cb; return p7dcx;}/* * Do the next chunk of PKCS7 decoding. If there is a problem, set * an error and return a failure status. Note that in the case of * an error, this routine is still prepared to be called again and * again in case that is the easiest route for our caller to take. * We simply detect it and do not do anything except keep setting * that error in case our caller has not noticed it yet... */SECStatusSEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx, const char *buf, unsigned long len){ if (p7dcx->cinfo != NULL && p7dcx->dcx != NULL) { PORT_Assert (p7dcx->error == 0); if (p7dcx->error == 0) { if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) { p7dcx->error = PORT_GetError(); PORT_Assert (p7dcx->error); if (p7dcx->error == 0) p7dcx->error = -1; } } } if (p7dcx->error) { if (p7dcx->dcx != NULL) { (void) SEC_ASN1DecoderFinish (p7dcx->dcx); p7dcx->dcx = NULL; } if (p7dcx->cinfo != NULL) { SEC_PKCS7DestroyContentInfo (p7dcx->cinfo); p7dcx->cinfo = NULL; } PORT_SetError (p7dcx->error); return SECFailure; } return SECSuccess;}SEC_PKCS7ContentInfo *SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx){ SEC_PKCS7ContentInfo *cinfo; cinfo = p7dcx->cinfo; if (p7dcx->dcx != NULL) { if (SEC_ASN1DecoderFinish (p7dcx->dcx) != SECSuccess) { SEC_PKCS7DestroyContentInfo (cinfo); cinfo = NULL; } } PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE); PORT_Free (p7dcx); return cinfo;}SEC_PKCS7ContentInfo *SEC_PKCS7DecodeItem(SECItem *p7item, SEC_PKCS7DecoderContentCallback cb, void *cb_arg, SECKEYGetPasswordKey pwfn, void *pwfn_arg, SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg, SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb){ SEC_PKCS7DecoderContext *p7dcx; p7dcx = SEC_PKCS7DecoderStart(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, decrypt_allowed_cb); (void) SEC_PKCS7DecoderUpdate(p7dcx, (char *) p7item->data, p7item->len); return SEC_PKCS7DecoderFinish(p7dcx);}/* * If the thing contains any certs or crls return true; false otherwise. */PRBoolSEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo){ SECOidTag kind; SECItem **certs; CERTSignedCrl **crls; kind = SEC_PKCS7ContentType (cinfo); switch (kind) { default: case SEC_OID_PKCS7_DATA: case SEC_OID_PKCS7_DIGESTED_DATA: case SEC_OID_PKCS7_ENVELOPED_DATA: case SEC_OID_PKCS7_ENCRYPTED_DATA: return PR_FALSE; case SEC_OID_PKCS7_SIGNED_DATA: certs = cinfo->content.signedData->rawCerts; crls = cinfo->content.signedData->crls; break; case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: certs = cinfo->content.signedAndEnvelopedData->rawCerts; crls = cinfo->content.signedAndEnvelopedData->crls; break; } /* * I know this could be collapsed, but I was in a mood to be explicit. */ if (certs != NULL && certs[0] != NULL) return PR_TRUE; else if (crls != NULL && crls[0] != NULL) return PR_TRUE; else return PR_FALSE;}/* return the content length...could use GetContent, however we * need the encrypted content length */PRBoolSEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen){ SECItem *item = NULL; if(cinfo == NULL) { return PR_TRUE; } switch(SEC_PKCS7ContentType(cinfo)) { case SEC_OID_PKCS7_DATA: item = cinfo->content.data; break; case SEC_OID_PKCS7_ENCRYPTED_DATA: item = &cinfo->content.encryptedData->encContentInfo.encContent; break; default: /* add other types */ return PR_FALSE; } if(!item) { return PR_TRUE; } else if(item->len <= minLen) { return PR_TRUE; } return PR_FALSE;}PRBoolSEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo){ SECOidTag kind; kind = SEC_PKCS7ContentType (cinfo); switch (kind) { default: case SEC_OID_PKCS7_DATA: case SEC_OID_PKCS7_DIGESTED_DATA: case SEC_OID_PKCS7_SIGNED_DATA: return PR_FALSE; case SEC_OID_PKCS7_ENCRYPTED_DATA: case SEC_OID_PKCS7_ENVELOPED_DATA: case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: return PR_TRUE; }}/* * If the PKCS7 content has a signature (not just *could* have a signature) * return true; false otherwise. This can/should be called before calling * VerifySignature, which will always indicate failure if no signature is * present, but that does not mean there even was a signature! * Note that the content itself can be empty (detached content was sent * another way); it is the presence of the signature that matters. */PRBoolSEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo){ SECOidTag kind; SEC_PKCS7SignerInfo **signerinfos; kind = SEC_PKCS7ContentType (cinfo); switch (kind) { default: case SEC_OID_PKCS7_DATA: case SEC_OID_PKCS7_DIGESTED_DATA: case SEC_OID_PKCS7_ENVELOPED_DATA: case SEC_OID_PKCS7_ENCRYPTED_DATA: return PR_FALSE; case SEC_OID_PKCS7_SIGNED_DATA: signerinfos = cinfo->content.signedData->signerInfos; break; case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos; break; } /* * I know this could be collapsed; but I kind of think it will get * more complicated before I am finished, so... */ if (signerinfos != NULL && signerinfos[0] != NULL) return PR_TRUE; else return PR_FALSE;}/* * SEC_PKCS7ContentVerifySignature * Look at a PKCS7 contentInfo and check if the signature is good. * The digest was either calculated earlier (and is stored in the * contentInfo itself) or is passed in via "detached_digest". * * The verification checks that the signing cert is valid and trusted * for the purpose specified by "certusage". * * In addition, if "keepcerts" is true, add any new certificates found * into our local database. * * XXX Each place which returns PR_FALSE should be sure to have a good * error set for inspection by the caller. Alternatively, we could create * an enumeration of success and each type of failure and return that * instead of a boolean. For now, the default in a bad situation is to * set the error to SEC_ERROR_PKCS7_BAD_SIGNATURE. But this should be * reviewed; better (more specific) errors should be possible (to distinguish * a signature failure from a badly-formed pkcs7 signedData, for example). * Some of the errors should probably just be SEC_ERROR_BAD_SIGNATURE, * but that has a less helpful error string associated with it right now; * if/when that changes, review and change these as needed. * * XXX This is broken wrt signedAndEnvelopedData. In that case, the * message digest is doubly encrypted -- first encrypted with the signer * private key but then again encrypted with the bulk encryption key used * to encrypt the content. So before we can pass the digest to VerifyDigest, * we need to decrypt it with the bulk encryption key. Also, in this case, * there should be NO authenticatedAttributes (signerinfo->authAttr should * be NULL). */static PRBoolsec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo, SECCertUsage certusage, SECItem *detached_digest, HASH_HashType digest_type, PRBool keepcerts){ SECAlgorithmID **digestalgs, *bulkid; SECItem *digest; SECItem **digests; SECItem **rawcerts; CERTSignedCrl **crls; SEC_PKCS7SignerInfo **signerinfos, *signerinfo; CERTCertificate *cert, **certs; PRBool goodsig; CERTCertDBHandle local_certdb, *certdb, *defaultdb; SECOidData *algiddata; int i, certcount; SECKEYPublicKey *publickey; SECItem *content_type; PK11SymKey *sigkey; SECItem *utc_stime; int64 stime; SECStatus rv; /* * Everything needed in order to "goto done" safely. */ goodsig = PR_FALSE; certcount = 0; cert = NULL; certs = NULL; certdb = NULL; defaultdb = CERT_GetDefaultCertDB(); publickey = NULL; if (! SEC_PKCS7ContentIsSigned(cinfo)) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } PORT_Assert (cinfo->contentTypeTag != NULL); switch (cinfo->contentTypeTag->offset) { default: case SEC_OID_PKCS7_DATA: case SEC_OID_PKCS7_DIGESTED_DATA: case SEC_OID_PKCS7_ENVELOPED_DATA: case SEC_OID_PKCS7_ENCRYPTED_DATA: /* Could only get here if SEC_PKCS7ContentIsSigned is broken. */ PORT_Assert (0); case SEC_OID_PKCS7_SIGNED_DATA: { SEC_PKCS7SignedData *sdp; sdp = cinfo->content.signedData; digestalgs = sdp->digestAlgorithms; digests = sdp->digests; rawcerts = sdp->rawCerts; crls = sdp->crls; signerinfos = sdp->signerInfos; content_type = &(sdp->contentInfo.contentType); sigkey = NULL; bulkid = NULL; } break; case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: { SEC_PKCS7SignedAndEnvelopedData *saedp; saedp = cinfo->content.signedAndEnvelopedData; digestalgs = saedp->digestAlgorithms; digests = saedp->digests; rawcerts = saedp->rawCerts; crls = saedp->crls; signerinfos = saedp->signerInfos; content_type = &(saedp->encContentInfo.contentType); sigkey = saedp->sigKey; bulkid = &(saedp->encContentInfo.contentEncAlg); } break; } if ((signerinfos == NULL) || (signerinfos[0] == NULL)) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } /* * XXX Need to handle multiple signatures; checking them is easy, * but what should be the semantics here (like, return value)? */ if (signerinfos[1] != NULL) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } signerinfo = signerinfos[0]; /* * XXX I would like to just pass the issuerAndSN, along with the rawcerts * and crls, to some function that did all of this certificate stuff * (open/close the database if necessary, verifying the certs, etc.) * and gave me back a cert pointer if all was good. */ certdb = defaultdb; if (certdb == NULL) { if (CERT_OpenCertDBFilename (&local_certdb, NULL, (PRBool)!keepcerts) != SECSuccess) goto done; certdb = &local_certdb; } certcount = 0; if (rawcerts != NULL) { for (; rawcerts[certcount] != NULL; certcount++) { /* just counting */ } } /* * Note that the result of this is that each cert in "certs" * needs to be destroyed. */ rv = CERT_ImportCerts(certdb, certusage, certcount, rawcerts, &certs, keepcerts, PR_FALSE, NULL); if ( rv != SECSuccess ) { goto done; } /* * This cert will also need to be freed, but since we save it * in signerinfo for later, we do not want to destroy it when * we leave this function -- we let the clean-up of the entire
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?