p7encode.c
来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 1,330 行 · 第 1/3 页
C
1,330 行
CERTCertificate **certs; CERTCertificateList **certlists; SECAlgorithmID **digestalgs; SECItem **digests; SEC_PKCS7SignerInfo *signerinfo, **signerinfos; SECItem **rawcerts, ***rawcertsp; PRArenaPool *poolp; int certcount; int ci, cli, rci, si; kind = SEC_PKCS7ContentType (cinfo); switch (kind) { default: case SEC_OID_PKCS7_DATA: case SEC_OID_PKCS7_DIGESTED_DATA: case SEC_OID_PKCS7_ENCRYPTED_DATA: case SEC_OID_PKCS7_ENVELOPED_DATA: certs = NULL; certlists = NULL; digestalgs = NULL; digests = NULL; signerinfos = NULL; rawcertsp = NULL; break; case SEC_OID_PKCS7_SIGNED_DATA: { SEC_PKCS7SignedData *sdp; sdp = cinfo->content.signedData; certs = sdp->certs; certlists = sdp->certLists; digestalgs = sdp->digestAlgorithms; digests = sdp->digests; signerinfos = sdp->signerInfos; rawcertsp = &(sdp->rawCerts); } break; case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: { SEC_PKCS7SignedAndEnvelopedData *saedp; saedp = cinfo->content.signedAndEnvelopedData; certs = saedp->certs; certlists = saedp->certLists; digestalgs = saedp->digestAlgorithms; digests = saedp->digests; signerinfos = saedp->signerInfos; rawcertsp = &(saedp->rawCerts); } break; } if (certs == NULL && certlists == NULL && signerinfos == NULL) return SECSuccess; /* nothing for us to do! */ poolp = cinfo->poolp; certcount = 0; if (signerinfos != NULL) { SECOidTag digestalgtag; int di; SECStatus rv; CERTCertificate *cert; SECKEYPrivateKey *privkey; SECItem signature; SECOidTag signalgtag; PORT_Assert (digestalgs != NULL && digests != NULL); /* * If one fails, we bail right then. If we want to continue and * try to do subsequent signatures, this loop, and the departures * from it, will need to be reworked. */ for (si = 0; signerinfos[si] != NULL; si++) { signerinfo = signerinfos[si]; /* find right digest */ digestalgtag = SECOID_GetAlgorithmTag (&(signerinfo->digestAlg)); for (di = 0; digestalgs[di] != NULL; di++) { /* XXX Should I be comparing more than the tag? */ if (digestalgtag == SECOID_GetAlgorithmTag (digestalgs[di])) break; } if (digestalgs[di] == NULL) { /* XXX oops; do what? set an error? */ return SECFailure; } PORT_Assert (digests[di] != NULL); cert = signerinfo->cert; privkey = PK11_FindKeyByAnyCert (cert, pwfnarg); if (privkey == NULL) return SECFailure; /* * XXX I think there should be a cert-level interface for this, * so that I do not have to know about subjectPublicKeyInfo... */ signalgtag = SECOID_GetAlgorithmTag (&(cert->subjectPublicKeyInfo.algorithm)); /* Fortezza MISSI have weird signature formats. Map them * to standard DSA formats */ signalgtag = PK11_FortezzaMapSig(signalgtag); if (signerinfo->authAttr != NULL) { SEC_PKCS7Attribute *attr; SECItem encoded_attrs; SECItem *dummy; /* * First, find and fill in the message digest attribute. */ attr = sec_PKCS7FindAttribute (signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE); PORT_Assert (attr != NULL); if (attr == NULL) { SECKEY_DestroyPrivateKey (privkey); return SECFailure; } /* * XXX The second half of the following assertion prevents * the encoder from being called twice on the same content. * Either just remove the second half the assertion, or * change the code to check if the value already there is * the same as digests[di], whichever seems more right. */ PORT_Assert (attr->values != NULL && attr->values[0] == NULL); attr->values[0] = digests[di]; /* * Before encoding, reorder the attributes so that when they * are encoded, they will be conforming DER, which is required * to have a specific order and that is what must be used for * the hash/signature. We do this here, rather than building * it into EncodeAttributes, because we do not want to do * such reordering on incoming messages (which also uses * EncodeAttributes) or our old signatures (and other "broken" * implementations) will not verify. So, we want to guarantee * that we send out good DER encodings of attributes, but not * to expect to receive them. */ rv = sec_PKCS7ReorderAttributes (signerinfo->authAttr); if (rv != SECSuccess) { SECKEY_DestroyPrivateKey (privkey); return SECFailure; } encoded_attrs.data = NULL; encoded_attrs.len = 0; dummy = sec_PKCS7EncodeAttributes (NULL, &encoded_attrs, &(signerinfo->authAttr)); if (dummy == NULL) { SECKEY_DestroyPrivateKey (privkey); return SECFailure; } rv = SEC_SignData (&signature, encoded_attrs.data, encoded_attrs.len, privkey, sec_pkcs7_pick_sign_alg (digestalgtag, signalgtag)); SECITEM_FreeItem (&encoded_attrs, PR_FALSE); } else { rv = SGN_Digest (privkey, digestalgtag, &signature, digests[di]); } SECKEY_DestroyPrivateKey (privkey); if (rv != SECSuccess) return rv; rv = SECITEM_CopyItem (poolp, &(signerinfo->encDigest), &signature); if (rv != SECSuccess) return rv; SECITEM_FreeItem (&signature, PR_FALSE); rv = SECOID_SetAlgorithmID (poolp, &(signerinfo->digestEncAlg), signalgtag, NULL); if (rv != SECSuccess) return SECFailure; /* * Count the cert chain for this signer. */ if (signerinfo->certList != NULL) certcount += signerinfo->certList->len; } } if (certs != NULL) { for (ci = 0; certs[ci] != NULL; ci++) certcount++; } if (certlists != NULL) { for (cli = 0; certlists[cli] != NULL; cli++) certcount += certlists[cli]->len; } if (certcount == 0) return SECSuccess; /* signing done; no certs */ /* * Combine all of the certs and cert chains into rawcerts. * Note: certcount is an upper bound; we may not need that many slots * but we will allocate anyway to avoid having to do another pass. * (The temporary space saving is not worth it.) */ rawcerts = (SECItem**)PORT_ArenaAlloc (poolp, (certcount + 1) * sizeof(SECItem *)); if (rawcerts == NULL) return SECFailure; /* * XXX Want to check for duplicates and not add *any* cert that is * already in the set. This will be more important when we start * dealing with larger sets of certs, dual-key certs (signing and * encryption), etc. For the time being we can slide by... */ rci = 0; if (signerinfos != NULL) { for (si = 0; signerinfos[si] != NULL; si++) { signerinfo = signerinfos[si]; for (ci = 0; ci < signerinfo->certList->len; ci++) rawcerts[rci++] = &(signerinfo->certList->certs[ci]); } } if (certs != NULL) { for (ci = 0; certs[ci] != NULL; ci++) rawcerts[rci++] = &(certs[ci]->derCert); } if (certlists != NULL) { for (cli = 0; certlists[cli] != NULL; cli++) { for (ci = 0; ci < certlists[cli]->len; ci++) rawcerts[rci++] = &(certlists[cli]->certs[ci]); } } rawcerts[rci] = NULL; *rawcertsp = rawcerts; return SECSuccess;}SECStatusSEC_PKCS7EncoderFinish (SEC_PKCS7EncoderContext *p7ecx, SECKEYGetPasswordKey pwfn, void *pwfnarg){ SECStatus rv; /* * Flush out any remaining data. */ rv = sec_pkcs7_encoder_work_data (p7ecx, NULL, NULL, 0, PR_TRUE); /* * Turn off streaming stuff. */ SEC_ASN1EncoderClearTakeFromBuf (p7ecx->ecx); SEC_ASN1EncoderClearStreaming (p7ecx->ecx); if (rv != SECSuccess) goto loser; rv = sec_pkcs7_encoder_sig_and_certs (p7ecx->cinfo, pwfn, pwfnarg); if (rv != SECSuccess) goto loser; rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, NULL, 0);loser: SEC_ASN1EncoderFinish (p7ecx->ecx); PORT_Free (p7ecx); return rv;}/* * After this routine is called, the entire PKCS7 contentInfo is ready * to be encoded. This is used internally, but can also be called from * elsewhere for those who want to be able to just have pointers to * the ASN1 template for pkcs7 contentInfo built into their own encodings. */SECStatusSEC_PKCS7PrepareForEncode (SEC_PKCS7ContentInfo *cinfo, PK11SymKey *bulkkey, SECKEYGetPasswordKey pwfn, void *pwfnarg){ SEC_PKCS7EncoderContext *p7ecx; SECItem *content, *enc_content; SECStatus rv; p7ecx = sec_pkcs7_encoder_start_contexts (cinfo, bulkkey); if (p7ecx == NULL) return SECFailure; content = SEC_PKCS7GetContent (cinfo); if (p7ecx->encryptobj != NULL) { SECOidTag kind; SEC_PKCS7EncryptedContentInfo *enccinfo; kind = SEC_PKCS7ContentType (p7ecx->cinfo); switch (kind) { default: PORT_Assert (0); rv = SECFailure; goto loser; case SEC_OID_PKCS7_ENCRYPTED_DATA: enccinfo = &(p7ecx->cinfo->content.encryptedData->encContentInfo); break; case SEC_OID_PKCS7_ENVELOPED_DATA: enccinfo = &(p7ecx->cinfo->content.envelopedData->encContentInfo); break; case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: enccinfo = &(p7ecx->cinfo->content.signedAndEnvelopedData->encContentInfo); break; } enc_content = &(enccinfo->encContent); } else { enc_content = NULL; } if (content != NULL && content->data != NULL && content->len) { rv = sec_pkcs7_encoder_work_data (p7ecx, enc_content, content->data, content->len, PR_TRUE); if (rv != SECSuccess) goto loser; } rv = sec_pkcs7_encoder_sig_and_certs (cinfo, pwfn, pwfnarg);loser: PORT_Free (p7ecx); return rv;}/* * Encode a PKCS7 object, in one shot. All necessary components * of the object must already be specified. Either the data has * already been included (via SetContent), or the data is detached, * or there is no data at all (certs-only). * * "cinfo" specifies the object to be encoded. * * "outputfn" is where the encoded bytes will be passed. * * "outputarg" is an opaque argument to the above callback. * * "bulkkey" specifies the bulk encryption key to use. This argument * can be NULL if no encryption is being done, or if the bulk key should * be generated internally (usually the case for EnvelopedData but never * for EncryptedData, which *must* provide a bulk encryption key). * * "pwfn" is a callback for getting the password which protects the * private key of the signer. This argument can be NULL if it is known * that no signing is going to be done. * * "pwfnarg" is an opaque argument to the above callback. */SECStatusSEC_PKCS7Encode (SEC_PKCS7ContentInfo *cinfo, SEC_PKCS7EncoderOutputCallback outputfn, void *outputarg, PK11SymKey *bulkkey, SECKEYGetPasswordKey pwfn, void *pwfnarg){ SECStatus rv; rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg); if (rv == SECSuccess) { struct sec_pkcs7_encoder_output outputcx; outputcx.outputfn = outputfn; outputcx.outputarg = outputarg; rv = SEC_ASN1Encode (cinfo, sec_PKCS7ContentInfoTemplate, sec_pkcs7_encoder_out, &outputcx); } return rv;}/* * Encode a PKCS7 object, in one shot. All necessary components * of the object must already be specified. Either the data has * already been included (via SetContent), or the data is detached, * or there is no data at all (certs-only). The output, rather than * being passed to an output function as is done above, is all put * into a SECItem. * * "pool" specifies a pool from which to allocate the result. * It can be NULL, in which case memory is allocated generically. * * "dest" specifies a SECItem in which to put the result data. * It can be NULL, in which case the entire item is allocated, too. * * "cinfo" specifies the object to be encoded. * * "bulkkey" specifies the bulk encryption key to use. This argument * can be NULL if no encryption is being done, or if the bulk key should * be generated internally (usually the case for EnvelopedData but never * for EncryptedData, which *must* provide a bulk encryption key). * * "pwfn" is a callback for getting the password which protects the * private key of the signer. This argument can be NULL if it is known * that no signing is going to be done. * * "pwfnarg" is an opaque argument to the above callback. */SECItem *SEC_PKCS7EncodeItem (PRArenaPool *pool, SECItem *dest, SEC_PKCS7ContentInfo *cinfo, PK11SymKey *bulkkey, SECKEYGetPasswordKey pwfn, void *pwfnarg){ SECStatus rv; rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg); if (rv != SECSuccess) return NULL; return SEC_ASN1EncodeItem (pool, dest, cinfo, sec_PKCS7ContentInfoTemplate);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?