cmsenvdata.c
上传用户:lyxiangda
上传日期:2007-01-12
资源大小:3042k
文件大小:12k
源码类别:

CA认证

开发平台:

WINDOWS

  1. /*
  2.  * The contents of this file are subject to the Mozilla Public
  3.  * License Version 1.1 (the "License"); you may not use this file
  4.  * except in compliance with the License. You may obtain a copy of
  5.  * the License at http://www.mozilla.org/MPL/
  6.  * 
  7.  * Software distributed under the License is distributed on an "AS
  8.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9.  * implied. See the License for the specific language governing
  10.  * rights and limitations under the License.
  11.  * 
  12.  * The Original Code is the Netscape security libraries.
  13.  * 
  14.  * The Initial Developer of the Original Code is Netscape
  15.  * Communications Corporation.  Portions created by Netscape are 
  16.  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
  17.  * Rights Reserved.
  18.  * 
  19.  * Contributor(s):
  20.  * 
  21.  * Alternatively, the contents of this file may be used under the
  22.  * terms of the GNU General Public License Version 2 or later (the
  23.  * "GPL"), in which case the provisions of the GPL are applicable 
  24.  * instead of those above.  If you wish to allow use of your 
  25.  * version of this file only under the terms of the GPL and not to
  26.  * allow others to use your version of this file under the MPL,
  27.  * indicate your decision by deleting the provisions above and
  28.  * replace them with the notice and other provisions required by
  29.  * the GPL.  If you do not delete the provisions above, a recipient
  30.  * may use your version of this file under either the MPL or the
  31.  * GPL.
  32.  */
  33. /*
  34.  * CMS envelopedData methods.
  35.  *
  36.  * $Id: cmsenvdata.c,v 1.3 2000/10/06 23:26:10 nelsonb%netscape.com Exp $
  37.  */
  38. #include "cmslocal.h"
  39. #include "cert.h"
  40. #include "key.h"
  41. #include "secasn1.h"
  42. #include "secitem.h"
  43. #include "secoid.h"
  44. #include "pk11func.h"
  45. #include "secerr.h"
  46. /*
  47.  * NSS_CMSEnvelopedData_Create - create an enveloped data message
  48.  */
  49. NSSCMSEnvelopedData *
  50. NSS_CMSEnvelopedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize)
  51. {
  52.     void *mark;
  53.     NSSCMSEnvelopedData *envd;
  54.     PLArenaPool *poolp;
  55.     SECStatus rv;
  56.     poolp = cmsg->poolp;
  57.     mark = PORT_ArenaMark(poolp);
  58.     envd = (NSSCMSEnvelopedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSEnvelopedData));
  59.     if (envd == NULL)
  60. goto loser;
  61.     envd->cmsg = cmsg;
  62.     /* version is set in NSS_CMSEnvelopedData_Encode_BeforeStart() */
  63.     rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(envd->contentInfo), algorithm, NULL, keysize);
  64.     if (rv != SECSuccess)
  65. goto loser;
  66.     PORT_ArenaUnmark(poolp, mark);
  67.     return envd;
  68. loser:
  69.     PORT_ArenaRelease(poolp, mark);
  70.     return NULL;
  71. }
  72. /*
  73.  * NSS_CMSEnvelopedData_Destroy - destroy an enveloped data message
  74.  */
  75. void
  76. NSS_CMSEnvelopedData_Destroy(NSSCMSEnvelopedData *edp)
  77. {
  78.     NSSCMSRecipientInfo **recipientinfos;
  79.     NSSCMSRecipientInfo *ri;
  80.     if (edp == NULL)
  81. return;
  82.     recipientinfos = edp->recipientInfos;
  83.     if (recipientinfos == NULL)
  84. return;
  85.     while ((ri = *recipientinfos++) != NULL)
  86. NSS_CMSRecipientInfo_Destroy(ri);
  87.     /* XXX storage ??? */
  88. }
  89. /*
  90.  * NSS_CMSEnvelopedData_GetContentInfo - return pointer to this envelopedData's contentinfo
  91.  */
  92. NSSCMSContentInfo *
  93. NSS_CMSEnvelopedData_GetContentInfo(NSSCMSEnvelopedData *envd)
  94. {
  95.     return &(envd->contentInfo);
  96. }
  97. /*
  98.  * NSS_CMSEnvelopedData_AddRecipient - add a recipientinfo to the enveloped data msg
  99.  *
  100.  * rip must be created on the same pool as edp - this is not enforced, though.
  101.  */
  102. SECStatus
  103. NSS_CMSEnvelopedData_AddRecipient(NSSCMSEnvelopedData *edp, NSSCMSRecipientInfo *rip)
  104. {
  105.     void *mark;
  106.     SECStatus rv;
  107.     /* XXX compare pools, if not same, copy rip into edp's pool */
  108.     PR_ASSERT(edp != NULL);
  109.     PR_ASSERT(rip != NULL);
  110.     mark = PORT_ArenaMark(edp->cmsg->poolp);
  111.     rv = NSS_CMSArray_Add(edp->cmsg->poolp, (void ***)&(edp->recipientInfos), (void *)rip);
  112.     if (rv != SECSuccess) {
  113. PORT_ArenaRelease(edp->cmsg->poolp, mark);
  114. return SECFailure;
  115.     }
  116.     PORT_ArenaUnmark (edp->cmsg->poolp, mark);
  117.     return SECSuccess;
  118. }
  119. /*
  120.  * NSS_CMSEnvelopedData_Encode_BeforeStart - prepare this envelopedData for encoding
  121.  *
  122.  * at this point, we need
  123.  * - recipientinfos set up with recipient's certificates
  124.  * - a content encryption algorithm (if none, 3DES will be used)
  125.  *
  126.  * this function will generate a random content encryption key (aka bulk key),
  127.  * initialize the recipientinfos with certificate identification and wrap the bulk key
  128.  * using the proper algorithm for every certificiate.
  129.  * it will finally set the bulk algorithm and key so that the encode step can find it.
  130.  */
  131. SECStatus
  132. NSS_CMSEnvelopedData_Encode_BeforeStart(NSSCMSEnvelopedData *envd)
  133. {
  134.     int version;
  135.     NSSCMSRecipientInfo **recipientinfos;
  136.     NSSCMSContentInfo *cinfo;
  137.     PK11SymKey *bulkkey = NULL;
  138.     SECOidTag bulkalgtag;
  139.     CK_MECHANISM_TYPE type;
  140.     PK11SlotInfo *slot;
  141.     SECStatus rv;
  142.     SECItem *dummy;
  143.     PLArenaPool *poolp;
  144.     extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[];
  145.     void *mark = NULL;
  146.     int i;
  147.     poolp = envd->cmsg->poolp;
  148.     cinfo = &(envd->contentInfo);
  149.     recipientinfos = envd->recipientInfos;
  150.     if (recipientinfos == NULL) {
  151. PORT_SetError(SEC_ERROR_BAD_DATA);
  152. #if 0
  153. PORT_SetErrorString("Cannot find recipientinfos to encode.");
  154. #endif
  155. goto loser;
  156.     }
  157.     version = NSS_CMS_ENVELOPED_DATA_VERSION_REG;
  158.     if (envd->originatorInfo != NULL || envd->unprotectedAttr != NULL) {
  159. version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
  160.     } else {
  161. for (i = 0; recipientinfos[i] != NULL; i++) {
  162.     if (NSS_CMSRecipientInfo_GetVersion(recipientinfos[i]) != 0) {
  163. version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
  164. break;
  165.     }
  166. }
  167.     }
  168.     dummy = SEC_ASN1EncodeInteger(poolp, &(envd->version), version);
  169.     if (dummy == NULL)
  170. goto loser;
  171.     /* now we need to have a proper content encryption algorithm
  172.      * on the SMIME level, we would figure one out by looking at SMIME capabilities
  173.      * we cannot do that on our level, so if none is set already, we'll just go
  174.      * with one of the mandatory algorithms (3DES) */
  175.     if ((bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo)) == SEC_OID_UNKNOWN) {
  176. rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, cinfo, SEC_OID_DES_EDE3_CBC, NULL, 168);
  177. if (rv != SECSuccess)
  178.     goto loser;
  179. bulkalgtag = SEC_OID_DES_EDE3_CBC;
  180.     } 
  181.     /* generate a random bulk key suitable for content encryption alg */
  182.     type = PK11_AlgtagToMechanism(bulkalgtag);
  183.     slot = PK11_GetBestSlot(type, envd->cmsg->pwfn_arg);
  184.     if (slot == NULL)
  185. goto loser; /* error has been set by PK11_GetBestSlot */
  186.     /* this is expensive... */
  187.     bulkkey = PK11_KeyGen(slot, type, NULL, NSS_CMSContentInfo_GetBulkKeySize(cinfo) / 8, envd->cmsg->pwfn_arg);
  188.     PK11_FreeSlot(slot);
  189.     if (bulkkey == NULL)
  190. goto loser; /* error has been set by PK11_KeyGen */
  191.     mark = PORT_ArenaMark(poolp);
  192.     /* Encrypt the bulk key with the public key of each recipient.  */
  193.     for (i = 0; recipientinfos[i] != NULL; i++) {
  194. rv = NSS_CMSRecipientInfo_WrapBulkKey(recipientinfos[i], bulkkey, bulkalgtag);
  195. if (rv != SECSuccess)
  196.     goto loser; /* error has been set by NSS_CMSRecipientInfo_EncryptBulkKey */
  197.      /* could be: alg not supported etc. */
  198.     }
  199.     /* the recipientinfos are all finished. now sort them by DER for SET OF encoding */
  200.     rv = NSS_CMSArray_SortByDER((void **)envd->recipientInfos, NSSCMSRecipientInfoTemplate, NULL);
  201.     if (rv != SECSuccess)
  202. goto loser; /* error has been set by NSS_CMSArray_SortByDER */
  203.     /* store the bulk key in the contentInfo so that the encoder can find it */
  204.     NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
  205.     PORT_ArenaUnmark(poolp, mark);
  206.     PK11_FreeSymKey(bulkkey);
  207.     return SECSuccess;
  208. loser:
  209.     if (mark != NULL)
  210. PORT_ArenaRelease (poolp, mark);
  211.     if (bulkkey)
  212. PK11_FreeSymKey(bulkkey);
  213.     return SECFailure;
  214. }
  215. /*
  216.  * NSS_CMSEnvelopedData_Encode_BeforeData - set up encryption
  217.  *
  218.  * it is essential that this is called before the contentEncAlg is encoded, because
  219.  * setting up the encryption may generate IVs and thus change it!
  220.  */
  221. SECStatus
  222. NSS_CMSEnvelopedData_Encode_BeforeData(NSSCMSEnvelopedData *envd)
  223. {
  224.     NSSCMSContentInfo *cinfo;
  225.     PK11SymKey *bulkkey;
  226.     SECAlgorithmID *algid;
  227.     cinfo = &(envd->contentInfo);
  228.     /* find bulkkey and algorithm - must have been set by NSS_CMSEnvelopedData_Encode_BeforeStart */
  229.     bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo);
  230.     if (bulkkey == NULL)
  231. return SECFailure;
  232.     algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
  233.     if (algid == NULL)
  234. return SECFailure;
  235.     /* this may modify algid (with IVs generated in a token).
  236.      * it is essential that algid is a pointer to the contentEncAlg data, not a
  237.      * pointer to a copy! */
  238.     cinfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(envd->cmsg->poolp, bulkkey, algid);
  239.     PK11_FreeSymKey(bulkkey);
  240.     if (cinfo->ciphcx == NULL)
  241. return SECFailure;
  242.     return SECSuccess;
  243. }
  244. /*
  245.  * NSS_CMSEnvelopedData_Encode_AfterData - finalize this envelopedData for encoding
  246.  */
  247. SECStatus
  248. NSS_CMSEnvelopedData_Encode_AfterData(NSSCMSEnvelopedData *envd)
  249. {
  250.     if (envd->contentInfo.ciphcx) {
  251. NSS_CMSCipherContext_Destroy(envd->contentInfo.ciphcx);
  252. envd->contentInfo.ciphcx = NULL;
  253.     }
  254.     /* nothing else to do after data */
  255.     return SECSuccess;
  256. }
  257. /*
  258.  * NSS_CMSEnvelopedData_Decode_BeforeData - find our recipientinfo, 
  259.  * derive bulk key & set up our contentinfo
  260.  */
  261. SECStatus
  262. NSS_CMSEnvelopedData_Decode_BeforeData(NSSCMSEnvelopedData *envd)
  263. {
  264.     NSSCMSRecipientInfo *ri;
  265.     PK11SymKey *bulkkey = NULL;
  266.     SECOidTag bulkalgtag;
  267.     SECAlgorithmID *bulkalg;
  268.     SECStatus rv = SECFailure;
  269.     NSSCMSContentInfo *cinfo;
  270.     NSSCMSRecipient **recipient_list;
  271.     NSSCMSRecipient *recipient;
  272.     int rlIndex;
  273.     if (NSS_CMSArray_Count((void **)envd->recipientInfos) == 0) {
  274. PORT_SetError(SEC_ERROR_BAD_DATA);
  275. #if 0
  276. PORT_SetErrorString("No recipient data in envelope.");
  277. #endif
  278. goto loser;
  279.     }
  280.     /* look if one of OUR cert's issuerSN is on the list of recipients, and if so,  */
  281.     /* get the cert and private key for it right away */
  282.     recipient_list = nss_cms_recipient_list_create(envd->recipientInfos);
  283.     if (recipient_list == NULL)
  284. goto loser;
  285.     /* what about multiple recipientInfos that match?
  286.      * especially if, for some reason, we could not produce a bulk key with the first match?!
  287.      * we could loop & feed partial recipient_list to PK11_FindCertAndKeyByRecipientList...
  288.      * maybe later... */
  289.     rlIndex = PK11_FindCertAndKeyByRecipientListNew(recipient_list, envd->cmsg->pwfn_arg);
  290.     /* if that fails, then we're not an intended recipient and cannot decrypt */
  291.     if (rlIndex < 0) {
  292. PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT);
  293. #if 0
  294. PORT_SetErrorString("Cannot decrypt data because proper key cannot be found.");
  295. #endif
  296. goto loser;
  297.     }
  298.     recipient = recipient_list[rlIndex];
  299.     if (!recipient->cert || !recipient->privkey) {
  300. /* XXX should set an error code ?!? */
  301. goto loser;
  302.     }
  303.     /* get a pointer to "our" recipientinfo */
  304.     ri = envd->recipientInfos[recipient->riIndex];
  305.     cinfo = &(envd->contentInfo);
  306.     bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo);
  307.     bulkkey = NSS_CMSRecipientInfo_UnwrapBulkKey(ri,recipient->subIndex,
  308.     recipient->cert,
  309.     recipient->privkey,
  310.     bulkalgtag);
  311.     if (bulkkey == NULL) {
  312. /* no success finding a bulk key */
  313. goto loser;
  314.     }
  315.     NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
  316.     bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
  317.     cinfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg);
  318.     if (cinfo->ciphcx == NULL)
  319. goto loser; /* error has been set by NSS_CMSCipherContext_StartDecrypt */
  320.     /* 
  321.      * HACK ALERT!!
  322.      * For PKCS5 Encryption Algorithms, the bulkkey is actually a different
  323.      * structure.  Therefore, we need to set the bulkkey to the actual key 
  324.      * prior to freeing it.
  325.      */
  326.     if (SEC_PKCS5IsAlgorithmPBEAlg(bulkalg)) {
  327. SEC_PKCS5KeyAndPassword *keyPwd = (SEC_PKCS5KeyAndPassword *)bulkkey;
  328. bulkkey = keyPwd->key;
  329.     }
  330.     rv = SECSuccess;
  331. loser:
  332.     if (bulkkey)
  333. PK11_FreeSymKey(bulkkey);
  334.     if (recipient_list != NULL)
  335. nss_cms_recipient_list_destroy(recipient_list);
  336.     return rv;
  337. }
  338. /*
  339.  * NSS_CMSEnvelopedData_Decode_AfterData - finish decrypting this envelopedData's content
  340.  */
  341. SECStatus
  342. NSS_CMSEnvelopedData_Decode_AfterData(NSSCMSEnvelopedData *envd)
  343. {
  344.     if (envd->contentInfo.ciphcx) {
  345. NSS_CMSCipherContext_Destroy(envd->contentInfo.ciphcx);
  346. envd->contentInfo.ciphcx = NULL;
  347.     }
  348.     return SECSuccess;
  349. }
  350. /*
  351.  * NSS_CMSEnvelopedData_Decode_AfterEnd - finish decoding this envelopedData
  352.  */
  353. SECStatus
  354. NSS_CMSEnvelopedData_Decode_AfterEnd(NSSCMSEnvelopedData *envd)
  355. {
  356.     /* apply final touches */
  357.     return SECSuccess;
  358. }