]> git.saurik.com Git - apple/security.git/blob - SecurityTests/clxutils/cmstool/cmstool.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / clxutils / cmstool / cmstool.cpp
1 /*
2 * cmstool.cpp - manipluate CMS messages, intended to be an alternate for the
3 * currently useless cms command in /usr/bin/security
4 */
5
6 #include <Security/Security.h>
7 #include <security_cdsa_utils/cuFileIo.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <utilLib/common.h>
13 #include <security_cdsa_utils/cuFileIo.h>
14 #include <security_cdsa_utils/cuPrintCert.h>
15 #include <clAppUtils/identPicker.h>
16 #include <clAppUtils/sslAppUtils.h>
17 #include <security_cdsa_utils/cuOidParser.h>
18 #include <CoreFoundation/CoreFoundation.h>
19 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
20 #include <Security/SecCmsEncoder.h>
21 #include <Security/SecCmsDecoder.h>
22 #include <Security/SecCmsEncryptedData.h>
23 #include <Security/SecCmsEnvelopedData.h>
24 #include <Security/SecCmsMessage.h>
25 #include <Security/SecCmsRecipientInfo.h>
26 #include <Security/SecCmsSignedData.h>
27 #include <Security/SecCmsSignerInfo.h>
28 #include <Security/SecCmsContentInfo.h>
29 #include <Security/SecCmsDigestContext.h>
30 #include <Security/SecTrustPriv.h>
31 #include <Security/SecKeychainItemPriv.h>
32 #include <Security/SecCertificate.h>
33 #include <Security/SecSMIME.h>
34 #include <Security/oidsattr.h>
35 #include <Security/SecAsn1Coder.h>
36 #include <Security/secasn1t.h>
37 #include <Security/SecAsn1Templates.h>
38
39 static void usage(char **argv)
40 {
41 printf("Usage: %s cmd [option ...]\n", argv[0]);
42 printf("cmd values:\n");
43 printf(" sign -- create signedData\n");
44 printf(" envel -- create envelopedData\n");
45 printf(" signEnv -- create nested EnvelopedData(signedData(data))\n");
46 printf(" parse -- parse a CMS message file\n");
47 printf("Options:\n");
48 printf(" -i infile\n");
49 printf(" -o outfile\n");
50 printf(" -k keychain -- Keychain to search for certs\n");
51 printf(" -p -- Use identity picker\n");
52 printf(" -r recipient -- specify recipient of enveloped data\n");
53 printf(" -c -- parse signer cert\n");
54 printf(" -v sign|encr -- verify message is signed/encrypted\n");
55 printf(" -e eContentType -- a(uthData)|r(keyData)\n");
56 printf(" -d detached -- infile contains detached content (sign only)\n");
57 printf(" -D detachedContent -- detached content (parse only)\n");
58 printf(" -q -- quiet\n");
59 exit(1);
60 }
61
62 /* high level op */
63 typedef enum {
64 CTO_Sign,
65 CTO_Envelop,
66 CTO_SignEnvelop,
67 CTO_Parse
68 } CT_Op;
69
70 /* to verify */
71 typedef enum {
72 CTV_None,
73 CTV_Sign,
74 CTV_Envelop,
75 CTV_SignEnvelop
76 } CT_Vfy;
77
78 /* additional OIDS to specify as eContentType */
79 #define OID_PKINIT 0x2B, 6, 1, 5, 2, 3
80 #define OID_PKINIT_LEN 6
81
82 static const uint8 OID_PKINIT_AUTH_DATA[] = {OID_PKINIT, 1};
83 static const uint8 OID_PKINIT_DH_KEY_DATA[] = {OID_PKINIT, 2};
84 static const uint8 OID_PKINIT_RKEY_DATA[] = {OID_PKINIT, 3};
85 static const uint8 OID_PKINIT_KP_CLIENTAUTH[] = {OID_PKINIT, 3};
86 static const uint8 OID_PKINIT_KPKDC[] = {OID_PKINIT, 5};
87
88 static const CSSM_OID CSSMOID_PKINIT_AUTH_DATA =
89 {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_AUTH_DATA};
90 static const CSSM_OID CSSMOID_PKINIT_DH_KEY_DATA =
91 {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_DH_KEY_DATA};
92 static const CSSM_OID CSSMOID_PKINIT_RKEY_DATA =
93 {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_RKEY_DATA};
94 static const CSSM_OID CSSMOID_PKINIT_KP_CLIENTAUTH =
95 {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KP_CLIENTAUTH};
96 static const CSSM_OID CSSMOID_PKINIT_KPKDC =
97 {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KPKDC};
98
99 typedef struct {
100 CSSM_OID contentType;
101 CSSM_DATA content;
102 } SimpleContentInfo;
103
104 const SecAsn1Template SimpleContentInfoTemplate[] = {
105 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SimpleContentInfo) },
106 { SEC_ASN1_OBJECT_ID, offsetof(SimpleContentInfo, contentType) },
107 { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
108 offsetof(SimpleContentInfo, content),
109 kSecAsn1AnyTemplate },
110 { 0, }
111 };
112
113 /*
114 * Obtain the content of a contentInfo, This basically strips off the contentType OID
115 * and returns a mallocd copy of the ASN_ANY content.
116 */
117 static OSStatus ContentInfoContent(
118 const unsigned char *contentInfo,
119 unsigned contentInfoLen,
120 unsigned char **content, /* mallocd and RETURNED */
121 unsigned *contentLen) /* RETURNED */
122 {
123 SecAsn1CoderRef coder = NULL;
124 OSStatus ortn;
125 SimpleContentInfo decodedInfo;
126
127 ortn = SecAsn1CoderCreate(&coder);
128 if(ortn) {
129 return ortn;
130 }
131 memset(&decodedInfo, 0, sizeof(decodedInfo));
132 ortn = SecAsn1Decode(coder, contentInfo, contentInfoLen,
133 SimpleContentInfoTemplate, &decodedInfo);
134 if(ortn) {
135 goto errOut;
136 }
137 if(decodedInfo.content.Data == NULL) {
138 printf("***Error decoding contentInfo: no content\n");
139 ortn = internalComponentErr;
140 goto errOut;
141 }
142 *content = (unsigned char *)malloc(decodedInfo.content.Length);
143 memmove(*content, decodedInfo.content.Data, decodedInfo.content.Length);
144 *contentLen = decodedInfo.content.Length;
145 errOut:
146 SecAsn1CoderRelease(coder);
147 return ortn;
148 }
149
150 /*
151 * Find a cert in specified keychain or keychain list matching specified
152 * email address. We happen to knopw that the email address is stored with the
153 * kSecKeyAlias attribute.
154 */
155 static OSStatus findCert(
156 const char *emailAddress,
157 CFTypeRef kcArArray, // kc, array, or even NULL
158 SecCertificateRef *cert)
159 {
160 OSStatus ortn;
161 SecKeychainSearchRef srch;
162 SecKeychainAttributeList attrList;
163 SecKeychainAttribute attr;
164
165 attr.tag = kSecKeyAlias;
166 attr.length = strlen(emailAddress);
167 attr.data = (void *)emailAddress;
168 attrList.count = 1;
169 attrList.attr = &attr;
170
171 ortn = SecKeychainSearchCreateFromAttributes(kcArArray,
172 kSecCertificateItemClass,
173 &attrList,
174 &srch);
175 if(ortn) {
176 cssmPerror("SecKeychainSearchCreateFromAttributes", ortn);
177 return ortn;
178 }
179
180 ortn = SecKeychainSearchCopyNext(srch, (SecKeychainItemRef *)cert);
181 if(ortn) {
182 printf("***No certs founmd matching recipient %s. Aborting.\n",
183 emailAddress);
184 return ortn;
185 }
186 CFRelease(srch);
187 return noErr;
188 }
189
190 static void evalSecTrust(
191 SecTrustRef secTrust,
192 bool quiet)
193 {
194 OSStatus ortn;
195 SecTrustResultType secTrustResult;
196
197 ortn = SecTrustEvaluate(secTrust, &secTrustResult);
198 if(ortn) {
199 /* should never happen */
200 cssmPerror("SecTrustEvaluate", ortn);
201 return;
202 }
203 switch(secTrustResult) {
204 case kSecTrustResultUnspecified:
205 /* cert chain valid, no special UserTrust assignments */
206 case kSecTrustResultProceed:
207 /* cert chain valid AND user explicitly trusts this */
208 if(!quiet) {
209 fprintf(stderr, "Successful\n");
210 }
211 return;
212 case kSecTrustResultDeny:
213 case kSecTrustResultConfirm:
214 /*
215 * Cert chain may well have verified OK, but user has flagged
216 * one of these certs as untrustable.
217 */
218 printf("Not trusted per user-specified Trust level\n");
219 return;
220 default:
221 {
222 /* get low-level TP error */
223 OSStatus tpStatus;
224 ortn = SecTrustGetCssmResultCode(secTrust, &tpStatus);
225 if(ortn) {
226 cssmPerror("SecTrustGetCssmResultCode", ortn);
227 return;
228 }
229 switch(tpStatus) {
230 case CSSMERR_TP_INVALID_ANCHOR_CERT:
231 fprintf(stderr, "Untrusted root\n");
232 return;
233 case CSSMERR_TP_NOT_TRUSTED:
234 /* no root, not even in implicit SSL roots */
235 fprintf(stderr, "No root cert found\n");
236 return;
237 case CSSMERR_TP_CERT_EXPIRED:
238 fprintf(stderr, "Expired cert\n");
239 return;
240 case CSSMERR_TP_CERT_NOT_VALID_YET:
241 fprintf(stderr, "Cert not valid yet\n");
242 break;
243 default:
244 printf("Other cert failure: ");
245 cssmPerror("", tpStatus);
246 return;
247 }
248 }
249 } /* SecTrustEvaluate error */
250
251 }
252 static OSStatus parseSignedData(
253 SecCmsSignedDataRef signedData,
254 SecArenaPoolRef arena, /* used for detached content only */
255 const unsigned char *detachedData,
256 unsigned detachedDataLen,
257 CT_Vfy vfyOp,
258 bool quiet,
259 bool parseSignerCert)
260 {
261 Boolean b;
262 b = SecCmsSignedDataHasDigests(signedData);
263 if(!quiet) {
264 printf(" has digests : %s\n", b ? "true" : "false");
265 }
266
267 SecTrustRef secTrust = NULL;
268 OSStatus ortn;
269 SecPolicyRef policy = NULL;
270 SecPolicySearchRef policySearch = NULL;
271
272 ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
273 &CSSMOID_APPLE_X509_BASIC,
274 NULL,
275 &policySearch);
276 if(ortn) {
277 cssmPerror("SecPolicySearchCreate", ortn);
278 return ortn;
279 }
280 ortn = SecPolicySearchCopyNext(policySearch, &policy);
281 if(ortn) {
282 cssmPerror("SecPolicySearchCopyNext", ortn);
283 return ortn;
284 }
285
286 int numSigners = SecCmsSignedDataSignerInfoCount(signedData);
287 if(!quiet) {
288 printf(" num signers : %d\n", numSigners);
289 }
290 for(int dex=0; dex<numSigners; dex++) {
291 if(!quiet) {
292 fprintf(stderr, " signer %d :\n", dex);
293 fprintf(stderr, " vfy status : ");
294 }
295 Boolean b = SecCmsSignedDataHasDigests(signedData);
296 if(b) {
297 if(detachedData != NULL) {
298 fprintf(stderr, "<provided detachedContent, but msg has digests> ");
299 /* FIXME - does this make sense? Error? */
300 }
301 }
302 else if(detachedData != NULL) {
303 /* digest the detached content */
304 SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(signedData);
305 SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestAlgorithms);
306 CSSM_DATA **digests = NULL;
307
308 SecCmsDigestContextUpdate(digcx, detachedData, detachedDataLen);
309 ortn = SecCmsDigestContextFinishMultiple(digcx, arena, &digests);
310 if(ortn) {
311 fprintf(stderr, "SecCmsDigestContextFinishMultiple() returned %d\n", (int)ortn);
312 }
313 else {
314 SecCmsSignedDataSetDigests(signedData, digestAlgorithms, digests);
315 }
316 }
317 else {
318 fprintf(stderr, "<Msg has no digest: need detachedContent> ");
319 }
320 ortn = SecCmsSignedDataVerifySignerInfo(signedData, dex, NULL,
321 policy, &secTrust);
322 if(ortn) {
323 fprintf(stderr, "vfSignerInfo() returned %d\n", (int)ortn);
324 fprintf(stderr, " vfy status : ");
325 }
326 if(secTrust == NULL) {
327 fprintf(stderr, "***NO SecTrust available!\n");
328 }
329 else {
330 evalSecTrust(secTrust, quiet);
331 }
332
333 SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, dex);
334 CFStringRef emailAddrs = SecCmsSignerInfoGetSignerCommonName(signerInfo);
335 char emailStr[1000];
336 if(!quiet) {
337 fprintf(stderr, " signer : ");
338 }
339 if(emailAddrs == NULL) {
340 fprintf(stderr, "<<SecCmsSignerInfoGetSignerCommonName returned NULL)>>\n");
341 }
342 else {
343 if(!CFStringGetCString(emailAddrs, emailStr, 1000, kCFStringEncodingASCII)) {
344 fprintf(stderr, "*** Error converting email address to C string\n");
345 }
346 else if(!quiet) {
347
348 fprintf(stderr, "%s\n", emailStr);
349 }
350 }
351 if(parseSignerCert) {
352 SecCertificateRef signer;
353 signer = SecCmsSignerInfoGetSigningCertificate(signerInfo, NULL);
354 if(signer) {
355 CSSM_DATA certData;
356 ortn = SecCertificateGetData(signer, &certData);
357 if(ortn) {
358 fprintf(stderr, "***Error getting signing cert data***\n");
359 cssmPerror("SecCertificateGetData", ortn);
360 }
361 else {
362 printf("========== Signer Cert==========\n\n");
363 printCert(certData.Data, certData.Length, CSSM_FALSE);
364 printf("========== End Signer Cert==========\n\n");
365 }
366 }
367 else {
368 fprintf(stderr, "***Error getting signing cert ***\n");
369 }
370 }
371 }
372 return ortn;
373 }
374
375 static OSStatus doParse(
376 const unsigned char *data,
377 unsigned dataLen,
378 const unsigned char *detachedData,
379 unsigned detachedDataLen,
380 CT_Vfy vfyOp,
381 bool parseSignerCert,
382 bool quiet,
383 unsigned char **outData, // mallocd and RETURNED
384 unsigned *outDataLen) // RETURNED
385 {
386 if((data == NULL) || (dataLen == 0)) {
387 fprintf(stderr, "***Parse requires input file. Aborting.\n");
388 return paramErr;
389 }
390
391 SecArenaPoolRef arena = NULL;
392 SecArenaPoolCreate(1024, &arena);
393 SecCmsMessageRef cmsMsg = NULL;
394 SecCmsDecoderRef decoder;
395 OSStatus ortn;
396 OSStatus ourRtn = noErr;
397 bool foundOneSigned = false;
398 bool foundOneEnveloped = false;
399
400 ortn = SecCmsDecoderCreate(arena, NULL, NULL, NULL, NULL, NULL, NULL, &decoder);
401 if(ortn) {
402 cssmPerror("SecCmsDecoderCreate", ortn);
403 return ortn;
404 }
405 ortn = SecCmsDecoderUpdate(decoder, data, dataLen);
406 if(ortn) {
407 cssmPerror("SecCmsDecoderUpdate", ortn);
408 return ortn;
409 }
410 ortn = SecCmsDecoderFinish(decoder, &cmsMsg);
411 if(ortn) {
412 cssmPerror("SecCmsDecoderFinish", ortn);
413 return ortn;
414 }
415
416 Boolean b = SecCmsMessageIsSigned(cmsMsg);
417 switch(vfyOp) {
418 case CTV_None:
419 break;
420 case CTV_Sign:
421 if(!b) {
422 fprintf(stderr, "***Expected SignedData, but !SecCmsMessageIsSigned()\n");
423 ourRtn = -1;
424 }
425 break;
426 case CTV_SignEnvelop:
427 if(!b) {
428 fprintf(stderr, "***Expected Signed&Enveloped, but !SecCmsMessageIsSigned()\n");
429 ourRtn = -1;
430 }
431 break;
432 case CTV_Envelop:
433 if(b) {
434 fprintf(stderr, "***Expected EnvelopedData, but SecCmsMessageIsSigned() "
435 "TRUE\n");
436 ourRtn = -1;
437 }
438 break;
439 }
440 int numContentInfos = SecCmsMessageContentLevelCount(cmsMsg);
441 if(!quiet) {
442 fprintf(stderr, "=== CMS message info ===\n");
443 fprintf(stderr, " Signed : %s\n", b ? "true" : "false");
444 b = SecCmsMessageIsEncrypted(cmsMsg);
445 fprintf(stderr, " Encrypted : %s\n", b ? "true" : "false");
446 b = SecCmsMessageContainsCertsOrCrls(cmsMsg);
447 fprintf(stderr, " certs/crls : %s\n", b ? "present" : "not present");
448 fprintf(stderr, " Num ContentInfos : %d\n", numContentInfos);
449 }
450
451 /* FIXME needs work for CTV_SignEnvelop */
452 OidParser oidParser;
453 for(int dex=0; dex<numContentInfos; dex++) {
454 SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsMsg, dex);
455 if(!quiet) {
456 /* can't use stderr - oidparser is fixed w/stdout */
457 printf(" Content Info %d :\n", dex);
458 CSSM_OID *typeOid = SecCmsContentInfoGetContentTypeOID(ci);
459 printf(" OID Tag : ");
460 if(typeOid == NULL) {
461 printf("***NONE FOUND***]n");
462 }
463 else if(typeOid->Length == 0) {
464 printf("***EMPTY***\n");
465 }
466 else {
467 char str[OID_PARSER_STRING_SIZE];
468 oidParser.oidParse(typeOid->Data, typeOid->Length, str);
469 printf("%s\n", str);
470 }
471 }
472 SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
473 switch(tag) {
474 case SEC_OID_PKCS7_SIGNED_DATA:
475 {
476 switch(vfyOp) {
477 case CTV_None: // caller doesn't care
478 case CTV_Sign: // got what we wanted
479 break;
480 case CTV_Envelop:
481 fprintf(stderr, "***Expected EnvelopedData, got SignedData\n");
482 ourRtn = -1;
483 break;
484 case CTV_SignEnvelop:
485 printf("CTV_SignEnvelop code on demand\n");
486 break;
487 }
488 foundOneSigned = true;
489 SecCmsSignedDataRef sd =
490 (SecCmsSignedDataRef) SecCmsContentInfoGetContent(ci);
491 parseSignedData(sd, arena,
492 detachedData, detachedDataLen,
493 vfyOp, quiet, parseSignerCert);
494 break;
495 }
496 case SEC_OID_PKCS7_DATA:
497 case SEC_OID_OTHER:
498 break;
499 case SEC_OID_PKCS7_ENVELOPED_DATA:
500 foundOneEnveloped = true;
501 if(vfyOp == CTV_Sign) {
502 fprintf(stderr, "***Expected SignedData, EnvelopedData\n");
503 ourRtn = -1;
504 break;
505 }
506 case SEC_OID_PKCS7_ENCRYPTED_DATA:
507 switch(vfyOp) {
508 case CTV_None:
509 break;
510 case CTV_Sign:
511 fprintf(stderr, "***Expected SignedData, got EncryptedData\n");
512 ourRtn = -1;
513 break;
514 case CTV_Envelop:
515 fprintf(stderr, "***Expected EnvelopedData, got EncryptedData\n");
516 ourRtn = -1;
517 break;
518 case CTV_SignEnvelop:
519 printf("CTV_SignEnvelop code on demand\n");
520 break;
521 }
522 break;
523 default:
524 fprintf(stderr, " other content type TBD\n");
525 }
526 }
527 if(outData) {
528 CSSM_DATA_PTR odata = SecCmsMessageGetContent(cmsMsg);
529 if(odata == NULL) {
530 fprintf(stderr, "***No inner content available\n");
531 }
532 else {
533 *outData = (unsigned char *)malloc(odata->Length);
534 memmove(*outData, odata->Data, odata->Length);
535 *outDataLen = odata->Length;
536 }
537 }
538 if(arena) {
539 SecArenaPoolFree(arena, false);
540 }
541 switch(vfyOp) {
542 case CTV_None:
543 break;
544 case CTV_Sign:
545 if(!foundOneSigned) {
546 fprintf(stderr, "Expected signed, never saw a SignedData\n");
547 ourRtn = -1;
548 }
549 break;
550 case CTV_Envelop:
551 if(!foundOneEnveloped) {
552 fprintf(stderr, "Expected enveloped, never saw an EnvelopedData\n");
553 ourRtn = -1;
554 }
555 break;
556 case CTV_SignEnvelop:
557 if(!foundOneSigned) {
558 fprintf(stderr, "Expected signed, never saw a SignedData\n");
559 ourRtn = -1;
560 }
561 if(!foundOneEnveloped) {
562 fprintf(stderr, "Expected enveloped, never saw an EnvelopedData\n");
563 ourRtn = -1;
564 }
565 break;
566 }
567 /* free decoder? cmsMsg? */
568 return ourRtn;
569 }
570
571 /*
572 * Common encode routine.
573 */
574 #if 1
575 /* the simple way, when 3655861 is fixed */
576 static OSStatus encodeCms(
577 SecCmsMessageRef cmsMsg,
578 const unsigned char *inData, // add in this
579 unsigned inDataLen,
580 unsigned char **outData, // mallocd and RETURNED
581 unsigned *outDataLen) // RETURNED
582 {
583 SecArenaPoolRef arena = NULL;
584 SecArenaPoolCreate(1024, &arena);
585 CSSM_DATA cdataIn = {inDataLen, (uint8 *)inData};
586 CSSM_DATA cdataOut = {0, NULL};
587
588 OSStatus ortn = SecCmsMessageEncode(cmsMsg, &cdataIn, arena, &cdataOut);
589 if((ortn == noErr) && (cdataOut.Length != 0)) {
590 *outData = (unsigned char *)malloc(cdataOut.Length);
591 memmove(*outData, cdataOut.Data, cdataOut.Length);
592 *outDataLen = cdataOut.Length;
593 }
594 else {
595 cssmPerror("SecCmsMessageEncode", ortn);
596 *outData = NULL;
597 *outDataLen = 0;
598 }
599 SecArenaPoolFree(arena, false);
600 return ortn;
601 }
602
603 #else
604
605 /* the hard way back when SecCmsMessageEncode() didn't work */
606 static OSStatus encodeCms(
607 SecCmsMessageRef cmsMsg,
608 const unsigned char *inData, // add in this
609 unsigned inDataLen,
610 unsigned char **outData, // mallocd and RETURNED
611 unsigned *outDataLen) // RETURNED
612 {
613 SecArenaPoolRef arena = NULL;
614 SecArenaPoolCreate(1024, &arena);
615 SecCmsEncoderRef cmsEnc = NULL;
616 CSSM_DATA output = { 0, NULL };
617 OSStatus ortn;
618
619 ortn = SecCmsEncoderCreate(cmsMsg,
620 NULL, NULL, // no callback
621 &output, arena, // data goes here
622 NULL, NULL, // no password callback (right?)
623 NULL, NULL, // decrypt key callback
624 NULL, NULL, // detached digests
625 &cmsEnc);
626 if(ortn) {
627 cssmPerror("SecKeychainItemCopyKeychain", ortn);
628 goto errOut;
629 }
630 ortn = SecCmsEncoderUpdate(cmsEnc, (char *)inData, inDataLen);
631 if(ortn) {
632 cssmPerror("SecCmsEncoderUpdate", ortn);
633 goto errOut;
634 }
635 ortn = SecCmsEncoderFinish(cmsEnc);
636 if(ortn) {
637 cssmPerror("SecCMsEncoderFinish", ortn);
638 goto errOut;
639 }
640
641 /* Did we get any data? */
642 if(output.Length) {
643 *outData = (unsigned char *)malloc(output.Length);
644 memmove(*outData, output.Data, output.Length);
645 *outDataLen = output.Length;
646 }
647 else {
648 *outData = NULL;
649 *outDataLen = 0;
650 }
651 errOut:
652 if(arena) {
653 SecArenaPoolFree(arena, false);
654 }
655 return ortn;
656 }
657
658 #endif
659
660 static OSStatus doSign(
661 SecIdentityRef signerId,
662 const unsigned char *inData,
663 unsigned inDataLen,
664 bool detachedContent,
665 const CSSM_OID *eContentType, // OPTIONAL
666 unsigned char **outData, // mallocd and RETURNED
667 unsigned *outDataLen) // RETURNED
668 {
669 if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
670 fprintf(stderr, "***Sign requires input file. Aborting.\n");
671 return paramErr;
672 }
673 if(signerId == NULL) {
674 fprintf(stderr, "***Sign requires a signing identity. Aborting.\n");
675 return paramErr;
676 }
677
678 SecCmsMessageRef cmsMsg = NULL;
679 SecCmsContentInfoRef contentInfo = NULL;
680 SecCmsSignedDataRef signedData = NULL;
681 SecCertificateRef ourCert = NULL;
682 SecCmsSignerInfoRef signerInfo;
683 OSStatus ortn;
684 SecKeychainRef ourKc = NULL;
685
686 ortn = SecIdentityCopyCertificate(signerId, &ourCert);
687 if(ortn) {
688 cssmPerror("SecIdentityCopyCertificate", ortn);
689 return ortn;
690 }
691 ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)ourCert, &ourKc);
692 if(ortn) {
693 cssmPerror("SecKeychainItemCopyKeychain", ortn);
694 goto errOut;
695 }
696
697 // build chain of objects: message->signedData->data
698 cmsMsg = SecCmsMessageCreate(NULL);
699 if(cmsMsg == NULL) {
700 fprintf(stderr, "***Error creating SecCmsMessageRef\n");
701 ortn = -1;
702 goto errOut;
703 }
704 signedData = SecCmsSignedDataCreate(cmsMsg);
705 if(signedData == NULL) {
706 printf("***Error creating SecCmsSignedDataRef\n");
707 ortn = -1;
708 goto errOut;
709 }
710 contentInfo = SecCmsMessageGetContentInfo(cmsMsg);
711 ortn = SecCmsContentInfoSetContentSignedData(cmsMsg, contentInfo, signedData);
712 if(ortn) {
713 cssmPerror("SecCmsContentInfoSetContentSignedData", ortn);
714 goto errOut;
715 }
716 contentInfo = SecCmsSignedDataGetContentInfo(signedData);
717 if(eContentType != NULL) {
718 ortn = SecCmsContentInfoSetContentOther(cmsMsg, contentInfo,
719 NULL /* data */,
720 detachedContent,
721 eContentType);
722 if(ortn) {
723 cssmPerror("SecCmsContentInfoSetContentData", ortn);
724 goto errOut;
725 }
726 }
727 else {
728 ortn = SecCmsContentInfoSetContentData(cmsMsg, contentInfo, NULL /* data */,
729 detachedContent);
730 if(ortn) {
731 cssmPerror("SecCmsContentInfoSetContentData", ortn);
732 goto errOut;
733 }
734 }
735
736 /*
737 * create & attach signer information
738 */
739 signerInfo = SecCmsSignerInfoCreate(cmsMsg, signerId, SEC_OID_SHA1);
740 if (signerInfo == NULL) {
741 fprintf(stderr, "***Error on SecCmsSignerInfoCreate\n");
742 ortn = -1;
743 goto errOut;
744 }
745 /* we want the cert chain included for this one */
746 /* FIXME - what's the significance of the usage? */
747 ortn = SecCmsSignerInfoIncludeCerts(signerInfo, SecCmsCMCertChain, certUsageEmailSigner);
748 if(ortn) {
749 cssmPerror("SecCmsSignerInfoIncludeCerts", ortn);
750 goto errOut;
751 }
752
753 /* other options go here - signing time, etc. */
754
755 ortn = SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc);
756 if(ortn) {
757 cssmPerror("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
758 goto errOut;
759 }
760 ortn = SecCmsSignedDataAddCertificate(signedData, ourCert);
761 if(ortn) {
762 cssmPerror("SecCmsSignedDataAddCertificate", ortn);
763 goto errOut;
764 }
765
766 ortn = SecCmsSignedDataAddSignerInfo(signedData, signerInfo);
767 if(ortn) {
768 cssmPerror("SecCmsSignedDataAddSignerInfo", ortn);
769 goto errOut;
770 }
771
772 /* go */
773 ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen);
774 errOut:
775 /* free resources */
776 if(cmsMsg) {
777 SecCmsMessageDestroy(cmsMsg);
778 }
779 if(ourCert) {
780 CFRelease(ourCert);
781 }
782 if(ourKc) {
783 CFRelease(ourKc);
784 }
785 return ortn;
786 }
787
788 static OSStatus doEncrypt(
789 SecCertificateRef recipCert, // eventually more than one
790 const unsigned char *inData,
791 unsigned inDataLen,
792 unsigned char **outData, // mallocd and RETURNED
793 unsigned *outDataLen) // RETURNED
794 {
795 if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
796 fprintf(stderr, "***Encrypt requires input file. Aborting.\n");
797 return paramErr;
798 }
799 if(recipCert == NULL) {
800 fprintf(stderr, "***Encrypt requires a recipient certificate. Aborting.\n");
801 return paramErr;
802 }
803
804 SecCmsMessageRef cmsMsg = NULL;
805 SecCmsContentInfoRef contentInfo = NULL;
806 SecCmsEnvelopedDataRef envelopedData = NULL;
807 SecCmsRecipientInfoRef recipientInfo = NULL;
808 OSStatus ortn;
809 SecCertificateRef allCerts[2] = { recipCert, NULL};
810
811 SECOidTag algorithmTag;
812 int keySize;
813
814 ortn = SecSMIMEFindBulkAlgForRecipients(allCerts, &algorithmTag, &keySize);
815 if(ortn) {
816 cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn);
817 return ortn;
818 }
819
820 // build chain of objects: message->envelopedData->data
821 cmsMsg = SecCmsMessageCreate(NULL);
822 if(cmsMsg == NULL) {
823 fprintf(stderr, "***Error creating SecCmsMessageRef\n");
824 ortn = -1;
825 goto errOut;
826 }
827 envelopedData = SecCmsEnvelopedDataCreate(cmsMsg, algorithmTag, keySize);
828 if(envelopedData == NULL) {
829 fprintf(stderr, "***Error creating SecCmsEnvelopedDataRef\n");
830 ortn = -1;
831 goto errOut;
832 }
833 contentInfo = SecCmsMessageGetContentInfo(cmsMsg);
834 ortn = SecCmsContentInfoSetContentEnvelopedData(cmsMsg, contentInfo, envelopedData);
835 if(ortn) {
836 cssmPerror("SecCmsContentInfoSetContentEnvelopedData", ortn);
837 goto errOut;
838 }
839 contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData);
840 ortn = SecCmsContentInfoSetContentData(cmsMsg, contentInfo, NULL /* data */, false);
841 if(ortn) {
842 cssmPerror("SecCmsContentInfoSetContentData", ortn);
843 goto errOut;
844 }
845
846 /*
847 * create & attach recipient information
848 */
849 recipientInfo = SecCmsRecipientInfoCreate(cmsMsg, recipCert);
850 ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
851 if(ortn) {
852 cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn);
853 goto errOut;
854 }
855
856
857 /* go */
858 ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen);
859 errOut:
860 /* free resources */
861 if(cmsMsg) {
862 SecCmsMessageDestroy(cmsMsg);
863 }
864 return ortn;
865 }
866
867 /* create nested message: msg = EnvelopedData(SignedData(inData)) */
868 static OSStatus doSignEncrypt(
869 SecCertificateRef recipCert, // encryption recipient
870 SecIdentityRef signerId, // signer
871 const CSSM_OID *eContentType, // OPTIONAL - for signedData
872 const unsigned char *inData,
873 unsigned inDataLen,
874 unsigned char **outData, // mallocd and RETURNED
875 unsigned *outDataLen) // RETURNED
876 {
877 if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
878 fprintf(stderr, "***Sign/Encrypt requires input file. Aborting.\n");
879 return paramErr;
880 }
881 if(recipCert == NULL) {
882 fprintf(stderr, "***Sign/Encrypt requires a recipient certificate. Aborting.\n");
883 return paramErr;
884 }
885 if(signerId == NULL) {
886 fprintf(stderr, "***Sign/Encrypt requires a signer Identity. Aborting.\n");
887 return paramErr;
888 }
889
890 OSStatus ortn;
891 unsigned char *signedData = NULL;
892 unsigned signedDataLen = 0;
893 SecCmsMessageRef cmsMsg = NULL;
894 SecCmsContentInfoRef contentInfo = NULL;
895 SecCmsEnvelopedDataRef envelopedData = NULL;
896 SecCmsRecipientInfoRef recipientInfo = NULL;
897 SecCertificateRef allCerts[2] = { recipCert, NULL};
898 SECOidTag algorithmTag;
899 int keySize;
900
901 /* first get a SignedData */
902 ortn = doSign(signerId, inData, inDataLen,
903 false, /* can't do detached content here */
904 eContentType,
905 &signedData, &signedDataLen);
906 if(ortn) {
907 printf("***Error generating inner signedData. Aborting.\n");
908 return ortn;
909 }
910
911 /* extract just the content - don't need the whole ContentINfo */
912 unsigned char *signedDataContent = NULL;
913 unsigned signedDataContentLen = 0;
914 ortn = ContentInfoContent(signedData, signedDataLen, &signedDataContent, &signedDataContentLen);
915 if(ortn) {
916 goto errOut;
917 }
918
919 /* now wrap that in an EnvelopedData */
920 ortn = SecSMIMEFindBulkAlgForRecipients(allCerts, &algorithmTag, &keySize);
921 if(ortn) {
922 cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn);
923 return ortn;
924 }
925
926 // build chain of objects: message->envelopedData->data
927 cmsMsg = SecCmsMessageCreate(NULL);
928 if(cmsMsg == NULL) {
929 fprintf(stderr, "***Error creating SecCmsMessageRef\n");
930 ortn = -1;
931 goto errOut;
932 }
933 envelopedData = SecCmsEnvelopedDataCreate(cmsMsg, algorithmTag, keySize);
934 if(envelopedData == NULL) {
935 fprintf(stderr, "***Error creating SecCmsEnvelopedDataRef\n");
936 ortn = -1;
937 goto errOut;
938 }
939 contentInfo = SecCmsMessageGetContentInfo(cmsMsg);
940 ortn = SecCmsContentInfoSetContentEnvelopedData(cmsMsg, contentInfo, envelopedData);
941 if(ortn) {
942 cssmPerror("SecCmsContentInfoSetContentEnvelopedData", ortn);
943 goto errOut;
944 }
945 contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData);
946
947 /* here's the difference: we override the 'data' content with a SignedData type,
948 * but we fool the smime lib into thinking it's a plain old data so it doesn't try
949 * to encode the SignedData */
950 ortn = SecCmsContentInfoSetContentOther(cmsMsg, contentInfo,
951 NULL /* data */,
952 false,
953 &CSSMOID_PKCS7_SignedData);
954 if(ortn) {
955 cssmPerror("SecCmsContentInfoSetContentData", ortn);
956 goto errOut;
957 }
958
959 /*
960 * create & attach recipient information
961 */
962 recipientInfo = SecCmsRecipientInfoCreate(cmsMsg, recipCert);
963 ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
964 if(ortn) {
965 cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn);
966 goto errOut;
967 }
968
969
970 /* go */
971 ortn = encodeCms(cmsMsg, signedDataContent, signedDataContentLen, outData, outDataLen);
972 errOut:
973 /* free resources */
974 if(cmsMsg) {
975 SecCmsMessageDestroy(cmsMsg);
976 }
977 if(signedData) {
978 free(signedData);
979 }
980 if(signedDataContent) {
981 free(signedDataContent);
982 }
983 return ortn;
984 }
985
986 int main(int argc, char **argv)
987 {
988 if(argc < 2) {
989 usage(argv);
990 }
991
992 CT_Op op;
993 bool needId = false;
994 if(!strcmp(argv[1], "sign")) {
995 op = CTO_Sign;
996 needId = true;
997 }
998 else if(!strcmp(argv[1], "envel")) {
999 op = CTO_Envelop;
1000 }
1001 else if(!strcmp(argv[1], "signEnv")) {
1002 op = CTO_SignEnvelop;
1003 needId = true;
1004 }
1005 else if(!strcmp(argv[1], "parse")) {
1006 op = CTO_Parse;
1007 }
1008 else {
1009 fprintf(stderr, "***Unrecognized cmd.\n");
1010 usage(argv);
1011 }
1012
1013 extern int optind;
1014 extern char *optarg;
1015 int arg;
1016
1017 /* optional args */
1018 const char *keychainName = NULL;
1019 char *inFileName = NULL;
1020 char *outFileName = NULL;
1021 bool detachedContent = false;
1022 char *detachedFile = NULL;
1023 bool useIdPicker = false;
1024 char *recipient = NULL;
1025 bool quiet = false;
1026 bool parseSignerCert = false;
1027 CT_Vfy vfyOp = CTV_None;
1028 const CSSM_OID *eContentType = NULL;
1029
1030 optind = 2;
1031 while ((arg = getopt(argc, argv, "i:o:k:pr:e:dD:qcv:")) != -1) {
1032 switch (arg) {
1033 case 'i':
1034 inFileName = optarg;
1035 break;
1036 case 'o':
1037 outFileName = optarg;
1038 break;
1039 case 'k':
1040 keychainName = optarg;
1041 break;
1042 case 'p':
1043 useIdPicker = true;
1044 break;
1045 case 'r':
1046 recipient = optarg;
1047 break;
1048 case 'c':
1049 parseSignerCert = true;
1050 break;
1051 case 'v':
1052 if(!strcmp(optarg, "sign")) {
1053 vfyOp = CTV_Sign;
1054 }
1055 else if(!strcmp(optarg, "encr")) {
1056 vfyOp = CTV_Envelop;
1057 }
1058 else if(!strcmp(optarg, "signEnv")) {
1059 vfyOp = CTV_SignEnvelop;
1060 }
1061 else {
1062 usage(argv);
1063 }
1064 break;
1065 case 'e':
1066 switch(optarg[0]) {
1067 case 'a':
1068 eContentType = &CSSMOID_PKINIT_AUTH_DATA;
1069 break;
1070 case 'r':
1071 eContentType = &CSSMOID_PKINIT_RKEY_DATA;
1072 break;
1073 default:
1074 usage(argv);
1075 }
1076 break;
1077 case 'd':
1078 if(op != CTO_Sign) {
1079 printf("-d only valid for op sign\n");
1080 exit(1);
1081 }
1082 detachedContent = true;
1083 break;
1084 case 'D':
1085 if(op != CTO_Parse) {
1086 printf("-D only valid for op sign\n");
1087 exit(1);
1088 }
1089 detachedFile = optarg;
1090 break;
1091 case 'q':
1092 quiet = true;
1093 break;
1094 default:
1095 case '?':
1096 usage(argv);
1097 }
1098 }
1099 if(optind != argc) {
1100 /* getopt does not return '?' */
1101 usage(argv);
1102 }
1103
1104 SecIdentityRef idRef = NULL;
1105 SecKeychainRef kcRef = NULL;
1106 SecCertificateRef recipientCert = NULL;
1107 unsigned char *inData = NULL;
1108 unsigned inDataLen = 0;
1109 unsigned char *outData = NULL;
1110 unsigned outDataLen = 0;
1111 unsigned char *detachedData = NULL;
1112 unsigned detachedDataLen = 0;
1113 OSStatus ortn;
1114
1115 if(inFileName) {
1116 if(readFile(inFileName, &inData, &inDataLen)) {
1117 fprintf(stderr, "***Error reading infile %s. Aborting.\n", inFileName);
1118 exit(1);
1119 }
1120 }
1121 if(detachedFile) {
1122 if(readFile(detachedFile, &detachedData, &detachedDataLen)) {
1123 fprintf(stderr, "***Error reading detachedFile %s. Aborting.\n", detachedFile);
1124 exit(1);
1125 }
1126 }
1127 if(keychainName) {
1128 ortn = SecKeychainOpen(keychainName, &kcRef);
1129 if(ortn) {
1130 cssmPerror("SecKeychainOpen", ortn);
1131 exit(1);
1132 }
1133 }
1134 if(useIdPicker) {
1135 ortn = sslSimpleIdentPicker(kcRef, &idRef);
1136 if(ortn) {
1137 fprintf(stderr, "***Error obtaining identity via picker. Aborting.\n");
1138 exit(1);
1139 }
1140 }
1141 else if(needId) {
1142 /* use first identity in specified keychain */
1143 CFArrayRef array = sslKcRefToCertArray(kcRef, CSSM_FALSE, CSSM_FALSE,
1144 NULL, // no verify policy
1145 NULL);
1146 if(array == NULL) {
1147 fprintf(stderr, "***Error finding a signing cert. Aborting.\n");
1148 exit(1);
1149 }
1150 idRef = (SecIdentityRef)CFArrayGetValueAtIndex(array, 0);
1151 if(idRef == NULL) {
1152 fprintf(stderr, "***No identities found. Aborting.\n");
1153 exit(1);
1154 }
1155 CFRetain(idRef);
1156 CFRelease(array);
1157 }
1158 if(recipient) {
1159 ortn = findCert(recipient, kcRef, &recipientCert);
1160 if(ortn) {
1161 exit(1);
1162 }
1163 }
1164
1165 switch(op) {
1166 case CTO_Sign:
1167 ortn = doSign(idRef, inData, inDataLen,
1168 detachedContent, eContentType,
1169 &outData, &outDataLen);
1170 break;
1171 case CTO_Envelop:
1172 if(recipientCert == NULL) {
1173 if(idRef == NULL) {
1174 printf("***Need a recipient or an identity to encrypt\n");
1175 exit(1);
1176 }
1177 ortn = SecIdentityCopyCertificate(idRef, &recipientCert);
1178 if(ortn) {
1179 cssmPerror("SecIdentityCopyCertificate", ortn);
1180 exit(1);
1181 }
1182 }
1183 ortn = doEncrypt(recipientCert, inData, inDataLen, &outData, &outDataLen);
1184 break;
1185 case CTO_SignEnvelop:
1186 ortn = doSignEncrypt(recipientCert, idRef, eContentType,
1187 inData, inDataLen, &outData, &outDataLen);
1188 break;
1189 case CTO_Parse:
1190 ortn = doParse(inData, inDataLen,
1191 detachedData, detachedDataLen,
1192 vfyOp, parseSignerCert, quiet,
1193 &outData, &outDataLen);
1194 break;
1195 }
1196 if(ortn) {
1197 goto errOut;
1198 }
1199 if(outData && outFileName) {
1200 if(writeFile(outFileName, outData, outDataLen)) {
1201 fprintf(stderr, "***Error writing to %s.\n", outFileName);
1202 ortn = -1;
1203 }
1204 else {
1205 if(!quiet) {
1206 fprintf(stderr, "...wrote %u bytes to %s.\n", outDataLen, outFileName);
1207 }
1208 }
1209 }
1210 else if(outData) {
1211 fprintf(stderr, "...generated %u bytes but no place to write it.\n", outDataLen);
1212 }
1213 else if(outFileName) {
1214 fprintf(stderr, "...nothing to write to file %s.\n", outFileName);
1215 /* assume this is an error, caller wanted something */
1216 ortn = -1;
1217 }
1218 errOut:
1219 return ortn;
1220 }