|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "p12t.h" |
|
6 #include "p12.h" |
|
7 #include "plarena.h" |
|
8 #include "secitem.h" |
|
9 #include "secoid.h" |
|
10 #include "seccomon.h" |
|
11 #include "secport.h" |
|
12 #include "cert.h" |
|
13 #include "secpkcs7.h" |
|
14 #include "secasn1.h" |
|
15 #include "secerr.h" |
|
16 #include "pk11func.h" |
|
17 #include "p12plcy.h" |
|
18 #include "p12local.h" |
|
19 #include "prcpucfg.h" |
|
20 |
|
21 extern const int NSS_PBE_DEFAULT_ITERATION_COUNT; /* defined in p7create.c */ |
|
22 |
|
23 /* |
|
24 ** This PKCS12 file encoder uses numerous nested ASN.1 and PKCS7 encoder |
|
25 ** contexts. It can be difficult to keep straight. Here's a picture: |
|
26 ** |
|
27 ** "outer" ASN.1 encoder. The output goes to the library caller's CB. |
|
28 ** "middle" PKCS7 encoder. Feeds the "outer" ASN.1 encoder. |
|
29 ** "middle" ASN1 encoder. Encodes the encrypted aSafes. |
|
30 ** Feeds the "middle" P7 encoder above. |
|
31 ** "inner" PKCS7 encoder. Encrypts the "authenticated Safes" (aSafes) |
|
32 ** Feeds the "middle" ASN.1 encoder above. |
|
33 ** "inner" ASN.1 encoder. Encodes the unencrypted aSafes. |
|
34 ** Feeds the "inner" P7 enocder above. |
|
35 ** |
|
36 ** Buffering has been added at each point where the output of an ASN.1 |
|
37 ** encoder feeds the input of a PKCS7 encoder. |
|
38 */ |
|
39 |
|
40 /********************************* |
|
41 * Output buffer object, used to buffer output from ASN.1 encoder |
|
42 * before passing data on down to the next PKCS7 encoder. |
|
43 *********************************/ |
|
44 |
|
45 #define PK12_OUTPUT_BUFFER_SIZE 8192 |
|
46 |
|
47 struct sec_pkcs12OutputBufferStr { |
|
48 SEC_PKCS7EncoderContext * p7eCx; |
|
49 PK11Context * hmacCx; |
|
50 unsigned int numBytes; |
|
51 unsigned int bufBytes; |
|
52 char buf[PK12_OUTPUT_BUFFER_SIZE]; |
|
53 }; |
|
54 typedef struct sec_pkcs12OutputBufferStr sec_pkcs12OutputBuffer; |
|
55 |
|
56 /********************************* |
|
57 * Structures used in exporting the PKCS 12 blob |
|
58 *********************************/ |
|
59 |
|
60 /* A SafeInfo is used for each ContentInfo which makes up the |
|
61 * sequence of safes in the AuthenticatedSafe portion of the |
|
62 * PFX structure. |
|
63 */ |
|
64 struct SEC_PKCS12SafeInfoStr { |
|
65 PLArenaPool *arena; |
|
66 |
|
67 /* information for setting up password encryption */ |
|
68 SECItem pwitem; |
|
69 SECOidTag algorithm; |
|
70 PK11SymKey *encryptionKey; |
|
71 |
|
72 /* how many items have been stored in this safe, |
|
73 * we will skip any safe which does not contain any |
|
74 * items |
|
75 */ |
|
76 unsigned int itemCount; |
|
77 |
|
78 /* the content info for the safe */ |
|
79 SEC_PKCS7ContentInfo *cinfo; |
|
80 |
|
81 sec_PKCS12SafeContents *safe; |
|
82 }; |
|
83 |
|
84 /* An opaque structure which contains information needed for exporting |
|
85 * certificates and keys through PKCS 12. |
|
86 */ |
|
87 struct SEC_PKCS12ExportContextStr { |
|
88 PLArenaPool *arena; |
|
89 PK11SlotInfo *slot; |
|
90 void *wincx; |
|
91 |
|
92 /* integrity information */ |
|
93 PRBool integrityEnabled; |
|
94 PRBool pwdIntegrity; |
|
95 union { |
|
96 struct sec_PKCS12PasswordModeInfo pwdInfo; |
|
97 struct sec_PKCS12PublicKeyModeInfo pubkeyInfo; |
|
98 } integrityInfo; |
|
99 |
|
100 /* helper functions */ |
|
101 /* retrieve the password call back */ |
|
102 SECKEYGetPasswordKey pwfn; |
|
103 void *pwfnarg; |
|
104 |
|
105 /* safe contents bags */ |
|
106 SEC_PKCS12SafeInfo **safeInfos; |
|
107 unsigned int safeInfoCount; |
|
108 |
|
109 /* the sequence of safes */ |
|
110 sec_PKCS12AuthenticatedSafe authSafe; |
|
111 |
|
112 /* information needing deletion */ |
|
113 CERTCertificate **certList; |
|
114 }; |
|
115 |
|
116 /* structures for passing information to encoder callbacks when processing |
|
117 * data through the ASN1 engine. |
|
118 */ |
|
119 struct sec_pkcs12_encoder_output { |
|
120 SEC_PKCS12EncoderOutputCallback outputfn; |
|
121 void *outputarg; |
|
122 }; |
|
123 |
|
124 struct sec_pkcs12_hmac_and_output_info { |
|
125 void *arg; |
|
126 struct sec_pkcs12_encoder_output output; |
|
127 }; |
|
128 |
|
129 /* An encoder context which is used for the actual encoding |
|
130 * portion of PKCS 12. |
|
131 */ |
|
132 typedef struct sec_PKCS12EncoderContextStr { |
|
133 PLArenaPool *arena; |
|
134 SEC_PKCS12ExportContext *p12exp; |
|
135 |
|
136 /* encoder information - this is set up based on whether |
|
137 * password based or public key pased privacy is being used |
|
138 */ |
|
139 SEC_ASN1EncoderContext *outerA1ecx; |
|
140 union { |
|
141 struct sec_pkcs12_hmac_and_output_info hmacAndOutputInfo; |
|
142 struct sec_pkcs12_encoder_output encOutput; |
|
143 } output; |
|
144 |
|
145 /* structures for encoding of PFX and MAC */ |
|
146 sec_PKCS12PFXItem pfx; |
|
147 sec_PKCS12MacData mac; |
|
148 |
|
149 /* authenticated safe encoding tracking information */ |
|
150 SEC_PKCS7ContentInfo *aSafeCinfo; |
|
151 SEC_PKCS7EncoderContext *middleP7ecx; |
|
152 SEC_ASN1EncoderContext *middleA1ecx; |
|
153 unsigned int currentSafe; |
|
154 |
|
155 /* hmac context */ |
|
156 PK11Context *hmacCx; |
|
157 |
|
158 /* output buffers */ |
|
159 sec_pkcs12OutputBuffer middleBuf; |
|
160 sec_pkcs12OutputBuffer innerBuf; |
|
161 |
|
162 } sec_PKCS12EncoderContext; |
|
163 |
|
164 |
|
165 /********************************* |
|
166 * Export setup routines |
|
167 *********************************/ |
|
168 |
|
169 /* SEC_PKCS12CreateExportContext |
|
170 * Creates an export context and sets the unicode and password retrieval |
|
171 * callbacks. This is the first call which must be made when exporting |
|
172 * a PKCS 12 blob. |
|
173 * |
|
174 * pwfn, pwfnarg - password retrieval callback and argument. these are |
|
175 * required for password-authentication mode. |
|
176 */ |
|
177 SEC_PKCS12ExportContext * |
|
178 SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg, |
|
179 PK11SlotInfo *slot, void *wincx) |
|
180 { |
|
181 PLArenaPool *arena = NULL; |
|
182 SEC_PKCS12ExportContext *p12ctxt = NULL; |
|
183 |
|
184 /* allocate the arena and create the context */ |
|
185 arena = PORT_NewArena(4096); |
|
186 if(!arena) { |
|
187 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
188 return NULL; |
|
189 } |
|
190 |
|
191 p12ctxt = (SEC_PKCS12ExportContext *)PORT_ArenaZAlloc(arena, |
|
192 sizeof(SEC_PKCS12ExportContext)); |
|
193 if(!p12ctxt) { |
|
194 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
195 goto loser; |
|
196 } |
|
197 |
|
198 /* password callback for key retrieval */ |
|
199 p12ctxt->pwfn = pwfn; |
|
200 p12ctxt->pwfnarg = pwfnarg; |
|
201 |
|
202 p12ctxt->integrityEnabled = PR_FALSE; |
|
203 p12ctxt->arena = arena; |
|
204 p12ctxt->wincx = wincx; |
|
205 p12ctxt->slot = (slot) ? PK11_ReferenceSlot(slot) : PK11_GetInternalSlot(); |
|
206 |
|
207 return p12ctxt; |
|
208 |
|
209 loser: |
|
210 if(arena) { |
|
211 PORT_FreeArena(arena, PR_TRUE); |
|
212 } |
|
213 |
|
214 return NULL; |
|
215 } |
|
216 |
|
217 /* |
|
218 * Adding integrity mode |
|
219 */ |
|
220 |
|
221 /* SEC_PKCS12AddPasswordIntegrity |
|
222 * Add password integrity to the exported data. If an integrity method |
|
223 * has already been set, then return an error. |
|
224 * |
|
225 * p12ctxt - the export context |
|
226 * pwitem - the password for integrity mode |
|
227 * integAlg - the integrity algorithm to use for authentication. |
|
228 */ |
|
229 SECStatus |
|
230 SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt, |
|
231 SECItem *pwitem, SECOidTag integAlg) |
|
232 { |
|
233 if(!p12ctxt || p12ctxt->integrityEnabled) { |
|
234 return SECFailure; |
|
235 } |
|
236 |
|
237 /* set up integrity information */ |
|
238 p12ctxt->pwdIntegrity = PR_TRUE; |
|
239 p12ctxt->integrityInfo.pwdInfo.password = |
|
240 (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); |
|
241 if(!p12ctxt->integrityInfo.pwdInfo.password) { |
|
242 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
243 return SECFailure; |
|
244 } |
|
245 if(SECITEM_CopyItem(p12ctxt->arena, |
|
246 p12ctxt->integrityInfo.pwdInfo.password, pwitem) |
|
247 != SECSuccess) { |
|
248 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
249 return SECFailure; |
|
250 } |
|
251 p12ctxt->integrityInfo.pwdInfo.algorithm = integAlg; |
|
252 p12ctxt->integrityEnabled = PR_TRUE; |
|
253 |
|
254 return SECSuccess; |
|
255 } |
|
256 |
|
257 /* SEC_PKCS12AddPublicKeyIntegrity |
|
258 * Add public key integrity to the exported data. If an integrity method |
|
259 * has already been set, then return an error. The certificate must be |
|
260 * allowed to be used as a signing cert. |
|
261 * |
|
262 * p12ctxt - the export context |
|
263 * cert - signer certificate |
|
264 * certDb - the certificate database |
|
265 * algorithm - signing algorithm |
|
266 * keySize - size of the signing key (?) |
|
267 */ |
|
268 SECStatus |
|
269 SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt, |
|
270 CERTCertificate *cert, CERTCertDBHandle *certDb, |
|
271 SECOidTag algorithm, int keySize) |
|
272 { |
|
273 if(!p12ctxt) { |
|
274 return SECFailure; |
|
275 } |
|
276 |
|
277 p12ctxt->integrityInfo.pubkeyInfo.cert = cert; |
|
278 p12ctxt->integrityInfo.pubkeyInfo.certDb = certDb; |
|
279 p12ctxt->integrityInfo.pubkeyInfo.algorithm = algorithm; |
|
280 p12ctxt->integrityInfo.pubkeyInfo.keySize = keySize; |
|
281 p12ctxt->integrityEnabled = PR_TRUE; |
|
282 |
|
283 return SECSuccess; |
|
284 } |
|
285 |
|
286 |
|
287 /* |
|
288 * Adding safes - encrypted (password/public key) or unencrypted |
|
289 * Each of the safe creation routines return an opaque pointer which |
|
290 * are later passed into the routines for exporting certificates and |
|
291 * keys. |
|
292 */ |
|
293 |
|
294 /* append the newly created safeInfo to list of safeInfos in the export |
|
295 * context. |
|
296 */ |
|
297 static SECStatus |
|
298 sec_pkcs12_append_safe_info(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *info) |
|
299 { |
|
300 void *mark = NULL, *dummy1 = NULL, *dummy2 = NULL; |
|
301 |
|
302 if(!p12ctxt || !info) { |
|
303 return SECFailure; |
|
304 } |
|
305 |
|
306 mark = PORT_ArenaMark(p12ctxt->arena); |
|
307 |
|
308 /* if no safeInfos have been set, create the list, otherwise expand it. */ |
|
309 if(!p12ctxt->safeInfoCount) { |
|
310 p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)PORT_ArenaZAlloc(p12ctxt->arena, |
|
311 2 * sizeof(SEC_PKCS12SafeInfo *)); |
|
312 dummy1 = p12ctxt->safeInfos; |
|
313 p12ctxt->authSafe.encodedSafes = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, |
|
314 2 * sizeof(SECItem *)); |
|
315 dummy2 = p12ctxt->authSafe.encodedSafes; |
|
316 } else { |
|
317 dummy1 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->safeInfos, |
|
318 (p12ctxt->safeInfoCount + 1) * sizeof(SEC_PKCS12SafeInfo *), |
|
319 (p12ctxt->safeInfoCount + 2) * sizeof(SEC_PKCS12SafeInfo *)); |
|
320 p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)dummy1; |
|
321 dummy2 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->authSafe.encodedSafes, |
|
322 (p12ctxt->authSafe.safeCount + 1) * sizeof(SECItem *), |
|
323 (p12ctxt->authSafe.safeCount + 2) * sizeof(SECItem *)); |
|
324 p12ctxt->authSafe.encodedSafes = (SECItem**)dummy2; |
|
325 } |
|
326 if(!dummy1 || !dummy2) { |
|
327 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
328 goto loser; |
|
329 } |
|
330 |
|
331 /* append the new safeInfo and null terminate the list */ |
|
332 p12ctxt->safeInfos[p12ctxt->safeInfoCount] = info; |
|
333 p12ctxt->safeInfos[++p12ctxt->safeInfoCount] = NULL; |
|
334 p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount] = |
|
335 (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); |
|
336 if(!p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount]) { |
|
337 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
338 goto loser; |
|
339 } |
|
340 p12ctxt->authSafe.encodedSafes[++p12ctxt->authSafe.safeCount] = NULL; |
|
341 |
|
342 PORT_ArenaUnmark(p12ctxt->arena, mark); |
|
343 return SECSuccess; |
|
344 |
|
345 loser: |
|
346 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
347 return SECFailure; |
|
348 } |
|
349 |
|
350 /* SEC_PKCS12CreatePasswordPrivSafe |
|
351 * Create a password privacy safe to store exported information in. |
|
352 * |
|
353 * p12ctxt - export context |
|
354 * pwitem - password for encryption |
|
355 * privAlg - pbe algorithm through which encryption is done. |
|
356 */ |
|
357 SEC_PKCS12SafeInfo * |
|
358 SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt, |
|
359 SECItem *pwitem, SECOidTag privAlg) |
|
360 { |
|
361 SEC_PKCS12SafeInfo *safeInfo = NULL; |
|
362 void *mark = NULL; |
|
363 PK11SlotInfo *slot = NULL; |
|
364 SECAlgorithmID *algId; |
|
365 SECItem uniPwitem = {siBuffer, NULL, 0}; |
|
366 |
|
367 if(!p12ctxt) { |
|
368 return NULL; |
|
369 } |
|
370 |
|
371 /* allocate the safe info */ |
|
372 mark = PORT_ArenaMark(p12ctxt->arena); |
|
373 safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, |
|
374 sizeof(SEC_PKCS12SafeInfo)); |
|
375 if(!safeInfo) { |
|
376 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
377 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
378 return NULL; |
|
379 } |
|
380 |
|
381 safeInfo->itemCount = 0; |
|
382 |
|
383 /* create the encrypted safe */ |
|
384 safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn, |
|
385 p12ctxt->pwfnarg); |
|
386 if(!safeInfo->cinfo) { |
|
387 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
388 goto loser; |
|
389 } |
|
390 safeInfo->arena = p12ctxt->arena; |
|
391 |
|
392 /* convert the password to unicode */ |
|
393 if(!sec_pkcs12_convert_item_to_unicode(NULL, &uniPwitem, pwitem, |
|
394 PR_TRUE, PR_TRUE, PR_TRUE)) { |
|
395 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
396 goto loser; |
|
397 } |
|
398 if(SECITEM_CopyItem(p12ctxt->arena, &safeInfo->pwitem, &uniPwitem) != SECSuccess) { |
|
399 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
400 goto loser; |
|
401 } |
|
402 |
|
403 /* generate the encryption key */ |
|
404 slot = PK11_ReferenceSlot(p12ctxt->slot); |
|
405 if(!slot) { |
|
406 slot = PK11_GetInternalKeySlot(); |
|
407 if(!slot) { |
|
408 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
409 goto loser; |
|
410 } |
|
411 } |
|
412 |
|
413 algId = SEC_PKCS7GetEncryptionAlgorithm(safeInfo->cinfo); |
|
414 safeInfo->encryptionKey = PK11_PBEKeyGen(slot, algId, &uniPwitem, |
|
415 PR_FALSE, p12ctxt->wincx); |
|
416 if(!safeInfo->encryptionKey) { |
|
417 goto loser; |
|
418 } |
|
419 |
|
420 safeInfo->arena = p12ctxt->arena; |
|
421 safeInfo->safe = NULL; |
|
422 if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { |
|
423 goto loser; |
|
424 } |
|
425 |
|
426 if(uniPwitem.data) { |
|
427 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); |
|
428 } |
|
429 PORT_ArenaUnmark(p12ctxt->arena, mark); |
|
430 |
|
431 if (slot) { |
|
432 PK11_FreeSlot(slot); |
|
433 } |
|
434 return safeInfo; |
|
435 |
|
436 loser: |
|
437 if (slot) { |
|
438 PK11_FreeSlot(slot); |
|
439 } |
|
440 if(safeInfo->cinfo) { |
|
441 SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); |
|
442 } |
|
443 |
|
444 if(uniPwitem.data) { |
|
445 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); |
|
446 } |
|
447 |
|
448 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
449 return NULL; |
|
450 } |
|
451 |
|
452 /* SEC_PKCS12CreateUnencryptedSafe |
|
453 * Creates an unencrypted safe within the export context. |
|
454 * |
|
455 * p12ctxt - the export context |
|
456 */ |
|
457 SEC_PKCS12SafeInfo * |
|
458 SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt) |
|
459 { |
|
460 SEC_PKCS12SafeInfo *safeInfo = NULL; |
|
461 void *mark = NULL; |
|
462 |
|
463 if(!p12ctxt) { |
|
464 return NULL; |
|
465 } |
|
466 |
|
467 /* create the safe info */ |
|
468 mark = PORT_ArenaMark(p12ctxt->arena); |
|
469 safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, |
|
470 sizeof(SEC_PKCS12SafeInfo)); |
|
471 if(!safeInfo) { |
|
472 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
473 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
474 return NULL; |
|
475 } |
|
476 |
|
477 safeInfo->itemCount = 0; |
|
478 |
|
479 /* create the safe content */ |
|
480 safeInfo->cinfo = SEC_PKCS7CreateData(); |
|
481 if(!safeInfo->cinfo) { |
|
482 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
483 goto loser; |
|
484 } |
|
485 |
|
486 if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { |
|
487 goto loser; |
|
488 } |
|
489 |
|
490 PORT_ArenaUnmark(p12ctxt->arena, mark); |
|
491 return safeInfo; |
|
492 |
|
493 loser: |
|
494 if(safeInfo->cinfo) { |
|
495 SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); |
|
496 } |
|
497 |
|
498 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
499 return NULL; |
|
500 } |
|
501 |
|
502 /* SEC_PKCS12CreatePubKeyEncryptedSafe |
|
503 * Creates a safe which is protected by public key encryption. |
|
504 * |
|
505 * p12ctxt - the export context |
|
506 * certDb - the certificate database |
|
507 * signer - the signer's certificate |
|
508 * recipients - the list of recipient certificates. |
|
509 * algorithm - the encryption algorithm to use |
|
510 * keysize - the algorithms key size (?) |
|
511 */ |
|
512 SEC_PKCS12SafeInfo * |
|
513 SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt, |
|
514 CERTCertDBHandle *certDb, |
|
515 CERTCertificate *signer, |
|
516 CERTCertificate **recipients, |
|
517 SECOidTag algorithm, int keysize) |
|
518 { |
|
519 SEC_PKCS12SafeInfo *safeInfo = NULL; |
|
520 void *mark = NULL; |
|
521 |
|
522 if(!p12ctxt || !signer || !recipients || !(*recipients)) { |
|
523 return NULL; |
|
524 } |
|
525 |
|
526 /* allocate the safeInfo */ |
|
527 mark = PORT_ArenaMark(p12ctxt->arena); |
|
528 safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, |
|
529 sizeof(SEC_PKCS12SafeInfo)); |
|
530 if(!safeInfo) { |
|
531 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
532 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
533 return NULL; |
|
534 } |
|
535 |
|
536 safeInfo->itemCount = 0; |
|
537 safeInfo->arena = p12ctxt->arena; |
|
538 |
|
539 /* create the enveloped content info using certUsageEmailSigner currently. |
|
540 * XXX We need to eventually use something other than certUsageEmailSigner |
|
541 */ |
|
542 safeInfo->cinfo = SEC_PKCS7CreateEnvelopedData(signer, certUsageEmailSigner, |
|
543 certDb, algorithm, keysize, |
|
544 p12ctxt->pwfn, p12ctxt->pwfnarg); |
|
545 if(!safeInfo->cinfo) { |
|
546 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
547 goto loser; |
|
548 } |
|
549 |
|
550 /* add recipients */ |
|
551 if(recipients) { |
|
552 unsigned int i = 0; |
|
553 while(recipients[i] != NULL) { |
|
554 SECStatus rv = SEC_PKCS7AddRecipient(safeInfo->cinfo, recipients[i], |
|
555 certUsageEmailRecipient, certDb); |
|
556 if(rv != SECSuccess) { |
|
557 goto loser; |
|
558 } |
|
559 i++; |
|
560 } |
|
561 } |
|
562 |
|
563 if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { |
|
564 goto loser; |
|
565 } |
|
566 |
|
567 PORT_ArenaUnmark(p12ctxt->arena, mark); |
|
568 return safeInfo; |
|
569 |
|
570 loser: |
|
571 if(safeInfo->cinfo) { |
|
572 SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); |
|
573 safeInfo->cinfo = NULL; |
|
574 } |
|
575 |
|
576 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
577 return NULL; |
|
578 } |
|
579 |
|
580 /********************************* |
|
581 * Routines to handle the exporting of the keys and certificates |
|
582 *********************************/ |
|
583 |
|
584 /* creates a safe contents which safeBags will be appended to */ |
|
585 sec_PKCS12SafeContents * |
|
586 sec_PKCS12CreateSafeContents(PLArenaPool *arena) |
|
587 { |
|
588 sec_PKCS12SafeContents *safeContents; |
|
589 |
|
590 if(arena == NULL) { |
|
591 return NULL; |
|
592 } |
|
593 |
|
594 /* create the safe contents */ |
|
595 safeContents = (sec_PKCS12SafeContents *)PORT_ArenaZAlloc(arena, |
|
596 sizeof(sec_PKCS12SafeContents)); |
|
597 if(!safeContents) { |
|
598 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
599 goto loser; |
|
600 } |
|
601 |
|
602 /* set up the internal contents info */ |
|
603 safeContents->safeBags = NULL; |
|
604 safeContents->arena = arena; |
|
605 safeContents->bagCount = 0; |
|
606 |
|
607 return safeContents; |
|
608 |
|
609 loser: |
|
610 return NULL; |
|
611 } |
|
612 |
|
613 /* appends a safe bag to a safeContents using the specified arena. |
|
614 */ |
|
615 SECStatus |
|
616 sec_pkcs12_append_bag_to_safe_contents(PLArenaPool *arena, |
|
617 sec_PKCS12SafeContents *safeContents, |
|
618 sec_PKCS12SafeBag *safeBag) |
|
619 { |
|
620 void *mark = NULL, *dummy = NULL; |
|
621 |
|
622 if(!arena || !safeBag || !safeContents) { |
|
623 return SECFailure; |
|
624 } |
|
625 |
|
626 mark = PORT_ArenaMark(arena); |
|
627 if(!mark) { |
|
628 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
629 return SECFailure; |
|
630 } |
|
631 |
|
632 /* allocate space for the list, or reallocate to increase space */ |
|
633 if(!safeContents->safeBags) { |
|
634 safeContents->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(arena, |
|
635 (2 * sizeof(sec_PKCS12SafeBag *))); |
|
636 dummy = safeContents->safeBags; |
|
637 safeContents->bagCount = 0; |
|
638 } else { |
|
639 dummy = PORT_ArenaGrow(arena, safeContents->safeBags, |
|
640 (safeContents->bagCount + 1) * sizeof(sec_PKCS12SafeBag *), |
|
641 (safeContents->bagCount + 2) * sizeof(sec_PKCS12SafeBag *)); |
|
642 safeContents->safeBags = (sec_PKCS12SafeBag **)dummy; |
|
643 } |
|
644 |
|
645 if(!dummy) { |
|
646 PORT_ArenaRelease(arena, mark); |
|
647 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
648 return SECFailure; |
|
649 } |
|
650 |
|
651 /* append the bag at the end and null terminate the list */ |
|
652 safeContents->safeBags[safeContents->bagCount++] = safeBag; |
|
653 safeContents->safeBags[safeContents->bagCount] = NULL; |
|
654 |
|
655 PORT_ArenaUnmark(arena, mark); |
|
656 |
|
657 return SECSuccess; |
|
658 } |
|
659 |
|
660 /* appends a safeBag to a specific safeInfo. |
|
661 */ |
|
662 SECStatus |
|
663 sec_pkcs12_append_bag(SEC_PKCS12ExportContext *p12ctxt, |
|
664 SEC_PKCS12SafeInfo *safeInfo, sec_PKCS12SafeBag *safeBag) |
|
665 { |
|
666 sec_PKCS12SafeContents *dest; |
|
667 SECStatus rv = SECFailure; |
|
668 |
|
669 if(!p12ctxt || !safeBag || !safeInfo) { |
|
670 return SECFailure; |
|
671 } |
|
672 |
|
673 if(!safeInfo->safe) { |
|
674 safeInfo->safe = sec_PKCS12CreateSafeContents(p12ctxt->arena); |
|
675 if(!safeInfo->safe) { |
|
676 return SECFailure; |
|
677 } |
|
678 } |
|
679 |
|
680 dest = safeInfo->safe; |
|
681 rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, dest, safeBag); |
|
682 if(rv == SECSuccess) { |
|
683 safeInfo->itemCount++; |
|
684 } |
|
685 |
|
686 return rv; |
|
687 } |
|
688 |
|
689 /* Creates a safeBag of the specified type, and if bagData is specified, |
|
690 * the contents are set. The contents could be set later by the calling |
|
691 * routine. |
|
692 */ |
|
693 sec_PKCS12SafeBag * |
|
694 sec_PKCS12CreateSafeBag(SEC_PKCS12ExportContext *p12ctxt, SECOidTag bagType, |
|
695 void *bagData) |
|
696 { |
|
697 sec_PKCS12SafeBag *safeBag; |
|
698 PRBool setName = PR_TRUE; |
|
699 void *mark = NULL; |
|
700 SECStatus rv = SECSuccess; |
|
701 SECOidData *oidData = NULL; |
|
702 |
|
703 if(!p12ctxt) { |
|
704 return NULL; |
|
705 } |
|
706 |
|
707 mark = PORT_ArenaMark(p12ctxt->arena); |
|
708 if(!mark) { |
|
709 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
710 return NULL; |
|
711 } |
|
712 |
|
713 safeBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12ctxt->arena, |
|
714 sizeof(sec_PKCS12SafeBag)); |
|
715 if(!safeBag) { |
|
716 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
717 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
718 return NULL; |
|
719 } |
|
720 |
|
721 /* set the bags content based upon bag type */ |
|
722 switch(bagType) { |
|
723 case SEC_OID_PKCS12_V1_KEY_BAG_ID: |
|
724 safeBag->safeBagContent.pkcs8KeyBag = |
|
725 (SECKEYPrivateKeyInfo *)bagData; |
|
726 break; |
|
727 case SEC_OID_PKCS12_V1_CERT_BAG_ID: |
|
728 safeBag->safeBagContent.certBag = (sec_PKCS12CertBag *)bagData; |
|
729 break; |
|
730 case SEC_OID_PKCS12_V1_CRL_BAG_ID: |
|
731 safeBag->safeBagContent.crlBag = (sec_PKCS12CRLBag *)bagData; |
|
732 break; |
|
733 case SEC_OID_PKCS12_V1_SECRET_BAG_ID: |
|
734 safeBag->safeBagContent.secretBag = (sec_PKCS12SecretBag *)bagData; |
|
735 break; |
|
736 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: |
|
737 safeBag->safeBagContent.pkcs8ShroudedKeyBag = |
|
738 (SECKEYEncryptedPrivateKeyInfo *)bagData; |
|
739 break; |
|
740 case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: |
|
741 safeBag->safeBagContent.safeContents = |
|
742 (sec_PKCS12SafeContents *)bagData; |
|
743 setName = PR_FALSE; |
|
744 break; |
|
745 default: |
|
746 goto loser; |
|
747 } |
|
748 |
|
749 oidData = SECOID_FindOIDByTag(bagType); |
|
750 if(oidData) { |
|
751 rv = SECITEM_CopyItem(p12ctxt->arena, &safeBag->safeBagType, &oidData->oid); |
|
752 if(rv != SECSuccess) { |
|
753 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
754 goto loser; |
|
755 } |
|
756 } else { |
|
757 goto loser; |
|
758 } |
|
759 |
|
760 safeBag->arena = p12ctxt->arena; |
|
761 PORT_ArenaUnmark(p12ctxt->arena, mark); |
|
762 |
|
763 return safeBag; |
|
764 |
|
765 loser: |
|
766 if(mark) { |
|
767 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
768 } |
|
769 |
|
770 return NULL; |
|
771 } |
|
772 |
|
773 /* Creates a new certificate bag and returns a pointer to it. If an error |
|
774 * occurs NULL is returned. |
|
775 */ |
|
776 sec_PKCS12CertBag * |
|
777 sec_PKCS12NewCertBag(PLArenaPool *arena, SECOidTag certType) |
|
778 { |
|
779 sec_PKCS12CertBag *certBag = NULL; |
|
780 SECOidData *bagType = NULL; |
|
781 SECStatus rv; |
|
782 void *mark = NULL; |
|
783 |
|
784 if(!arena) { |
|
785 return NULL; |
|
786 } |
|
787 |
|
788 mark = PORT_ArenaMark(arena); |
|
789 certBag = (sec_PKCS12CertBag *)PORT_ArenaZAlloc(arena, |
|
790 sizeof(sec_PKCS12CertBag)); |
|
791 if(!certBag) { |
|
792 PORT_ArenaRelease(arena, mark); |
|
793 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
794 return NULL; |
|
795 } |
|
796 |
|
797 bagType = SECOID_FindOIDByTag(certType); |
|
798 if(!bagType) { |
|
799 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
800 goto loser; |
|
801 } |
|
802 |
|
803 rv = SECITEM_CopyItem(arena, &certBag->bagID, &bagType->oid); |
|
804 if(rv != SECSuccess) { |
|
805 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
806 goto loser; |
|
807 } |
|
808 |
|
809 PORT_ArenaUnmark(arena, mark); |
|
810 return certBag; |
|
811 |
|
812 loser: |
|
813 PORT_ArenaRelease(arena, mark); |
|
814 return NULL; |
|
815 } |
|
816 |
|
817 /* Creates a new CRL bag and returns a pointer to it. If an error |
|
818 * occurs NULL is returned. |
|
819 */ |
|
820 sec_PKCS12CRLBag * |
|
821 sec_PKCS12NewCRLBag(PLArenaPool *arena, SECOidTag crlType) |
|
822 { |
|
823 sec_PKCS12CRLBag *crlBag = NULL; |
|
824 SECOidData *bagType = NULL; |
|
825 SECStatus rv; |
|
826 void *mark = NULL; |
|
827 |
|
828 if(!arena) { |
|
829 return NULL; |
|
830 } |
|
831 |
|
832 mark = PORT_ArenaMark(arena); |
|
833 crlBag = (sec_PKCS12CRLBag *)PORT_ArenaZAlloc(arena, |
|
834 sizeof(sec_PKCS12CRLBag)); |
|
835 if(!crlBag) { |
|
836 PORT_ArenaRelease(arena, mark); |
|
837 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
838 return NULL; |
|
839 } |
|
840 |
|
841 bagType = SECOID_FindOIDByTag(crlType); |
|
842 if(!bagType) { |
|
843 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
844 goto loser; |
|
845 } |
|
846 |
|
847 rv = SECITEM_CopyItem(arena, &crlBag->bagID, &bagType->oid); |
|
848 if(rv != SECSuccess) { |
|
849 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
850 goto loser; |
|
851 } |
|
852 |
|
853 PORT_ArenaUnmark(arena, mark); |
|
854 return crlBag; |
|
855 |
|
856 loser: |
|
857 PORT_ArenaRelease(arena, mark); |
|
858 return NULL; |
|
859 } |
|
860 |
|
861 /* sec_PKCS12AddAttributeToBag |
|
862 * adds an attribute to a safeBag. currently, the only attributes supported |
|
863 * are those which are specified within PKCS 12. |
|
864 * |
|
865 * p12ctxt - the export context |
|
866 * safeBag - the safeBag to which attributes are appended |
|
867 * attrType - the attribute type |
|
868 * attrData - the attribute data |
|
869 */ |
|
870 SECStatus |
|
871 sec_PKCS12AddAttributeToBag(SEC_PKCS12ExportContext *p12ctxt, |
|
872 sec_PKCS12SafeBag *safeBag, SECOidTag attrType, |
|
873 SECItem *attrData) |
|
874 { |
|
875 sec_PKCS12Attribute *attribute; |
|
876 void *mark = NULL, *dummy = NULL; |
|
877 SECOidData *oiddata = NULL; |
|
878 SECItem unicodeName = { siBuffer, NULL, 0}; |
|
879 void *src = NULL; |
|
880 unsigned int nItems = 0; |
|
881 SECStatus rv; |
|
882 |
|
883 if(!safeBag || !p12ctxt) { |
|
884 return SECFailure; |
|
885 } |
|
886 |
|
887 mark = PORT_ArenaMark(safeBag->arena); |
|
888 |
|
889 /* allocate the attribute */ |
|
890 attribute = (sec_PKCS12Attribute *)PORT_ArenaZAlloc(safeBag->arena, |
|
891 sizeof(sec_PKCS12Attribute)); |
|
892 if(!attribute) { |
|
893 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
894 goto loser; |
|
895 } |
|
896 |
|
897 /* set up the attribute */ |
|
898 oiddata = SECOID_FindOIDByTag(attrType); |
|
899 if(!oiddata) { |
|
900 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
901 goto loser; |
|
902 } |
|
903 if(SECITEM_CopyItem(p12ctxt->arena, &attribute->attrType, &oiddata->oid) != |
|
904 SECSuccess) { |
|
905 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
906 goto loser; |
|
907 } |
|
908 |
|
909 nItems = 1; |
|
910 switch(attrType) { |
|
911 case SEC_OID_PKCS9_LOCAL_KEY_ID: |
|
912 { |
|
913 src = attrData; |
|
914 break; |
|
915 } |
|
916 case SEC_OID_PKCS9_FRIENDLY_NAME: |
|
917 { |
|
918 if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, |
|
919 &unicodeName, attrData, PR_FALSE, |
|
920 PR_FALSE, PR_TRUE)) { |
|
921 goto loser; |
|
922 } |
|
923 src = &unicodeName; |
|
924 break; |
|
925 } |
|
926 default: |
|
927 goto loser; |
|
928 } |
|
929 |
|
930 /* append the attribute to the attribute value list */ |
|
931 attribute->attrValue = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, |
|
932 ((nItems + 1) * sizeof(SECItem *))); |
|
933 if(!attribute->attrValue) { |
|
934 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
935 goto loser; |
|
936 } |
|
937 |
|
938 /* XXX this will need to be changed if attributes requiring more than |
|
939 * one element are ever used. |
|
940 */ |
|
941 attribute->attrValue[0] = (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, |
|
942 sizeof(SECItem)); |
|
943 if(!attribute->attrValue[0]) { |
|
944 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
945 goto loser; |
|
946 } |
|
947 attribute->attrValue[1] = NULL; |
|
948 |
|
949 rv = SECITEM_CopyItem(p12ctxt->arena, attribute->attrValue[0], |
|
950 (SECItem*)src); |
|
951 if(rv != SECSuccess) { |
|
952 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
953 goto loser; |
|
954 } |
|
955 |
|
956 /* append the attribute to the safeBag attributes */ |
|
957 if(safeBag->nAttribs) { |
|
958 dummy = PORT_ArenaGrow(p12ctxt->arena, safeBag->attribs, |
|
959 ((safeBag->nAttribs + 1) * sizeof(sec_PKCS12Attribute *)), |
|
960 ((safeBag->nAttribs + 2) * sizeof(sec_PKCS12Attribute *))); |
|
961 safeBag->attribs = (sec_PKCS12Attribute **)dummy; |
|
962 } else { |
|
963 safeBag->attribs = (sec_PKCS12Attribute **)PORT_ArenaZAlloc(p12ctxt->arena, |
|
964 2 * sizeof(sec_PKCS12Attribute *)); |
|
965 dummy = safeBag->attribs; |
|
966 } |
|
967 if(!dummy) { |
|
968 goto loser; |
|
969 } |
|
970 |
|
971 safeBag->attribs[safeBag->nAttribs] = attribute; |
|
972 safeBag->attribs[++safeBag->nAttribs] = NULL; |
|
973 |
|
974 PORT_ArenaUnmark(p12ctxt->arena, mark); |
|
975 return SECSuccess; |
|
976 |
|
977 loser: |
|
978 if(mark) { |
|
979 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
980 } |
|
981 |
|
982 return SECFailure; |
|
983 } |
|
984 |
|
985 /* SEC_PKCS12AddCert |
|
986 * Adds a certificate to the data being exported. |
|
987 * |
|
988 * p12ctxt - the export context |
|
989 * safe - the safeInfo to which the certificate is placed |
|
990 * nestedDest - if the cert is to be placed within a nested safeContents then, |
|
991 * this value is to be specified with the destination |
|
992 * cert - the cert to export |
|
993 * certDb - the certificate database handle |
|
994 * keyId - a unique identifier to associate a certificate/key pair |
|
995 * includeCertChain - PR_TRUE if the certificate chain is to be included. |
|
996 */ |
|
997 SECStatus |
|
998 SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, |
|
999 void *nestedDest, CERTCertificate *cert, |
|
1000 CERTCertDBHandle *certDb, SECItem *keyId, |
|
1001 PRBool includeCertChain) |
|
1002 { |
|
1003 sec_PKCS12CertBag *certBag; |
|
1004 sec_PKCS12SafeBag *safeBag; |
|
1005 void *mark; |
|
1006 SECStatus rv; |
|
1007 SECItem nick = {siBuffer, NULL,0}; |
|
1008 |
|
1009 if(!p12ctxt || !cert) { |
|
1010 return SECFailure; |
|
1011 } |
|
1012 mark = PORT_ArenaMark(p12ctxt->arena); |
|
1013 |
|
1014 /* allocate the cert bag */ |
|
1015 certBag = sec_PKCS12NewCertBag(p12ctxt->arena, |
|
1016 SEC_OID_PKCS9_X509_CERT); |
|
1017 if(!certBag) { |
|
1018 goto loser; |
|
1019 } |
|
1020 |
|
1021 if(SECITEM_CopyItem(p12ctxt->arena, &certBag->value.x509Cert, |
|
1022 &cert->derCert) != SECSuccess) { |
|
1023 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1024 goto loser; |
|
1025 } |
|
1026 |
|
1027 /* if the cert chain is to be included, we should only be exporting |
|
1028 * the cert from our internal database. |
|
1029 */ |
|
1030 if(includeCertChain) { |
|
1031 CERTCertificateList *certList = CERT_CertChainFromCert(cert, |
|
1032 certUsageSSLClient, |
|
1033 PR_TRUE); |
|
1034 unsigned int count = 0; |
|
1035 if(!certList) { |
|
1036 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1037 goto loser; |
|
1038 } |
|
1039 |
|
1040 /* add cert chain */ |
|
1041 for(count = 0; count < (unsigned int)certList->len; count++) { |
|
1042 if(SECITEM_CompareItem(&certList->certs[count], &cert->derCert) |
|
1043 != SECEqual) { |
|
1044 CERTCertificate *tempCert; |
|
1045 |
|
1046 /* decode the certificate */ |
|
1047 /* XXX |
|
1048 * This was rather silly. The chain is constructed above |
|
1049 * by finding all of the CERTCertificate's in the database. |
|
1050 * Then the chain is put into a CERTCertificateList, which only |
|
1051 * contains the DER. Finally, the DER was decoded, and the |
|
1052 * decoded cert was sent recursively back to this function. |
|
1053 * Beyond being inefficent, this causes data loss (specifically, |
|
1054 * the nickname). Instead, for 3.4, we'll do a lookup by the |
|
1055 * DER, which should return the cached entry. |
|
1056 */ |
|
1057 tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), |
|
1058 &certList->certs[count]); |
|
1059 if(!tempCert) { |
|
1060 CERT_DestroyCertificateList(certList); |
|
1061 goto loser; |
|
1062 } |
|
1063 |
|
1064 /* add the certificate */ |
|
1065 if(SEC_PKCS12AddCert(p12ctxt, safe, nestedDest, tempCert, |
|
1066 certDb, NULL, PR_FALSE) != SECSuccess) { |
|
1067 CERT_DestroyCertificate(tempCert); |
|
1068 CERT_DestroyCertificateList(certList); |
|
1069 goto loser; |
|
1070 } |
|
1071 CERT_DestroyCertificate(tempCert); |
|
1072 } |
|
1073 } |
|
1074 CERT_DestroyCertificateList(certList); |
|
1075 } |
|
1076 |
|
1077 /* if the certificate has a nickname, we will set the friendly name |
|
1078 * to that. |
|
1079 */ |
|
1080 if(cert->nickname) { |
|
1081 if (cert->slot && !PK11_IsInternal(cert->slot)) { |
|
1082 /* |
|
1083 * The cert is coming off of an external token, |
|
1084 * let's strip the token name from the nickname |
|
1085 * and only add what comes after the colon as the |
|
1086 * nickname. -javi |
|
1087 */ |
|
1088 char *delimit; |
|
1089 |
|
1090 delimit = PORT_Strchr(cert->nickname,':'); |
|
1091 if (delimit == NULL) { |
|
1092 nick.data = (unsigned char *)cert->nickname; |
|
1093 nick.len = PORT_Strlen(cert->nickname); |
|
1094 } else { |
|
1095 delimit++; |
|
1096 nick.data = (unsigned char *)PORT_ArenaStrdup(p12ctxt->arena, |
|
1097 delimit); |
|
1098 nick.len = PORT_Strlen(delimit); |
|
1099 } |
|
1100 } else { |
|
1101 nick.data = (unsigned char *)cert->nickname; |
|
1102 nick.len = PORT_Strlen(cert->nickname); |
|
1103 } |
|
1104 } |
|
1105 |
|
1106 safeBag = sec_PKCS12CreateSafeBag(p12ctxt, SEC_OID_PKCS12_V1_CERT_BAG_ID, |
|
1107 certBag); |
|
1108 if(!safeBag) { |
|
1109 goto loser; |
|
1110 } |
|
1111 |
|
1112 /* add the friendly name and keyId attributes, if necessary */ |
|
1113 if(nick.data) { |
|
1114 if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, |
|
1115 SEC_OID_PKCS9_FRIENDLY_NAME, &nick) |
|
1116 != SECSuccess) { |
|
1117 goto loser; |
|
1118 } |
|
1119 } |
|
1120 |
|
1121 if(keyId) { |
|
1122 if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, SEC_OID_PKCS9_LOCAL_KEY_ID, |
|
1123 keyId) != SECSuccess) { |
|
1124 goto loser; |
|
1125 } |
|
1126 } |
|
1127 |
|
1128 /* append the cert safeBag */ |
|
1129 if(nestedDest) { |
|
1130 rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, |
|
1131 (sec_PKCS12SafeContents*)nestedDest, |
|
1132 safeBag); |
|
1133 } else { |
|
1134 rv = sec_pkcs12_append_bag(p12ctxt, safe, safeBag); |
|
1135 } |
|
1136 |
|
1137 if(rv != SECSuccess) { |
|
1138 goto loser; |
|
1139 } |
|
1140 |
|
1141 PORT_ArenaUnmark(p12ctxt->arena, mark); |
|
1142 return SECSuccess; |
|
1143 |
|
1144 loser: |
|
1145 if(mark) { |
|
1146 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
1147 } |
|
1148 |
|
1149 return SECFailure; |
|
1150 } |
|
1151 |
|
1152 /* SEC_PKCS12AddKeyForCert |
|
1153 * Extracts the key associated with a particular certificate and exports |
|
1154 * it. |
|
1155 * |
|
1156 * p12ctxt - the export context |
|
1157 * safe - the safeInfo to place the key in |
|
1158 * nestedDest - the nested safeContents to place a key |
|
1159 * cert - the certificate which the key belongs to |
|
1160 * shroudKey - encrypt the private key for export. This value should |
|
1161 * always be true. lower level code will not allow the export |
|
1162 * of unencrypted private keys. |
|
1163 * algorithm - the algorithm with which to encrypt the private key |
|
1164 * pwitem - the password to encrypt the private key with |
|
1165 * keyId - the keyID attribute |
|
1166 * nickName - the nickname attribute |
|
1167 */ |
|
1168 SECStatus |
|
1169 SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, |
|
1170 void *nestedDest, CERTCertificate *cert, |
|
1171 PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem, |
|
1172 SECItem *keyId, SECItem *nickName) |
|
1173 { |
|
1174 void *mark; |
|
1175 void *keyItem; |
|
1176 SECOidTag keyType; |
|
1177 SECStatus rv = SECFailure; |
|
1178 SECItem nickname = {siBuffer,NULL,0}, uniPwitem = {siBuffer, NULL, 0}; |
|
1179 sec_PKCS12SafeBag *returnBag; |
|
1180 |
|
1181 if(!p12ctxt || !cert || !safe) { |
|
1182 return SECFailure; |
|
1183 } |
|
1184 |
|
1185 mark = PORT_ArenaMark(p12ctxt->arena); |
|
1186 |
|
1187 /* retrieve the key based upon the type that it is and |
|
1188 * specify the type of safeBag to store the key in |
|
1189 */ |
|
1190 if(!shroudKey) { |
|
1191 |
|
1192 /* extract the key unencrypted. this will most likely go away */ |
|
1193 SECKEYPrivateKeyInfo *pki = PK11_ExportPrivateKeyInfo(cert, |
|
1194 p12ctxt->wincx); |
|
1195 if(!pki) { |
|
1196 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
1197 PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); |
|
1198 return SECFailure; |
|
1199 } |
|
1200 keyItem = PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECKEYPrivateKeyInfo)); |
|
1201 if(!keyItem) { |
|
1202 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1203 goto loser; |
|
1204 } |
|
1205 rv = SECKEY_CopyPrivateKeyInfo(p12ctxt->arena, |
|
1206 (SECKEYPrivateKeyInfo *)keyItem, pki); |
|
1207 keyType = SEC_OID_PKCS12_V1_KEY_BAG_ID; |
|
1208 SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE); |
|
1209 } else { |
|
1210 |
|
1211 /* extract the key encrypted */ |
|
1212 SECKEYEncryptedPrivateKeyInfo *epki = NULL; |
|
1213 PK11SlotInfo *slot = NULL; |
|
1214 |
|
1215 if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, &uniPwitem, |
|
1216 pwitem, PR_TRUE, PR_TRUE, PR_TRUE)) { |
|
1217 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1218 goto loser; |
|
1219 } |
|
1220 |
|
1221 /* we want to make sure to take the key out of the key slot */ |
|
1222 if(PK11_IsInternal(p12ctxt->slot)) { |
|
1223 slot = PK11_GetInternalKeySlot(); |
|
1224 } else { |
|
1225 slot = PK11_ReferenceSlot(p12ctxt->slot); |
|
1226 } |
|
1227 |
|
1228 epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, |
|
1229 &uniPwitem, cert, |
|
1230 NSS_PBE_DEFAULT_ITERATION_COUNT, |
|
1231 p12ctxt->wincx); |
|
1232 PK11_FreeSlot(slot); |
|
1233 if(!epki) { |
|
1234 PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); |
|
1235 goto loser; |
|
1236 } |
|
1237 |
|
1238 keyItem = PORT_ArenaZAlloc(p12ctxt->arena, |
|
1239 sizeof(SECKEYEncryptedPrivateKeyInfo)); |
|
1240 if(!keyItem) { |
|
1241 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1242 goto loser; |
|
1243 } |
|
1244 rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, |
|
1245 (SECKEYEncryptedPrivateKeyInfo *)keyItem, |
|
1246 epki); |
|
1247 keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID; |
|
1248 SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE); |
|
1249 } |
|
1250 |
|
1251 if(rv != SECSuccess) { |
|
1252 goto loser; |
|
1253 } |
|
1254 |
|
1255 /* if no nickname specified, let's see if the certificate has a |
|
1256 * nickname. |
|
1257 */ |
|
1258 if(!nickName) { |
|
1259 if(cert->nickname) { |
|
1260 nickname.data = (unsigned char *)cert->nickname; |
|
1261 nickname.len = PORT_Strlen(cert->nickname); |
|
1262 nickName = &nickname; |
|
1263 } |
|
1264 } |
|
1265 |
|
1266 /* create the safe bag and set any attributes */ |
|
1267 returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem); |
|
1268 if(!returnBag) { |
|
1269 rv = SECFailure; |
|
1270 goto loser; |
|
1271 } |
|
1272 |
|
1273 if(nickName) { |
|
1274 if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, |
|
1275 SEC_OID_PKCS9_FRIENDLY_NAME, nickName) |
|
1276 != SECSuccess) { |
|
1277 goto loser; |
|
1278 } |
|
1279 } |
|
1280 |
|
1281 if(keyId) { |
|
1282 if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID, |
|
1283 keyId) != SECSuccess) { |
|
1284 goto loser; |
|
1285 } |
|
1286 } |
|
1287 |
|
1288 if(nestedDest) { |
|
1289 rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, |
|
1290 (sec_PKCS12SafeContents*)nestedDest, |
|
1291 returnBag); |
|
1292 } else { |
|
1293 rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag); |
|
1294 } |
|
1295 |
|
1296 loser: |
|
1297 |
|
1298 if (rv != SECSuccess) { |
|
1299 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
1300 } else { |
|
1301 PORT_ArenaUnmark(p12ctxt->arena, mark); |
|
1302 } |
|
1303 |
|
1304 return rv; |
|
1305 } |
|
1306 |
|
1307 /* SEC_PKCS12AddCertOrChainAndKey |
|
1308 * Add a certificate and key pair to be exported. |
|
1309 * |
|
1310 * p12ctxt - the export context |
|
1311 * certSafe - the safeInfo where the cert is stored |
|
1312 * certNestedDest - the nested safeContents to store the cert |
|
1313 * keySafe - the safeInfo where the key is stored |
|
1314 * keyNestedDest - the nested safeContents to store the key |
|
1315 * shroudKey - extract the private key encrypted? |
|
1316 * pwitem - the password with which the key is encrypted |
|
1317 * algorithm - the algorithm with which the key is encrypted |
|
1318 * includeCertChain - also add certs from chain to bag. |
|
1319 */ |
|
1320 SECStatus |
|
1321 SEC_PKCS12AddCertOrChainAndKey(SEC_PKCS12ExportContext *p12ctxt, |
|
1322 void *certSafe, void *certNestedDest, |
|
1323 CERTCertificate *cert, CERTCertDBHandle *certDb, |
|
1324 void *keySafe, void *keyNestedDest, |
|
1325 PRBool shroudKey, SECItem *pwitem, |
|
1326 SECOidTag algorithm, PRBool includeCertChain) |
|
1327 { |
|
1328 SECStatus rv = SECFailure; |
|
1329 SGNDigestInfo *digest = NULL; |
|
1330 void *mark = NULL; |
|
1331 |
|
1332 if(!p12ctxt || !certSafe || !keySafe || !cert) { |
|
1333 return SECFailure; |
|
1334 } |
|
1335 |
|
1336 mark = PORT_ArenaMark(p12ctxt->arena); |
|
1337 |
|
1338 /* generate the thumbprint of the cert to use as a keyId */ |
|
1339 digest = sec_pkcs12_compute_thumbprint(&cert->derCert); |
|
1340 if(!digest) { |
|
1341 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
1342 return SECFailure; |
|
1343 } |
|
1344 |
|
1345 /* add the certificate */ |
|
1346 rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, |
|
1347 (SEC_PKCS12SafeInfo*)certNestedDest, cert, certDb, |
|
1348 &digest->digest, includeCertChain); |
|
1349 if(rv != SECSuccess) { |
|
1350 goto loser; |
|
1351 } |
|
1352 |
|
1353 /* add the key */ |
|
1354 rv = SEC_PKCS12AddKeyForCert(p12ctxt, (SEC_PKCS12SafeInfo*)keySafe, |
|
1355 keyNestedDest, cert, |
|
1356 shroudKey, algorithm, pwitem, |
|
1357 &digest->digest, NULL ); |
|
1358 if(rv != SECSuccess) { |
|
1359 goto loser; |
|
1360 } |
|
1361 |
|
1362 SGN_DestroyDigestInfo(digest); |
|
1363 |
|
1364 PORT_ArenaUnmark(p12ctxt->arena, mark); |
|
1365 return SECSuccess; |
|
1366 |
|
1367 loser: |
|
1368 SGN_DestroyDigestInfo(digest); |
|
1369 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
1370 |
|
1371 return SECFailure; |
|
1372 } |
|
1373 |
|
1374 /* like SEC_PKCS12AddCertOrChainAndKey, but always adds cert chain */ |
|
1375 SECStatus |
|
1376 SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt, |
|
1377 void *certSafe, void *certNestedDest, |
|
1378 CERTCertificate *cert, CERTCertDBHandle *certDb, |
|
1379 void *keySafe, void *keyNestedDest, |
|
1380 PRBool shroudKey, SECItem *pwItem, SECOidTag algorithm) |
|
1381 { |
|
1382 return SEC_PKCS12AddCertOrChainAndKey(p12ctxt, certSafe, certNestedDest, |
|
1383 cert, certDb, keySafe, keyNestedDest, shroudKey, pwItem, |
|
1384 algorithm, PR_TRUE); |
|
1385 } |
|
1386 |
|
1387 |
|
1388 /* SEC_PKCS12CreateNestedSafeContents |
|
1389 * Allows nesting of safe contents to be implemented. No limit imposed on |
|
1390 * depth. |
|
1391 * |
|
1392 * p12ctxt - the export context |
|
1393 * baseSafe - the base safeInfo |
|
1394 * nestedDest - a parent safeContents (?) |
|
1395 */ |
|
1396 void * |
|
1397 SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt, |
|
1398 void *baseSafe, void *nestedDest) |
|
1399 { |
|
1400 sec_PKCS12SafeContents *newSafe; |
|
1401 sec_PKCS12SafeBag *safeContentsBag; |
|
1402 void *mark; |
|
1403 SECStatus rv; |
|
1404 |
|
1405 if(!p12ctxt || !baseSafe) { |
|
1406 return NULL; |
|
1407 } |
|
1408 |
|
1409 mark = PORT_ArenaMark(p12ctxt->arena); |
|
1410 |
|
1411 newSafe = sec_PKCS12CreateSafeContents(p12ctxt->arena); |
|
1412 if(!newSafe) { |
|
1413 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
1414 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1415 return NULL; |
|
1416 } |
|
1417 |
|
1418 /* create the safeContents safeBag */ |
|
1419 safeContentsBag = sec_PKCS12CreateSafeBag(p12ctxt, |
|
1420 SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID, |
|
1421 newSafe); |
|
1422 if(!safeContentsBag) { |
|
1423 goto loser; |
|
1424 } |
|
1425 |
|
1426 /* append the safeContents to the appropriate area */ |
|
1427 if(nestedDest) { |
|
1428 rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, |
|
1429 (sec_PKCS12SafeContents*)nestedDest, |
|
1430 safeContentsBag); |
|
1431 } else { |
|
1432 rv = sec_pkcs12_append_bag(p12ctxt, (SEC_PKCS12SafeInfo*)baseSafe, |
|
1433 safeContentsBag); |
|
1434 } |
|
1435 if(rv != SECSuccess) { |
|
1436 goto loser; |
|
1437 } |
|
1438 |
|
1439 PORT_ArenaUnmark(p12ctxt->arena, mark); |
|
1440 return newSafe; |
|
1441 |
|
1442 loser: |
|
1443 PORT_ArenaRelease(p12ctxt->arena, mark); |
|
1444 return NULL; |
|
1445 } |
|
1446 |
|
1447 /********************************* |
|
1448 * Encoding routines |
|
1449 *********************************/ |
|
1450 |
|
1451 /* Clean up the resources allocated by a sec_PKCS12EncoderContext. */ |
|
1452 static void |
|
1453 sec_pkcs12_encoder_destroy_context(sec_PKCS12EncoderContext *p12enc) |
|
1454 { |
|
1455 if(p12enc) { |
|
1456 if(p12enc->outerA1ecx) { |
|
1457 SEC_ASN1EncoderFinish(p12enc->outerA1ecx); |
|
1458 p12enc->outerA1ecx = NULL; |
|
1459 } |
|
1460 if(p12enc->aSafeCinfo) { |
|
1461 SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo); |
|
1462 p12enc->aSafeCinfo = NULL; |
|
1463 } |
|
1464 if(p12enc->middleP7ecx) { |
|
1465 SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12enc->p12exp->pwfn, |
|
1466 p12enc->p12exp->pwfnarg); |
|
1467 p12enc->middleP7ecx = NULL; |
|
1468 } |
|
1469 if(p12enc->middleA1ecx) { |
|
1470 SEC_ASN1EncoderFinish(p12enc->middleA1ecx); |
|
1471 p12enc->middleA1ecx = NULL; |
|
1472 } |
|
1473 if(p12enc->hmacCx) { |
|
1474 PK11_DestroyContext(p12enc->hmacCx, PR_TRUE); |
|
1475 p12enc->hmacCx = NULL; |
|
1476 } |
|
1477 } |
|
1478 } |
|
1479 |
|
1480 /* set up the encoder context based on information in the export context |
|
1481 * and return the newly allocated enocoder context. A return of NULL |
|
1482 * indicates an error occurred. |
|
1483 */ |
|
1484 static sec_PKCS12EncoderContext * |
|
1485 sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp) |
|
1486 { |
|
1487 sec_PKCS12EncoderContext *p12enc = NULL; |
|
1488 unsigned int i, nonEmptyCnt; |
|
1489 SECStatus rv; |
|
1490 SECItem ignore = {0}; |
|
1491 void *mark; |
|
1492 |
|
1493 if(!p12exp || !p12exp->safeInfos) { |
|
1494 return NULL; |
|
1495 } |
|
1496 |
|
1497 /* check for any empty safes and skip them */ |
|
1498 i = nonEmptyCnt = 0; |
|
1499 while(p12exp->safeInfos[i]) { |
|
1500 if(p12exp->safeInfos[i]->itemCount) { |
|
1501 nonEmptyCnt++; |
|
1502 } |
|
1503 i++; |
|
1504 } |
|
1505 if(nonEmptyCnt == 0) { |
|
1506 return NULL; |
|
1507 } |
|
1508 p12exp->authSafe.encodedSafes[nonEmptyCnt] = NULL; |
|
1509 |
|
1510 /* allocate the encoder context */ |
|
1511 mark = PORT_ArenaMark(p12exp->arena); |
|
1512 p12enc = PORT_ArenaZNew(p12exp->arena, sec_PKCS12EncoderContext); |
|
1513 if(!p12enc) { |
|
1514 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1515 return NULL; |
|
1516 } |
|
1517 |
|
1518 p12enc->arena = p12exp->arena; |
|
1519 p12enc->p12exp = p12exp; |
|
1520 |
|
1521 /* set up the PFX version and information */ |
|
1522 PORT_Memset(&p12enc->pfx, 0, sizeof(sec_PKCS12PFXItem)); |
|
1523 if(!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->pfx.version), |
|
1524 SEC_PKCS12_VERSION) ) { |
|
1525 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1526 goto loser; |
|
1527 } |
|
1528 |
|
1529 /* set up the authenticated safe content info based on the |
|
1530 * type of integrity being used. this should be changed to |
|
1531 * enforce integrity mode, but will not be implemented until |
|
1532 * it is confirmed that integrity must be in place |
|
1533 */ |
|
1534 if(p12exp->integrityEnabled && !p12exp->pwdIntegrity) { |
|
1535 SECStatus rv; |
|
1536 |
|
1537 /* create public key integrity mode */ |
|
1538 p12enc->aSafeCinfo = SEC_PKCS7CreateSignedData( |
|
1539 p12exp->integrityInfo.pubkeyInfo.cert, |
|
1540 certUsageEmailSigner, |
|
1541 p12exp->integrityInfo.pubkeyInfo.certDb, |
|
1542 p12exp->integrityInfo.pubkeyInfo.algorithm, |
|
1543 NULL, |
|
1544 p12exp->pwfn, |
|
1545 p12exp->pwfnarg); |
|
1546 if(!p12enc->aSafeCinfo) { |
|
1547 goto loser; |
|
1548 } |
|
1549 if(SEC_PKCS7IncludeCertChain(p12enc->aSafeCinfo,NULL) != SECSuccess) { |
|
1550 goto loser; |
|
1551 } |
|
1552 rv = SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo); |
|
1553 PORT_Assert(rv == SECSuccess); |
|
1554 } else { |
|
1555 p12enc->aSafeCinfo = SEC_PKCS7CreateData(); |
|
1556 |
|
1557 /* init password pased integrity mode */ |
|
1558 if(p12exp->integrityEnabled) { |
|
1559 SECItem pwd = {siBuffer,NULL, 0}; |
|
1560 SECItem *salt = sec_pkcs12_generate_salt(); |
|
1561 PK11SymKey *symKey; |
|
1562 SECItem *params; |
|
1563 CK_MECHANISM_TYPE integrityMechType; |
|
1564 CK_MECHANISM_TYPE hmacMechType; |
|
1565 |
|
1566 /* zero out macData and set values */ |
|
1567 PORT_Memset(&p12enc->mac, 0, sizeof(sec_PKCS12MacData)); |
|
1568 |
|
1569 if(!salt) { |
|
1570 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1571 goto loser; |
|
1572 } |
|
1573 if(SECITEM_CopyItem(p12exp->arena, &(p12enc->mac.macSalt), salt) |
|
1574 != SECSuccess) { |
|
1575 /* XXX salt is leaked */ |
|
1576 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1577 goto loser; |
|
1578 } |
|
1579 if (!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->mac.iter), |
|
1580 NSS_PBE_DEFAULT_ITERATION_COUNT)) { |
|
1581 /* XXX salt is leaked */ |
|
1582 goto loser; |
|
1583 } |
|
1584 |
|
1585 /* generate HMAC key */ |
|
1586 if(!sec_pkcs12_convert_item_to_unicode(NULL, &pwd, |
|
1587 p12exp->integrityInfo.pwdInfo.password, PR_TRUE, |
|
1588 PR_TRUE, PR_TRUE)) { |
|
1589 /* XXX salt is leaked */ |
|
1590 goto loser; |
|
1591 } |
|
1592 /* |
|
1593 * This code only works with PKCS #12 Mac using PKCS #5 v1 |
|
1594 * PBA keygens. PKCS #5 v2 support will require a change to |
|
1595 * the PKCS #12 spec. |
|
1596 */ |
|
1597 params = PK11_CreatePBEParams(salt, &pwd, |
|
1598 NSS_PBE_DEFAULT_ITERATION_COUNT); |
|
1599 SECITEM_ZfreeItem(salt, PR_TRUE); |
|
1600 SECITEM_ZfreeItem(&pwd, PR_FALSE); |
|
1601 |
|
1602 /* get the PBA Mechanism to generate the key */ |
|
1603 switch (p12exp->integrityInfo.pwdInfo.algorithm) { |
|
1604 case SEC_OID_SHA1: |
|
1605 integrityMechType = CKM_PBA_SHA1_WITH_SHA1_HMAC; break; |
|
1606 case SEC_OID_MD5: |
|
1607 integrityMechType = CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN; break; |
|
1608 case SEC_OID_MD2: |
|
1609 integrityMechType = CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN; break; |
|
1610 default: |
|
1611 /* XXX params is leaked */ |
|
1612 goto loser; |
|
1613 } |
|
1614 |
|
1615 /* generate the key */ |
|
1616 symKey = PK11_KeyGen(NULL, integrityMechType, params, 20, NULL); |
|
1617 PK11_DestroyPBEParams(params); |
|
1618 if(!symKey) { |
|
1619 goto loser; |
|
1620 } |
|
1621 |
|
1622 /* initialize HMAC */ |
|
1623 /* Get the HMAC mechanism from the hash OID */ |
|
1624 hmacMechType= sec_pkcs12_algtag_to_mech( |
|
1625 p12exp->integrityInfo.pwdInfo.algorithm); |
|
1626 |
|
1627 p12enc->hmacCx = PK11_CreateContextBySymKey( hmacMechType, |
|
1628 CKA_SIGN, symKey, &ignore); |
|
1629 |
|
1630 PK11_FreeSymKey(symKey); |
|
1631 if(!p12enc->hmacCx) { |
|
1632 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1633 goto loser; |
|
1634 } |
|
1635 rv = PK11_DigestBegin(p12enc->hmacCx); |
|
1636 if (rv != SECSuccess) |
|
1637 goto loser; |
|
1638 } |
|
1639 } |
|
1640 |
|
1641 if(!p12enc->aSafeCinfo) { |
|
1642 goto loser; |
|
1643 } |
|
1644 |
|
1645 PORT_ArenaUnmark(p12exp->arena, mark); |
|
1646 |
|
1647 return p12enc; |
|
1648 |
|
1649 loser: |
|
1650 sec_pkcs12_encoder_destroy_context(p12enc); |
|
1651 if (p12exp->arena != NULL) |
|
1652 PORT_ArenaRelease(p12exp->arena, mark); |
|
1653 |
|
1654 return NULL; |
|
1655 } |
|
1656 |
|
1657 /* The outermost ASN.1 encoder calls this function for output. |
|
1658 ** This function calls back to the library caller's output routine, |
|
1659 ** which typically writes to a PKCS12 file. |
|
1660 */ |
|
1661 static void |
|
1662 sec_P12A1OutputCB_Outer(void *arg, const char *buf, unsigned long len, |
|
1663 int depth, SEC_ASN1EncodingPart data_kind) |
|
1664 { |
|
1665 struct sec_pkcs12_encoder_output *output; |
|
1666 |
|
1667 output = (struct sec_pkcs12_encoder_output*)arg; |
|
1668 (* output->outputfn)(output->outputarg, buf, len); |
|
1669 } |
|
1670 |
|
1671 /* The "middle" and "inner" ASN.1 encoders call this function to output. |
|
1672 ** This function does HMACing, if appropriate, and then buffers the data. |
|
1673 ** The buffered data is eventually passed down to the underlying PKCS7 encoder. |
|
1674 */ |
|
1675 static void |
|
1676 sec_P12A1OutputCB_HmacP7Update(void *arg, const char *buf, |
|
1677 unsigned long len, |
|
1678 int depth, |
|
1679 SEC_ASN1EncodingPart data_kind) |
|
1680 { |
|
1681 sec_pkcs12OutputBuffer * bufcx = (sec_pkcs12OutputBuffer *)arg; |
|
1682 |
|
1683 if(!buf || !len) |
|
1684 return; |
|
1685 |
|
1686 if (bufcx->hmacCx) { |
|
1687 PK11_DigestOp(bufcx->hmacCx, (unsigned char *)buf, len); |
|
1688 } |
|
1689 |
|
1690 /* buffer */ |
|
1691 if (bufcx->numBytes > 0) { |
|
1692 int toCopy; |
|
1693 if (len + bufcx->numBytes <= bufcx->bufBytes) { |
|
1694 memcpy(bufcx->buf + bufcx->numBytes, buf, len); |
|
1695 bufcx->numBytes += len; |
|
1696 if (bufcx->numBytes < bufcx->bufBytes) |
|
1697 return; |
|
1698 SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes); |
|
1699 bufcx->numBytes = 0; |
|
1700 return; |
|
1701 } |
|
1702 toCopy = bufcx->bufBytes - bufcx->numBytes; |
|
1703 memcpy(bufcx->buf + bufcx->numBytes, buf, toCopy); |
|
1704 SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes); |
|
1705 bufcx->numBytes = 0; |
|
1706 len -= toCopy; |
|
1707 buf += toCopy; |
|
1708 } |
|
1709 /* buffer is presently empty */ |
|
1710 if (len >= bufcx->bufBytes) { |
|
1711 /* Just pass it through */ |
|
1712 SEC_PKCS7EncoderUpdate(bufcx->p7eCx, buf, len); |
|
1713 } else { |
|
1714 /* copy it all into the buffer, and return */ |
|
1715 memcpy(bufcx->buf, buf, len); |
|
1716 bufcx->numBytes = len; |
|
1717 } |
|
1718 } |
|
1719 |
|
1720 void |
|
1721 sec_FlushPkcs12OutputBuffer( sec_pkcs12OutputBuffer * bufcx) |
|
1722 { |
|
1723 if (bufcx->numBytes > 0) { |
|
1724 SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->numBytes); |
|
1725 bufcx->numBytes = 0; |
|
1726 } |
|
1727 } |
|
1728 |
|
1729 /* Feeds the output of a PKCS7 encoder into the next outward ASN.1 encoder. |
|
1730 ** This function is used by both the inner and middle PCS7 encoders. |
|
1731 */ |
|
1732 static void |
|
1733 sec_P12P7OutputCB_CallA1Update(void *arg, const char *buf, unsigned long len) |
|
1734 { |
|
1735 SEC_ASN1EncoderContext *cx = (SEC_ASN1EncoderContext*)arg; |
|
1736 |
|
1737 if (!buf || !len) |
|
1738 return; |
|
1739 |
|
1740 SEC_ASN1EncoderUpdate(cx, buf, len); |
|
1741 } |
|
1742 |
|
1743 |
|
1744 /* this function encodes content infos which are part of the |
|
1745 * sequence of content infos labeled AuthenticatedSafes |
|
1746 */ |
|
1747 static SECStatus |
|
1748 sec_pkcs12_encoder_asafe_process(sec_PKCS12EncoderContext *p12ecx) |
|
1749 { |
|
1750 SEC_PKCS7EncoderContext *innerP7ecx; |
|
1751 SEC_PKCS7ContentInfo *cinfo; |
|
1752 PK11SymKey *bulkKey = NULL; |
|
1753 SEC_ASN1EncoderContext *innerA1ecx = NULL; |
|
1754 SECStatus rv = SECSuccess; |
|
1755 |
|
1756 if(p12ecx->currentSafe < p12ecx->p12exp->authSafe.safeCount) { |
|
1757 SEC_PKCS12SafeInfo *safeInfo; |
|
1758 SECOidTag cinfoType; |
|
1759 |
|
1760 safeInfo = p12ecx->p12exp->safeInfos[p12ecx->currentSafe]; |
|
1761 |
|
1762 /* skip empty safes */ |
|
1763 if(safeInfo->itemCount == 0) { |
|
1764 return SECSuccess; |
|
1765 } |
|
1766 |
|
1767 cinfo = safeInfo->cinfo; |
|
1768 cinfoType = SEC_PKCS7ContentType(cinfo); |
|
1769 |
|
1770 /* determine the safe type and set the appropriate argument */ |
|
1771 switch(cinfoType) { |
|
1772 case SEC_OID_PKCS7_DATA: |
|
1773 case SEC_OID_PKCS7_ENVELOPED_DATA: |
|
1774 break; |
|
1775 case SEC_OID_PKCS7_ENCRYPTED_DATA: |
|
1776 bulkKey = safeInfo->encryptionKey; |
|
1777 PK11_SetSymKeyUserData(bulkKey, &safeInfo->pwitem, NULL); |
|
1778 break; |
|
1779 default: |
|
1780 return SECFailure; |
|
1781 |
|
1782 } |
|
1783 |
|
1784 /* start the PKCS7 encoder */ |
|
1785 innerP7ecx = SEC_PKCS7EncoderStart(cinfo, |
|
1786 sec_P12P7OutputCB_CallA1Update, |
|
1787 p12ecx->middleA1ecx, bulkKey); |
|
1788 if(!innerP7ecx) { |
|
1789 goto loser; |
|
1790 } |
|
1791 |
|
1792 /* encode safe contents */ |
|
1793 p12ecx->innerBuf.p7eCx = innerP7ecx; |
|
1794 p12ecx->innerBuf.hmacCx = NULL; |
|
1795 p12ecx->innerBuf.numBytes = 0; |
|
1796 p12ecx->innerBuf.bufBytes = sizeof p12ecx->innerBuf.buf; |
|
1797 |
|
1798 innerA1ecx = SEC_ASN1EncoderStart(safeInfo->safe, |
|
1799 sec_PKCS12SafeContentsTemplate, |
|
1800 sec_P12A1OutputCB_HmacP7Update, |
|
1801 &p12ecx->innerBuf); |
|
1802 if(!innerA1ecx) { |
|
1803 goto loser; |
|
1804 } |
|
1805 rv = SEC_ASN1EncoderUpdate(innerA1ecx, NULL, 0); |
|
1806 SEC_ASN1EncoderFinish(innerA1ecx); |
|
1807 sec_FlushPkcs12OutputBuffer( &p12ecx->innerBuf); |
|
1808 innerA1ecx = NULL; |
|
1809 if(rv != SECSuccess) { |
|
1810 goto loser; |
|
1811 } |
|
1812 |
|
1813 |
|
1814 /* finish up safe content info */ |
|
1815 rv = SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn, |
|
1816 p12ecx->p12exp->pwfnarg); |
|
1817 } |
|
1818 memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf); |
|
1819 return SECSuccess; |
|
1820 |
|
1821 loser: |
|
1822 if(innerP7ecx) { |
|
1823 SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn, |
|
1824 p12ecx->p12exp->pwfnarg); |
|
1825 } |
|
1826 |
|
1827 if(innerA1ecx) { |
|
1828 SEC_ASN1EncoderFinish(innerA1ecx); |
|
1829 } |
|
1830 memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf); |
|
1831 return SECFailure; |
|
1832 } |
|
1833 |
|
1834 /* finish the HMAC and encode the macData so that it can be |
|
1835 * encoded. |
|
1836 */ |
|
1837 static SECStatus |
|
1838 sec_Pkcs12FinishMac(sec_PKCS12EncoderContext *p12ecx) |
|
1839 { |
|
1840 SECItem hmac = { siBuffer, NULL, 0 }; |
|
1841 SECStatus rv; |
|
1842 SGNDigestInfo *di = NULL; |
|
1843 void *dummy; |
|
1844 |
|
1845 if(!p12ecx) { |
|
1846 return SECFailure; |
|
1847 } |
|
1848 |
|
1849 /* make sure we are using password integrity mode */ |
|
1850 if(!p12ecx->p12exp->integrityEnabled) { |
|
1851 return SECSuccess; |
|
1852 } |
|
1853 |
|
1854 if(!p12ecx->p12exp->pwdIntegrity) { |
|
1855 return SECSuccess; |
|
1856 } |
|
1857 |
|
1858 /* finish the hmac */ |
|
1859 hmac.data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH); |
|
1860 if(!hmac.data) { |
|
1861 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1862 return SECFailure; |
|
1863 } |
|
1864 |
|
1865 rv = PK11_DigestFinal(p12ecx->hmacCx, hmac.data, &hmac.len, SHA1_LENGTH); |
|
1866 |
|
1867 if(rv != SECSuccess) { |
|
1868 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1869 goto loser; |
|
1870 } |
|
1871 |
|
1872 /* create the digest info */ |
|
1873 di = SGN_CreateDigestInfo(p12ecx->p12exp->integrityInfo.pwdInfo.algorithm, |
|
1874 hmac.data, hmac.len); |
|
1875 if(!di) { |
|
1876 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1877 rv = SECFailure; |
|
1878 goto loser; |
|
1879 } |
|
1880 |
|
1881 rv = SGN_CopyDigestInfo(p12ecx->arena, &p12ecx->mac.safeMac, di); |
|
1882 if(rv != SECSuccess) { |
|
1883 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1884 goto loser; |
|
1885 } |
|
1886 |
|
1887 /* encode the mac data */ |
|
1888 dummy = SEC_ASN1EncodeItem(p12ecx->arena, &p12ecx->pfx.encodedMacData, |
|
1889 &p12ecx->mac, sec_PKCS12MacDataTemplate); |
|
1890 if(!dummy) { |
|
1891 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1892 rv = SECFailure; |
|
1893 } |
|
1894 |
|
1895 loser: |
|
1896 if(di) { |
|
1897 SGN_DestroyDigestInfo(di); |
|
1898 } |
|
1899 if(hmac.data) { |
|
1900 SECITEM_ZfreeItem(&hmac, PR_FALSE); |
|
1901 } |
|
1902 PK11_DestroyContext(p12ecx->hmacCx, PR_TRUE); |
|
1903 p12ecx->hmacCx = NULL; |
|
1904 |
|
1905 return rv; |
|
1906 } |
|
1907 |
|
1908 /* pfx notify function for ASN1 encoder. |
|
1909 * We want to stop encoding once we reach the authenticated safe. |
|
1910 * At that point, the encoder will be updated via streaming |
|
1911 * as the authenticated safe is encoded. |
|
1912 */ |
|
1913 static void |
|
1914 sec_pkcs12_encoder_pfx_notify(void *arg, PRBool before, void *dest, int real_depth) |
|
1915 { |
|
1916 sec_PKCS12EncoderContext *p12ecx; |
|
1917 |
|
1918 if(!before) { |
|
1919 return; |
|
1920 } |
|
1921 |
|
1922 /* look for authenticated safe */ |
|
1923 p12ecx = (sec_PKCS12EncoderContext*)arg; |
|
1924 if(dest != &p12ecx->pfx.encodedAuthSafe) { |
|
1925 return; |
|
1926 } |
|
1927 |
|
1928 SEC_ASN1EncoderSetTakeFromBuf(p12ecx->outerA1ecx); |
|
1929 SEC_ASN1EncoderSetStreaming(p12ecx->outerA1ecx); |
|
1930 SEC_ASN1EncoderClearNotifyProc(p12ecx->outerA1ecx); |
|
1931 } |
|
1932 |
|
1933 /* SEC_PKCS12Encode |
|
1934 * Encodes the PFX item and returns it to the output function, via |
|
1935 * callback. the output function must be capable of multiple updates. |
|
1936 * |
|
1937 * p12exp - the export context |
|
1938 * output - the output function callback, will be called more than once, |
|
1939 * must be able to accept streaming data. |
|
1940 * outputarg - argument for the output callback. |
|
1941 */ |
|
1942 SECStatus |
|
1943 SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp, |
|
1944 SEC_PKCS12EncoderOutputCallback output, void *outputarg) |
|
1945 { |
|
1946 sec_PKCS12EncoderContext *p12enc; |
|
1947 struct sec_pkcs12_encoder_output outInfo; |
|
1948 SECStatus rv; |
|
1949 |
|
1950 if(!p12exp || !output) { |
|
1951 return SECFailure; |
|
1952 } |
|
1953 |
|
1954 /* get the encoder context */ |
|
1955 p12enc = sec_pkcs12_encoder_start_context(p12exp); |
|
1956 if(!p12enc) { |
|
1957 return SECFailure; |
|
1958 } |
|
1959 |
|
1960 outInfo.outputfn = output; |
|
1961 outInfo.outputarg = outputarg; |
|
1962 |
|
1963 /* set up PFX encoder, the "outer" encoder. Set it for streaming */ |
|
1964 p12enc->outerA1ecx = SEC_ASN1EncoderStart(&p12enc->pfx, |
|
1965 sec_PKCS12PFXItemTemplate, |
|
1966 sec_P12A1OutputCB_Outer, |
|
1967 &outInfo); |
|
1968 if(!p12enc->outerA1ecx) { |
|
1969 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
1970 rv = SECFailure; |
|
1971 goto loser; |
|
1972 } |
|
1973 SEC_ASN1EncoderSetStreaming(p12enc->outerA1ecx); |
|
1974 SEC_ASN1EncoderSetNotifyProc(p12enc->outerA1ecx, |
|
1975 sec_pkcs12_encoder_pfx_notify, p12enc); |
|
1976 rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0); |
|
1977 if(rv != SECSuccess) { |
|
1978 rv = SECFailure; |
|
1979 goto loser; |
|
1980 } |
|
1981 |
|
1982 /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */ |
|
1983 p12enc->middleP7ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo, |
|
1984 sec_P12P7OutputCB_CallA1Update, |
|
1985 p12enc->outerA1ecx, NULL); |
|
1986 if(!p12enc->middleP7ecx) { |
|
1987 rv = SECFailure; |
|
1988 goto loser; |
|
1989 } |
|
1990 |
|
1991 /* encode asafe */ |
|
1992 p12enc->middleBuf.p7eCx = p12enc->middleP7ecx; |
|
1993 p12enc->middleBuf.hmacCx = NULL; |
|
1994 p12enc->middleBuf.numBytes = 0; |
|
1995 p12enc->middleBuf.bufBytes = sizeof p12enc->middleBuf.buf; |
|
1996 |
|
1997 /* Setup the "inner ASN.1 encoder for Authenticated Safes. */ |
|
1998 if(p12enc->p12exp->integrityEnabled && |
|
1999 p12enc->p12exp->pwdIntegrity) { |
|
2000 p12enc->middleBuf.hmacCx = p12enc->hmacCx; |
|
2001 } |
|
2002 p12enc->middleA1ecx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe, |
|
2003 sec_PKCS12AuthenticatedSafeTemplate, |
|
2004 sec_P12A1OutputCB_HmacP7Update, |
|
2005 &p12enc->middleBuf); |
|
2006 if(!p12enc->middleA1ecx) { |
|
2007 rv = SECFailure; |
|
2008 goto loser; |
|
2009 } |
|
2010 SEC_ASN1EncoderSetStreaming(p12enc->middleA1ecx); |
|
2011 SEC_ASN1EncoderSetTakeFromBuf(p12enc->middleA1ecx); |
|
2012 |
|
2013 /* encode each of the safes */ |
|
2014 while(p12enc->currentSafe != p12enc->p12exp->safeInfoCount) { |
|
2015 sec_pkcs12_encoder_asafe_process(p12enc); |
|
2016 p12enc->currentSafe++; |
|
2017 } |
|
2018 SEC_ASN1EncoderClearTakeFromBuf(p12enc->middleA1ecx); |
|
2019 SEC_ASN1EncoderClearStreaming(p12enc->middleA1ecx); |
|
2020 SEC_ASN1EncoderUpdate(p12enc->middleA1ecx, NULL, 0); |
|
2021 SEC_ASN1EncoderFinish(p12enc->middleA1ecx); |
|
2022 p12enc->middleA1ecx = NULL; |
|
2023 |
|
2024 sec_FlushPkcs12OutputBuffer( &p12enc->middleBuf); |
|
2025 |
|
2026 /* finish the encoding of the authenticated safes */ |
|
2027 rv = SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12exp->pwfn, |
|
2028 p12exp->pwfnarg); |
|
2029 p12enc->middleP7ecx = NULL; |
|
2030 if(rv != SECSuccess) { |
|
2031 goto loser; |
|
2032 } |
|
2033 |
|
2034 SEC_ASN1EncoderClearTakeFromBuf(p12enc->outerA1ecx); |
|
2035 SEC_ASN1EncoderClearStreaming(p12enc->outerA1ecx); |
|
2036 |
|
2037 /* update the mac, if necessary */ |
|
2038 rv = sec_Pkcs12FinishMac(p12enc); |
|
2039 if(rv != SECSuccess) { |
|
2040 goto loser; |
|
2041 } |
|
2042 |
|
2043 /* finish encoding the pfx */ |
|
2044 rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0); |
|
2045 |
|
2046 SEC_ASN1EncoderFinish(p12enc->outerA1ecx); |
|
2047 p12enc->outerA1ecx = NULL; |
|
2048 |
|
2049 loser: |
|
2050 sec_pkcs12_encoder_destroy_context(p12enc); |
|
2051 return rv; |
|
2052 } |
|
2053 |
|
2054 void |
|
2055 SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12ecx) |
|
2056 { |
|
2057 int i = 0; |
|
2058 |
|
2059 if(!p12ecx) { |
|
2060 return; |
|
2061 } |
|
2062 |
|
2063 if(p12ecx->safeInfos) { |
|
2064 i = 0; |
|
2065 while(p12ecx->safeInfos[i] != NULL) { |
|
2066 if(p12ecx->safeInfos[i]->encryptionKey) { |
|
2067 PK11_FreeSymKey(p12ecx->safeInfos[i]->encryptionKey); |
|
2068 } |
|
2069 if(p12ecx->safeInfos[i]->cinfo) { |
|
2070 SEC_PKCS7DestroyContentInfo(p12ecx->safeInfos[i]->cinfo); |
|
2071 } |
|
2072 i++; |
|
2073 } |
|
2074 } |
|
2075 |
|
2076 PK11_FreeSlot(p12ecx->slot); |
|
2077 |
|
2078 PORT_FreeArena(p12ecx->arena, PR_TRUE); |
|
2079 } |