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 + -
显示快捷键?