p7decode.c
来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 2,088 行 · 第 1/4 页
C
2,088 行
* cinfo structure later do the destroy of this cert. */ cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->issuerAndSN); if (cert == NULL) { goto done; } signerinfo->cert = cert; /* * Get and convert the signing time; if available, it will be used * both on the cert verification and for importing the sender * email profile. */ utc_stime = SEC_PKCS7GetSigningTime (cinfo); if (utc_stime != NULL) { if (DER_UTCTimeToTime (&stime, utc_stime) != SECSuccess) utc_stime = NULL; /* conversion failed, so pretend none */ } /* * XXX This uses the signing time, if available. Additionally, we * might want to, if there is no signing time, get the message time * from the mail header itself, and use that. That would require * a change to our interface though, and for S/MIME callers to pass * in a time (and for non-S/MIME callers to pass in nothing, or * maybe make them pass in the current time, always?). */ if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, utc_stime != NULL ? stime : PR_Now(), cinfo->pwfn_arg, NULL) != SECSuccess) { /* * XXX Give the user an option to check the signature anyway? * If we want to do this, need to give a way to leave and display * some dialog and get the answer and come back through (or do * the rest of what we do below elsewhere, maybe by putting it * in a function that we call below and could call from a dialog * finish handler). */ goto savecert; } publickey = CERT_ExtractPublicKey (cert); if (publickey == NULL) goto done; /* * XXX No! If digests is empty, see if we can create it now by * digesting the contents. This is necessary if we want to allow * somebody to do a simple decode (without filtering, etc.) and * then later call us here to do the verification. * OR, we can just specify that the interface to this routine * *requires* that the digest(s) be done before calling and either * stashed in the struct itself or passed in explicitly (as would * be done for detached contents). */ if ((digests == NULL || digests[0] == NULL) && (detached_digest == NULL || detached_digest->data == NULL)) goto done; /* * Find and confirm digest algorithm. */ algiddata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm)); if (detached_digest != NULL) { switch (digest_type) { default: case HASH_AlgNULL: PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; case HASH_AlgMD2: PORT_Assert (detached_digest->len == MD2_LENGTH); if (algiddata->offset != SEC_OID_MD2) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } break; case HASH_AlgMD5: PORT_Assert (detached_digest->len == MD5_LENGTH); if (algiddata->offset != SEC_OID_MD5) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } break; case HASH_AlgSHA1: PORT_Assert (detached_digest->len == SHA1_LENGTH); if (algiddata->offset != SEC_OID_SHA1) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } break; } digest = detached_digest; } else { PORT_Assert (digestalgs != NULL && digestalgs[0] != NULL); if (digestalgs == NULL || digestalgs[0] == NULL) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } /* * pick digest matching signerinfo->digestAlg from digests */ if (algiddata == NULL) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } for (i = 0; digestalgs[i] != NULL; i++) { if (SECOID_FindOID (&(digestalgs[i]->algorithm)) == algiddata) break; } if (digestalgs[i] == NULL) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } digest = digests[i]; } /* * XXX This may not be the right set of algorithms to check. * I'd prefer to trust that just calling VFY_Verify{Data,Digest} * would do the right thing (and set an error if it could not); * then additional algorithms could be handled by that code * and we would Just Work. So this check should just be removed, * but not until the VFY code is better at setting errors. */ algiddata = SECOID_FindOID (&(signerinfo->digestEncAlg.algorithm)); if (algiddata == NULL || ((algiddata->offset != SEC_OID_PKCS1_RSA_ENCRYPTION) && (algiddata->offset != SEC_OID_ANSIX9_DSA_SIGNATURE))) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } if (signerinfo->authAttr != NULL) { SEC_PKCS7Attribute *attr; SECItem *value; SECItem encoded_attrs; /* * We have a sigkey only for signedAndEnvelopedData, which is * not supposed to have any authenticated attributes. */ if (sigkey != NULL) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } /* * PKCS #7 says that if there are any authenticated attributes, * then there must be one for content type which matches the * content type of the content being signed, and there must * be one for message digest which matches our message digest. * So check these things first. * XXX Might be nice to have a compare-attribute-value function * which could collapse the following nicely. */ attr = sec_PKCS7FindAttribute (signerinfo->authAttr, SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE); value = sec_PKCS7AttributeValue (attr); if (value == NULL || value->len != content_type->len) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } if (PORT_Memcmp (value->data, content_type->data, value->len) != 0) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } attr = sec_PKCS7FindAttribute (signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE); value = sec_PKCS7AttributeValue (attr); if (value == NULL || value->len != digest->len) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } if (PORT_Memcmp (value->data, digest->data, value->len) != 0) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } /* * Okay, we met the constraints of the basic attributes. * Now check the signature, which is based on a digest of * the DER-encoded authenticated attributes. So, first we * encode and then we digest/verify. */ encoded_attrs.data = NULL; encoded_attrs.len = 0; if (sec_PKCS7EncodeAttributes (NULL, &encoded_attrs, &(signerinfo->authAttr)) == NULL) goto done; if (encoded_attrs.data == NULL || encoded_attrs.len == 0) { PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } goodsig = (PRBool)(VFY_VerifyData (encoded_attrs.data, encoded_attrs.len, publickey, &(signerinfo->encDigest), SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)), cinfo->pwfn_arg) == SECSuccess); PORT_Free (encoded_attrs.data); } else { SECItem *sig; SECItem holder; SECStatus rv; /* * No authenticated attributes. * The signature is based on the plain message digest. */ sig = &(signerinfo->encDigest); if (sig->len == 0) { /* bad signature */ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); goto done; } if (sigkey != NULL) { sec_PKCS7CipherObject *decryptobj; unsigned int buflen; /* * For signedAndEnvelopedData, we first must decrypt the encrypted * digest with the bulk encryption key. The result is the normal * encrypted digest (aka the signature). */ decryptobj = sec_PKCS7CreateDecryptObject (sigkey, bulkid); if (decryptobj == NULL) goto done; buflen = sec_PKCS7DecryptLength (decryptobj, sig->len, PR_TRUE); PORT_Assert (buflen); if (buflen == 0) { /* something is wrong */ sec_PKCS7DestroyDecryptObject (decryptobj); goto done; } holder.data = (unsigned char*)PORT_Alloc (buflen); if (holder.data == NULL) { sec_PKCS7DestroyDecryptObject (decryptobj); goto done; } rv = sec_PKCS7Decrypt (decryptobj, holder.data, &holder.len, buflen, sig->data, sig->len, PR_TRUE); if (rv != SECSuccess) { sec_PKCS7DestroyDecryptObject (decryptobj); goto done; } sig = &holder; } goodsig = (PRBool)(VFY_VerifyDigest (digest, publickey, sig, SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)), cinfo->pwfn_arg) == SECSuccess); if (sigkey != NULL) { PORT_Assert (sig == &holder); PORT_ZFree (holder.data, holder.len); } } if (! goodsig) { /* * XXX Change the generic error into our specific one, because * in that case we get a better explanation out of the Security * Advisor. This is really a bug in our error strings (the * "generic" error has a lousy/wrong message associated with it * which assumes the signature verification was done for the * purposes of checking the issuer signature on a certificate) * but this is at least an easy workaround and/or in the * Security Advisor, which specifically checks for the error * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation * in that case but does not similarly check for * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would * probably say the wrong thing in the case that it *was* the * certificate signature check that failed during the cert * verification done above. Our error handling is really a mess. */ if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE) PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); }savecert: /* * Only save the smime profile if we are checking an email message and * the cert has an email address in it. */ if ( ( cert->emailAddr != NULL ) && ( ( certusage == certUsageEmailSigner ) || ( certusage == certUsageEmailRecipient ) ) ) { SECItem *profile = NULL; int save_error; /* * Remember the current error set because we do not care about * anything set by the functions we are about to call. */ save_error = PORT_GetError(); if (goodsig && (signerinfo->authAttr != NULL)) { /* * If the signature is good, then we can save the S/MIME profile, * if we have one. */ SEC_PKCS7Attribute *attr; attr = sec_PKCS7FindAttribute (signerinfo->authAttr, SEC_OID_PKCS9_SMIME_CAPABILITIES, PR_TRUE); profile = sec_PKCS7AttributeValue (attr); } rv = CERT_SaveSMimeProfile (cert, profile, utc_stime); /* * Restore the saved error in case the calls above set a new * one that we do not actually care about. */ PORT_SetError (save_error); /* * XXX Failure is not indicated anywhere -- the signature * verification itself is unaffected by whether or not the * profile was successfully saved. */ } done: /* * See comment above about why we do not want to destroy cert * itself here. */ if (certs != NULL) CERT_DestroyCertArray (certs, certcount); if (defaultdb == NULL && certdb != NULL) CERT_ClosePermCertDB (certdb); if (publickey != NULL) SECKEY_DestroyPublicKey (publickey); return goodsig;}/* * SEC_PKCS7VerifySignature * Look at a PKCS7 contentInfo and check if the signature is good. * 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. */PRBoolSEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo, SECCertUsage certusage, PRBool keepcerts){ return sec_pkcs7_verify_signature (cinfo, certusage, NULL, HASH_AlgNULL, keepcerts);}/* * SEC_PKCS7VerifyDetachedSignature * Look at a PKCS7 contentInfo and check if the signature matches * a passed-in digest (calculated, supposedly, from detached contents). * 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. */PRBoolSEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo, SECCertUsage certusage, SECItem *detached_digest, HASH_HashType digest_type, PRBool keepcerts){ return sec_pkcs7_verify_signature (cinfo, certusage, detached_digest, digest_type, keepcerts);}/* * Return the asked-for portion of the name of the signer of a PKCS7 * signed object. * * Returns a pointer to allocated memory, which must be freed. * A NULL return value is an error. */#define sec_common_name 1#define sec_email_address 2static char *sec_pkcs7_get_signer_cert_info(SEC_PKCS7ContentInfo *cinfo, int selector){ SECOidTag kind; SEC_PKCS7SignerInfo **signerinfos; CERTCertificate *signercert; char *container; 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: PORT_Assert (0); return NULL; case SEC_OID_PKCS7_SIGNED_DATA: { SEC_PKCS7SignedData *sdp; sdp = cinfo->content.signedData; signerinfos = sdp->signerInfos; } break; case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: { SEC_PKCS7SignedAndEnvelopedData *saedp; saedp = cinfo->content.signedAndEnvelopedData; signerinfos = saedp->signerInfos; } break; } if (signerinfos == NULL || signerinfos[0] == NULL) return NULL; signercert = signerinfos[0]->cert; /* * No cert there; see if we can find one by calling verify ourselves. */ if (signercert == NULL) { /* * The cert usage does not matter in this case, because we do not * actually care about the verification itself, but we have to pick * some valid usage to pass in. */ (void) sec_pkcs7_verify_signature (cinfo, certUsageEmailSigner, NULL, HASH_AlgNULL, PR_FALSE); signercert = signerinfos[0]->cert; if (signercert == NULL) return NULL; } switch (selector) { case sec_common_name: container = CERT_GetCommonName (&signercert->subject); break; case sec_email_address: if(signercert->emailAddr) { container = PORT_Strdup(signercert->emailAddr); } else { container = NULL; } break; default: PORT_Assert (0); container = NULL; break; } return container;}char *SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo){ return sec_pkcs7_get_signer_cert_info(cinfo, sec_common_name);}char *SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo){ return sec_pkcs7_get_signer_cert_info(cinfo, sec_email_address);}/* * Return the signing time, in UTCTime format, of a PKCS7 contentInfo. */SECItem *SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo){ SEC_PKCS7SignerInfo **signerinfos; SEC_PKCS7Attribute *attr; if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) return NULL; signerinfos = cinfo->content.signedData->signerInfos; /* * No signature, or more than one, means no deal. */ if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL) return NULL; attr = sec_PKCS7FindAttribute (signerinfos[0]->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); return sec_PKCS7AttributeValue (attr);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?