2 * cmstool.cpp - manipulate CMS messages, CMSEncoder/CMSDecoder version
5 #include <Security/Security.h>
6 #include <security_cdsa_utils/cuFileIo.h>
11 #include <utilLib/common.h>
12 #include <security_cdsa_utils/cuFileIo.h>
13 #include <security_cdsa_utils/cuPrintCert.h>
14 #include <clAppUtils/identPicker.h>
15 #include <clAppUtils/sslAppUtils.h>
16 #include <security_cdsa_utils/cuOidParser.h>
17 #include <CoreFoundation/CoreFoundation.h>
18 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
20 #include <Security/SecTrustPriv.h> /* SecTrustGetCssmResultCode */
21 #include <Security/SecIdentityPriv.h> /* SecIdentityCreateWithCertificate */
22 #include <Security/CMSEncoder.h>
23 #include <Security/CMSDecoder.h>
24 #include <Security/CMSPrivate.h>
26 #include <Security/SecCertificate.h>
27 #include <Security/oidsattr.h>
29 #define CFRELEASE(cfr) if(cfr != NULL) { CFRelease(cfr); }
31 static SecKeychainRef
keychain_open(const char *name
);
33 static void usage(char **argv
)
35 printf("Usage: %s cmd [option ...]\n", argv
[0]);
36 printf("cmd values:\n");
37 printf(" sign -- create signedData\n");
38 printf(" envel -- create envelopedData\n");
39 printf(" signEnv -- create nested EnvelopedData(signedData(data))\n");
40 printf(" certs -- create certs-only CMS msg\n");
41 printf(" parse -- parse a CMS message file\n");
42 printf("Input/output options:\n");
43 printf(" -i infile\n");
44 printf(" -o outfile\n");
45 printf(" -D detachedContent -- detached content (parse only)\n");
46 printf(" -d detached -- infile contains detached content (sign only)\n");
47 printf(" -f certFileBase -- dump all certs to certFileBase\n");
48 printf("Signer and recipient options:\n");
49 printf(" -k keychain -- Keychain to search for certs\n");
50 printf(" -p -- Use identity picker\n");
51 printf(" -r recipient -- add recipient (via email address) of enveloped data\n");
52 printf(" -R recipCertFile -- add recipient (via cert from file) of enveloped data\n");
53 printf(" -S signerEmail -- add signer email address\n");
54 printf(" -C cert -- add (general) signedData cert\n");
55 printf("Misc. options:\n");
56 printf(" -e eContentType -- a(uthData)|r(keyData)\n");
57 printf(" -m -- multi updates; default is one-shot\n");
58 printf(" -1 (one) -- custom encoder/decoder\n");
59 printf(" -2 -- fetch SecCmsMessageRef\n");
60 printf(" -c -- parse signer certs\n");
61 printf(" -a [ceEt] -- Signed Attributes: c=SmimeCaps,\n");
62 printf(" e=EncrPrefs, E=MSEncrPrefs, t=signingTime\n");
63 printf(" -A anchorFile -- Verify certs using specified anchor cert\n");
64 printf(" -M -- Do SecTrustEvaluate manually\n");
65 printf(" -t certChainMode -- none|signer|chain|chainWithRoot; default is chain\n");
66 printf(" -l -- loop & pause for malloc debug\n");
67 printf(" -q -- quiet\n");
68 printf(" -Z -- silent, no output at all except for errors\n");
69 printf("Verification options:\n");
70 printf(" -v sign|encr|signEnv -- verify message is signed/encrypted/both\n");
71 printf(" -s numSigners -- verify msg has specified number of signers\n");
72 printf(" -E eContentType -- verify a(authData)|r(keyData)|d(data)\n");
73 printf(" -N numCerts -- verify number of certs\n");
94 /* additional OIDS to specify as eContentType */
95 #define OID_PKINIT 0x2B, 6, 1, 5, 2, 3
96 #define OID_PKINIT_LEN 6
98 static const uint8 OID_PKINIT_AUTH_DATA
[] = {OID_PKINIT
, 1};
99 static const uint8 OID_PKINIT_DH_KEY_DATA
[] = {OID_PKINIT
, 2};
100 static const uint8 OID_PKINIT_RKEY_DATA
[] = {OID_PKINIT
, 3};
101 static const uint8 OID_PKINIT_KP_CLIENTAUTH
[] = {OID_PKINIT
, 3};
102 static const uint8 OID_PKINIT_KPKDC
[] = {OID_PKINIT
, 5};
104 static const CSSM_OID CSSMOID_PKINIT_AUTH_DATA
=
105 {OID_PKINIT_LEN
+1, (uint8
*)OID_PKINIT_AUTH_DATA
};
106 static const CSSM_OID CSSMOID_PKINIT_DH_KEY_DATA
=
107 {OID_PKINIT_LEN
+1, (uint8
*)OID_PKINIT_DH_KEY_DATA
};
108 static const CSSM_OID CSSMOID_PKINIT_RKEY_DATA
=
109 {OID_PKINIT_LEN
+1, (uint8
*)OID_PKINIT_RKEY_DATA
};
110 static const CSSM_OID CSSMOID_PKINIT_KP_CLIENTAUTH
=
111 {OID_PKINIT_LEN
+1, (uint8
*)OID_PKINIT_KP_CLIENTAUTH
};
112 static const CSSM_OID CSSMOID_PKINIT_KPKDC
=
113 {OID_PKINIT_LEN
+1, (uint8
*)OID_PKINIT_KPKDC
};
116 * Find a cert in specified keychain or keychain list matching specified
117 * email address. We happen to know that the email address is stored with the
118 * kSecAlias attribute.
120 static OSStatus
findCert(
121 const char *emailAddress
,
122 CFTypeRef kcArArray
, // kc, array, or even NULL
123 SecCertificateRef
*cert
)
126 SecKeychainSearchRef srch
;
127 SecKeychainAttributeList attrList
;
128 SecKeychainAttribute attr
;
130 attr
.tag
= kSecAlias
;
131 attr
.length
= strlen(emailAddress
);
132 attr
.data
= (void *)emailAddress
;
134 attrList
.attr
= &attr
;
136 ortn
= SecKeychainSearchCreateFromAttributes(kcArArray
,
137 kSecCertificateItemClass
,
141 cssmPerror("SecKeychainSearchCreateFromAttributes", ortn
);
145 ortn
= SecKeychainSearchCopyNext(srch
, (SecKeychainItemRef
*)cert
);
147 printf("***No certs found matching recipient %s. Aborting.\n",
155 /* create a SecCertificateRef from a file */
156 static SecCertificateRef
readCertFile(
157 const char *fileName
)
159 unsigned char *certData
= NULL
;
160 unsigned certDataLen
;
161 SecCertificateRef rtnCert
= NULL
;
163 if(readFile(fileName
, &certData
, &certDataLen
)) {
164 printf("***Error reading %s. Aborting.\n", fileName
);
167 CSSM_DATA cssmCert
= {certDataLen
, (uint8
*)certData
};
168 OSStatus ortn
= SecCertificateCreateFromData(&cssmCert
,
169 CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_DER
,
172 cssmPerror("SecCertificateCreateFromData", ortn
);
173 printf("***Error creating cert fromn %s. Aborting.\n", fileName
);
179 static int dumpCertFiles(
181 const char *fileBase
,
186 if(allCerts
== NULL
) {
187 printf("...no certs to write.\n");
190 CFIndex numCerts
= CFArrayGetCount(allCerts
);
192 printf("...no certs to write.\n");
195 for(CFIndex dex
=0; dex
<numCerts
; dex
++) {
196 SecCertificateRef secCert
=
197 (SecCertificateRef
)CFArrayGetValueAtIndex(allCerts
, dex
);
201 ortn
= SecCertificateGetData(secCert
, &certData
);
203 cssmPerror("SecCertificateGetData", ortn
);
206 sprintf(fileName
, "%s_%u.cer", fileBase
, (unsigned)dex
);
207 if(writeFile(fileName
, certData
.Data
, certData
.Length
)) {
208 printf("***Error writing cert data to %s. Aborting.\n", fileName
);
212 printf("...wrote %u bytes to %s.\n",
213 (unsigned)certData
.Length
, fileName
);
220 * Do a random number of random-sized updates on a CMSEncoder.
222 static OSStatus
updateEncoder(
223 CMSEncoderRef cmsEncoder
,
224 const unsigned char *inData
,
227 unsigned toMove
= inDataLen
;
231 thisMove
= genRand(1, toMove
);
232 OSStatus ortn
= CMSEncoderUpdateContent(cmsEncoder
, inData
, thisMove
);
234 cssmPerror("CMSEncoderUpdateContent", ortn
);
244 * Do a random number of random-sized updates on a CMSDecoder.
246 static OSStatus
updateDecoder(
247 CMSDecoderRef cmsDecoder
,
248 const unsigned char *inData
,
251 unsigned toMove
= inDataLen
;
255 thisMove
= genRand(1, toMove
);
256 OSStatus ortn
= CMSDecoderUpdateMessage(cmsDecoder
, inData
, thisMove
);
258 cssmPerror("CMSDecoderUpdateMessage", ortn
);
267 #define TRUST_STRING_MAX 128
269 static OSStatus
evalSecTrust(
270 SecTrustRef secTrust
,
271 CFMutableArrayRef anchorArray
, // optional
272 char *trustStr
, // caller-mallocd, TRUST_STRING_MAX chars
276 SecTrustResultType secTrustResult
;
279 ortn
= SecTrustSetAnchorCertificates(secTrust
, anchorArray
);
281 /* should never happen */
282 cssmPerror("SecTrustSetAnchorCertificates", ortn
);
286 ortn
= SecTrustEvaluate(secTrust
, &secTrustResult
);
288 /* should never happen */
289 cssmPerror("SecTrustEvaluate", ortn
);
292 switch(secTrustResult
) {
293 case kSecTrustResultUnspecified
:
294 /* cert chain valid, no special UserTrust assignments */
295 case kSecTrustResultProceed
:
296 /* cert chain valid AND user explicitly trusts this */
297 sprintf(trustStr
, "Successful\n");
299 case kSecTrustResultDeny
:
300 case kSecTrustResultConfirm
:
302 * Cert chain may well have verified OK, but user has flagged
303 * one of these certs as untrustable.
305 sprintf(trustStr
, "Not trusted per user-specified Trust level\n");
306 /* bogus return code I know */
307 return errSecInvalidTrustSetting
;
310 /* get low-level TP error */
312 ortn
= SecTrustGetCssmResultCode(secTrust
, &tpStatus
);
314 cssmPerror("SecTrustGetCssmResultCode", ortn
);
318 case CSSMERR_TP_INVALID_ANCHOR_CERT
:
319 sprintf(trustStr
, "Untrusted root\n");
321 case CSSMERR_TP_NOT_TRUSTED
:
322 /* no root, not even in implicit SSL roots */
323 sprintf(trustStr
, "No root cert found\n");
325 case CSSMERR_TP_CERT_EXPIRED
:
326 sprintf(trustStr
, "Expired cert\n");
328 case CSSMERR_TP_CERT_NOT_VALID_YET
:
329 sprintf(trustStr
, "Cert not valid yet\n");
332 sprintf(trustStr
, "Other cert failure (%s)",
333 cssmErrToStr(tpStatus
));
338 } /* SecTrustEvaluate error */
342 static OSStatus
doParse(
343 const unsigned char *data
,
345 const unsigned char *detachedContent
,
346 unsigned detachedContentLen
,
349 const CSSM_OID
*eContentVfy
, // optional to verify
350 int numSignersVfy
, // optional (>=0) to verify
351 int numCertsVfy
, // optionjal (>= 0) to verify
352 bool parseSignerCert
,
353 const char *certFileBase
, // optionally write certs here
354 bool customDecoder
, // invoke CMSDecoderSetDecoder()
355 bool manTrustEval
, // evaluate SecTrust ourself
356 CFMutableArrayRef anchorArray
, // optional, and only for manTrustEval
358 CFDataRef
*outData
) // RETURNED
360 if((data
== NULL
) || (dataLen
== 0)) {
361 fprintf(stderr
, "***Parse requires input file. Aborting.\n");
365 CMSDecoderRef cmsDecoder
;
368 CFArrayRef allCerts
= NULL
;
370 SecPolicyRef policy
= NULL
;
371 SecPolicySearchRef policySearch
= NULL
;
372 CFIndex numCerts
= 0;
373 int addDetachedAfterDecode
= 0;
374 SecArenaPoolRef arena
= NULL
;
377 * Four different return codes:
378 * -- ortn used for function returns; if nonzero, bail immediately and goto errOut
379 * -- ourRtn is manually set per the output of CMSDecoderCopySignerStatus and
381 * -- trustRtn is the output of manual SecTrustEvaluate (via evalSecTrust())
382 * -- vfyErr indicates mismatch in caller-specified error params
384 * All four have to be zero fgor us to return zero.
386 OSStatus ourRtn
= noErr
;
387 OSStatus ortn
= noErr
;
388 OSStatus trustRtn
= noErr
;
391 ortn
= CMSDecoderCreate(&cmsDecoder
);
393 cssmPerror("CMSDecoderCreate", ortn
);
397 /* subsequent errors to errOut: */
399 if(detachedContent
!= NULL
) {
401 * We can add detached content either before or after the
402 * update/finalize; to test and verify, flip a coin to decide
405 addDetachedAfterDecode
= genRand(0, 1);
406 if(!addDetachedAfterDecode
) {
407 CFDataRef cfDetach
= CFDataCreate(NULL
, detachedContent
, detachedContentLen
);
408 ortn
= CMSDecoderSetDetachedContent(cmsDecoder
, cfDetach
);
411 cssmPerror("CMSDecoderSetDetachedContent", ortn
);
418 /* Create a decoder; we don't have to free it, but we do have to free the
420 SecCmsDecoderRef coder
= NULL
;
422 ortn
= SecArenaPoolCreate(1024, &arena
);
424 cssmPerror("SecArenaPoolCreate", ortn
);
427 ortn
= SecCmsDecoderCreate(arena
,
428 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &coder
);
430 cssmPerror("SecCmsDecoderCreate", ortn
);
433 ortn
= CMSDecoderSetDecoder(cmsDecoder
, coder
);
435 cssmPerror("CMSDecoderSetDecoder", ortn
);
439 printf("...set up custom SecCmsDecoderRef\n");
443 ortn
= updateDecoder(cmsDecoder
, data
, dataLen
);
449 ortn
= CMSDecoderUpdateMessage(cmsDecoder
, data
, dataLen
);
451 cssmPerror("CMSDecoderUpdateMessage", ortn
);
455 ortn
= CMSDecoderFinalizeMessage(cmsDecoder
);
457 cssmPerror("CMSDecoderFinalizeMessage", ortn
);
460 if(addDetachedAfterDecode
) {
461 CFDataRef cfDetach
= CFDataCreate(NULL
, detachedContent
, detachedContentLen
);
462 ortn
= CMSDecoderSetDetachedContent(cmsDecoder
, cfDetach
);
465 cssmPerror("CMSDecoderSetDetachedContent", ortn
);
469 ortn
= CMSDecoderGetNumSigners(cmsDecoder
, &numSigners
);
471 cssmPerror("CMSDecoderGetNumSigners", ortn
);
474 ortn
= CMSDecoderIsContentEncrypted(cmsDecoder
, &isEncrypted
);
476 cssmPerror("CMSDecoderIsContentEncrypted", ortn
);
479 ortn
= CMSDecoderCopyAllCerts(cmsDecoder
, &allCerts
);
481 cssmPerror("CMSDecoderCopyAllCerts", ortn
);
485 numCerts
= CFArrayGetCount(allCerts
);
488 /* optional verify of expected message type */
493 if((numSigners
== 0) && (allCerts
== NULL
)) {
494 fprintf(stderr
, "***Expected SignedData, but no signersFound\n");
499 fprintf(stderr
, "***Expected SignedData, but msg IS encrypted\n");
503 case CTV_SignEnvelop
:
504 if(numSigners
== 0) {
505 fprintf(stderr
, "***Expected Signed&Enveloped, but no signersFound\n");
509 fprintf(stderr
, "***Expected Signed&Enveloped, but msg not encrypted\n");
514 if(numSigners
!= 0) {
515 fprintf(stderr
, "***Expected EnvelopedData, but signers found\n");
519 fprintf(stderr
, "***Expected EnvelopedData, but msg not encrypted\n");
525 if(numSignersVfy
>= 0) {
526 if((unsigned)numSignersVfy
!= numSigners
) {
527 fprintf(stderr
, "***Expected %d signers; found %lu\n",
528 numSignersVfy
, numSigners
);
532 if(numCertsVfy
>= 0) {
533 if((int)numCerts
!= numCertsVfy
) {
534 fprintf(stderr
, "***Expected %d certs; found %d\n",
535 numCertsVfy
, (int)numCerts
);
540 fprintf(stderr
, "=== CMS message info ===\n");
541 fprintf(stderr
, " Num Signers : %lu\n", (unsigned long)numSigners
);
542 fprintf(stderr
, " Encrypted : %s\n", isEncrypted
? "true" : "false");
543 fprintf(stderr
, " Num Certs : %lu\n",
544 allCerts
? (unsigned long)CFArrayGetCount(allCerts
) : 0);
547 if((certFileBase
!= NULL
) & (allCerts
!= NULL
)) {
548 dumpCertFiles(allCerts
, certFileBase
, quiet
);
553 CSSM_OID eContentType
= {0, NULL
};
554 CFDataRef eContentData
= NULL
;
556 char str
[OID_PARSER_STRING_SIZE
];
558 ortn
= CMSDecoderCopyEncapsulatedContentType(cmsDecoder
, &eContentData
);
560 cssmPerror("CMSDecoderCopyEncapsulatedContentType", ortn
);
563 if(eContentData
!= NULL
) {
564 eContentType
.Data
= (uint8
*)CFDataGetBytePtr(eContentData
);
565 eContentType
.Length
= CFDataGetLength(eContentData
);
568 /* can't use stderr - oidparser is fixed w/stdout */
569 printf(" eContentType : ");
570 if(eContentType
.Data
== NULL
) {
571 printf("***NONE FOUND***\n");
573 else if(eContentType
.Length
== 0) {
574 printf("***EMPTY***\n");
577 oidParser
.oidParse(eContentType
.Data
, eContentType
.Length
, str
);
582 if(eContentVfy
!= NULL
) {
583 if(eContentType
.Data
== NULL
) {
584 fprintf(stderr
, "***Tried to verify eContentType, but none found\n");
587 else if(!appCompareCssmData(eContentVfy
, &eContentType
)) {
588 fprintf(stderr
, "***eContentType verify error\n");
589 fprintf(stderr
, " Expected: ");
590 oidParser
.oidParse(eContentVfy
->Data
, eContentVfy
->Length
, str
);
592 fprintf(stderr
, " Found : ");
593 oidParser
.oidParse(eContentType
.Data
, eContentType
.Length
, str
);
598 CFRELEASE(eContentData
);
600 /* get a policy for cert evaluation */
601 ortn
= SecPolicySearchCreate(CSSM_CERT_X_509v3
,
602 &CSSMOID_APPLE_X509_BASIC
,
606 cssmPerror("SecPolicySearchCreate", ortn
);
609 ortn
= SecPolicySearchCopyNext(policySearch
, &policy
);
611 cssmPerror("SecPolicySearchCopyNext", ortn
);
615 for(signerDex
=0; signerDex
<numSigners
; signerDex
++) {
616 CMSSignerStatus signerStatus
= kCMSSignerInvalidIndex
;
617 SecCertificateRef signerCert
;
618 CFStringRef signerEmailAddress
;
619 SecTrustRef secTrust
;
620 OSStatus certVerifyResultCode
;
621 char trustStr
[TRUST_STRING_MAX
];
623 ortn
= CMSDecoderCopySignerStatus(cmsDecoder
, signerDex
,
625 manTrustEval
? FALSE
: TRUE
, /* evaluateSecTrust */
628 &certVerifyResultCode
);
630 cssmPerror("CMSDecoderCopySignerStatus", ortn
);
633 if(ourRtn
== noErr
) {
634 if((signerStatus
!= kCMSSignerValid
) ||
635 (certVerifyResultCode
!= CSSM_OK
)) {
639 ortn
= CMSDecoderCopySignerEmailAddress(cmsDecoder
, signerDex
,
640 &signerEmailAddress
);
642 cssmPerror("CMSDecoderCopySignerEmailAddress", ortn
);
645 ortn
= CMSDecoderCopySignerCert(cmsDecoder
, signerDex
,
648 cssmPerror("CMSDecoderCopySignerCertificate", ortn
);
652 trustRtn
= evalSecTrust(secTrust
, anchorArray
, trustStr
, quiet
);
655 /* display nothing here if quiet true and status is copacetic */
656 if(!quiet
|| (signerStatus
!= kCMSSignerValid
) || (trustRtn
!= noErr
)) {
657 fprintf(stderr
, " Signer %u:\n", signerDex
);
658 fprintf(stderr
, " signerStatus : ");
659 switch(signerStatus
) {
660 case kCMSSignerUnsigned
:
661 fprintf(stderr
, "kCMSSignerUnsigned\n"); break;
662 case kCMSSignerValid
:
663 fprintf(stderr
, "kCMSSignerValid\n"); break;
664 case kCMSSignerNeedsDetachedContent
:
665 fprintf(stderr
, "kCMSSignerNeedsDetachedContent\n"); break;
666 case kCMSSignerInvalidSignature
:
667 fprintf(stderr
, "kCMSSignerInvalidSignature\n"); break;
668 case kCMSSignerInvalidCert
:
669 fprintf(stderr
, "kCMSSignerInvalidCert\n"); break;
670 case kCMSSignerInvalidIndex
:
671 fprintf(stderr
, "kCMSSignerInvalidIndex\n"); break;
674 fprintf(stderr
, " Trust Eval : %s\n", trustStr
);
676 fprintf(stderr
, " emailAddrs : ");
677 if(signerEmailAddress
== NULL
) {
678 fprintf(stderr
, "<<none found>>\n");
682 if(!CFStringGetCString(signerEmailAddress
,
683 emailStr
, 1000, kCFStringEncodingASCII
)) {
684 fprintf(stderr
, "<<<Error converting email address to C string>>>\n");
687 fprintf(stderr
, "%s\n", emailStr
);
691 fprintf(stderr
, " vfyResult : %s\n",
692 certVerifyResultCode
?
693 cssmErrToStr(certVerifyResultCode
) : "Success");
695 /* TBD: optionally manually verify the SecTrust object */
697 if(parseSignerCert
) {
699 if(signerCert
== NULL
) {
700 fprintf(stderr
, " <<<Unable to obtain signer cert>>>\n");
704 ortn
= SecCertificateGetData(signerCert
, &certData
);
706 fprintf(stderr
, " <<<Unable to obtain signer cert>>>\n");
707 cssmPerror("SecCertificateGetData", ortn
);
710 printf("========== Signer Cert==========\n\n");
711 printCert(certData
.Data
, certData
.Length
, CSSM_FALSE
);
712 printf("========== End Signer Cert==========\n\n");
715 } /* parseSignerCert */
716 } /* displaying per-signer info */
718 CFRELEASE(signerCert
);
720 CFRELEASE(signerEmailAddress
);
721 signerEmailAddress
= NULL
;
724 } /* for signerDex */
727 ortn
= CMSDecoderCopyContent(cmsDecoder
, outData
);
729 cssmPerror("CMSDecoderCopyContent", ortn
);
734 CFRelease(cmsDecoder
);
736 SecArenaPoolFree(arena
, false);
740 CFRELEASE(policySearch
);
756 static OSStatus
doSign(
757 CFTypeRef signerOrArray
,
758 const unsigned char *inData
,
761 bool detachedContent
,
762 const CSSM_OID
*eContentType
, // OPTIONAL
763 CMSSignedAttributes attrs
,
764 CFTypeRef otherCerts
, // OPTIONAL
767 CMSCertificateChainMode chainMode
,
769 CFDataRef
*outData
) // RETURNED
771 if((inData
== NULL
) || (inDataLen
== 0) || (outData
== NULL
)) {
772 fprintf(stderr
, "***Sign requires input file. Aborting.\n");
775 if(signerOrArray
== NULL
) {
776 fprintf(stderr
, "***Sign requires a signing identity. Aborting.\n");
781 CMSEncoderRef cmsEncoder
= NULL
;
782 SecCmsMessageRef msg
= NULL
; /* for optional CMSEncoderGetCmsMessage */
783 SecArenaPoolRef arena
= NULL
;
784 CSSM_DATA encoderOut
= {0, NULL
};
786 if(multiUpdate
|| otherCerts
|| getCmsMsg
|| customCoder
||
787 (chainMode
!= kCMSCertificateChain
)) {
788 /* one-shot encode doesn't support otherCerts or chainOptions*/
790 ortn
= CMSEncoderCreate(&cmsEncoder
);
792 cssmPerror("CMSEncoderCreate", ortn
);
795 /* subsequent errors to errOut: */
796 if(signerOrArray
!= NULL
) {
797 ortn
= CMSEncoderAddSigners(cmsEncoder
, signerOrArray
);
803 ortn
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, eContentType
);
808 if(detachedContent
) {
809 ortn
= CMSEncoderSetHasDetachedContent(cmsEncoder
, detachedContent
);
815 ortn
= CMSEncoderAddSupportingCerts(cmsEncoder
, otherCerts
);
821 ortn
= CMSEncoderAddSignedAttributes(cmsEncoder
, attrs
);
826 if(chainMode
!= kCMSCertificateChain
) {
827 ortn
= CMSEncoderSetCertificateChainMode(cmsEncoder
, chainMode
);
832 if(getCmsMsg
|| customCoder
) {
834 * We just want to trigger the state transition
835 * that we know should happen. We also might need
836 * the msg to create a custom coder.
838 ortn
= CMSEncoderGetCmsMessage(cmsEncoder
, &msg
);
840 cssmPerror("CMSEncoderGetCmsMessage", ortn
);
846 SecCmsEncoderRef coder
= NULL
;
847 ortn
= SecArenaPoolCreate(1024, &arena
);
849 cssmPerror("SecArenaPoolCreate", ortn
);
852 ortn
= SecCmsEncoderCreate(msg
,
853 NULL
, NULL
, // no callback
854 &encoderOut
, // data goes here
856 NULL
, NULL
, // no password callback (right?)
857 NULL
, NULL
, // decrypt key callback
858 NULL
, NULL
, // detached digests
861 cssmPerror("SecCmsEncoderCreate", ortn
);
864 ortn
= CMSEncoderSetEncoder(cmsEncoder
, coder
);
866 cssmPerror("CMSEncoderSetEncoder", ortn
);
870 printf("...set up custom SecCmsEncoderRef\n");
873 /* random number of random-sized updates */
874 ortn
= updateEncoder(cmsEncoder
, inData
, inDataLen
);
879 ortn
= CMSEncoderCopyEncodedContent(cmsEncoder
, outData
);
881 cssmPerror("CMSEncoderCopyEncodedContent", ortn
);
884 /* we have the data right here */
885 *outData
= CFDataCreate(NULL
,
886 (const UInt8
*)encoderOut
.Data
, encoderOut
.Length
);
890 ortn
= CMSEncode(signerOrArray
,
891 NULL
, /* recipients */
898 printf("***CMSEncode returned %ld\n", (long)ortn
);
899 cssmPerror("CMSEncode", ortn
);
904 CFRelease(cmsEncoder
);
907 SecArenaPoolFree(arena
, false);
912 static OSStatus
doEncrypt(
913 CFTypeRef recipOrArray
,
914 const unsigned char *inData
,
917 CFDataRef
*outData
) // RETURNED
919 if((inData
== NULL
) || (inDataLen
== 0) || (outData
== NULL
)) {
920 fprintf(stderr
, "***Encrypt requires input file. Aborting.\n");
923 if(recipOrArray
== NULL
) {
924 fprintf(stderr
, "***Encrypt requires a recipient certificate. Aborting.\n");
929 CMSEncoderRef cmsEncoder
= NULL
;
932 ortn
= CMSEncoderCreate(&cmsEncoder
);
934 cssmPerror("CMSEncoderCreate", ortn
);
937 /* subsequent errors to errOut: */
938 ortn
= CMSEncoderAddRecipients(cmsEncoder
, recipOrArray
);
943 /* random number of random-sized updates */
944 ortn
= updateEncoder(cmsEncoder
, inData
, inDataLen
);
948 ortn
= CMSEncoderCopyEncodedContent(cmsEncoder
, outData
);
950 cssmPerror("CMSEncoderCopyEncodedContent", ortn
);
955 ortn
= CMSEncode(NULL
, /* signers */
957 NULL
, /* eContentType */
958 FALSE
, /* detachedContent */
963 printf("***CMSEncode returned %ld\n", (long)ortn
);
964 cssmPerror("CMSEncode", ortn
);
969 CFRelease(cmsEncoder
);
974 /* create nested message: msg = EnvelopedData(SignedData(inData)) */
975 static OSStatus
doSignEncrypt(
976 CFTypeRef recipOrArray
, // encryption recipients
977 CFTypeRef signerOrArray
, // signers
978 const CSSM_OID
*eContentType
, // OPTIONAL - for signedData
979 CMSSignedAttributes attrs
,
980 const unsigned char *inData
,
983 CFTypeRef otherCerts
, // OPTIONAL
984 CFDataRef
*outData
) // RETURNED
986 if((inData
== NULL
) || (inDataLen
== 0) || (outData
== NULL
)) {
987 fprintf(stderr
, "***Sign/Encrypt requires input file. Aborting.\n");
990 if(recipOrArray
== NULL
) {
991 fprintf(stderr
, "***Sign/Encrypt requires a recipient certificate. Aborting.\n");
994 if(signerOrArray
== NULL
) {
995 fprintf(stderr
, "***Sign/Encrypt requires a signer Identity. Aborting.\n");
1000 CMSEncoderRef cmsEncoder
= NULL
;
1002 if(multiUpdate
|| otherCerts
) {
1003 ortn
= CMSEncoderCreate(&cmsEncoder
);
1005 cssmPerror("CMSEncoderCreate", ortn
);
1008 /* subsequent errors to errOut: */
1009 ortn
= CMSEncoderAddRecipients(cmsEncoder
, recipOrArray
);
1013 ortn
= CMSEncoderAddSigners(cmsEncoder
, signerOrArray
);
1018 ortn
= CMSEncoderSetEncapsulatedContentType(cmsEncoder
, eContentType
);
1024 ortn
= CMSEncoderAddSupportingCerts(cmsEncoder
, otherCerts
);
1030 ortn
= CMSEncoderAddSignedAttributes(cmsEncoder
, attrs
);
1036 /* random number of random-sized updates */
1037 ortn
= updateEncoder(cmsEncoder
, inData
, inDataLen
);
1041 ortn
= CMSEncoderCopyEncodedContent(cmsEncoder
, outData
);
1043 cssmPerror("CMSEncoderCopyEncodedContent", ortn
);
1047 ortn
= CMSEncode(signerOrArray
,
1050 FALSE
, /* detachedContent */
1055 printf("***CMSEncode returned %ld\n", (long)ortn
);
1056 cssmPerror("CMSEncode", ortn
);
1062 CFRelease(cmsEncoder
);
1068 * Create a CMS message containing only certs.
1070 static OSStatus
makeCertBag(
1071 CFTypeRef certsOrArray
,
1074 if(certsOrArray
== NULL
) {
1075 printf("***Need some certs to generate this type of message.\n");
1080 CMSEncoderRef cmsEncoder
= NULL
;
1082 ortn
= CMSEncoderCreate(&cmsEncoder
);
1084 cssmPerror("CMSEncoderCreate", ortn
);
1087 /* subsequent errors to errOut: */
1088 ortn
= CMSEncoderAddSupportingCerts(cmsEncoder
, certsOrArray
);
1092 ortn
= CMSEncoderCopyEncodedContent(cmsEncoder
, outData
);
1094 cssmPerror("CMSEncoderCopyEncodedContent", ortn
);
1097 CFRelease(cmsEncoder
);
1102 * Support maintanance of single item or array of them.
1104 * Given new incoming 'newThing':
1105 * if both *thingArray and *currThing are NULL
1106 * *currThing = newThing;
1108 * create *thingArray;
1109 * add *currThing to *thingArray if present;
1110 * add newThing to *thingArray;
1112 static void addThing(
1114 CFTypeRef
*currThing
,
1115 CFMutableArrayRef
*thingArray
)
1117 if((*currThing
== NULL
) && (*thingArray
== NULL
)) {
1118 /* first occurrence of a thing */
1119 *currThing
= newThing
;
1123 /* at least two things - prepare array */
1124 if(*thingArray
== NULL
) {
1125 *thingArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1127 if(*currThing
!= NULL
) {
1128 /* move current thing to array */
1129 CFArrayAppendValue(*thingArray
, *currThing
);
1130 CFRelease(*currThing
);
1133 CFArrayAppendValue(*thingArray
, newThing
);
1134 CFRelease(newThing
);
1137 int main(int argc
, char **argv
)
1144 bool needId
= false;
1145 if(!strcmp(argv
[1], "sign")) {
1149 else if(!strcmp(argv
[1], "envel")) {
1152 else if(!strcmp(argv
[1], "signEnv")) {
1153 op
= CTO_SignEnvelop
;
1156 else if(!strcmp(argv
[1], "certs")) {
1159 else if(!strcmp(argv
[1], "parse")) {
1163 fprintf(stderr
, "***Unrecognized cmd.\n");
1168 extern char *optarg
;
1173 char *inFileName
= NULL
;
1174 char *outFileName
= NULL
;
1175 bool detachedContent
= false;
1176 char *detachedFile
= NULL
;
1177 bool useIdPicker
= false;
1179 bool silent
= false;
1180 bool parseSignerCert
= false;
1181 const CSSM_OID
*eContentType
= NULL
;
1182 bool multiUpdate
= false;
1183 bool loopPause
= false;
1184 char *certFileBase
= NULL
;
1185 CMSSignedAttributes signedAttrs
= 0;
1186 char *anchorFile
= NULL
;
1187 bool manTrustEval
= false;
1188 CMSCertificateChainMode chainMode
= kCMSCertificateChain
;
1190 /* for verification, usually in quiet/script mode */
1191 CT_Vfy vfyOp
= CTV_None
;
1192 const CSSM_OID
*eContentVfy
= NULL
;
1193 int numSignersVfy
= -1;
1194 int numCertsVfy
= -1;
1196 /* for verifying functions in CMSPrivate.h */
1197 bool customCoder
= false;
1198 bool fetchSecCmsMsg
= false;
1201 * Signer/recipient items and arrays - use one item if possible,
1202 * else array (to test both paths)
1204 SecIdentityRef signerId
= NULL
;
1205 CFMutableArrayRef signerArray
= NULL
;
1206 SecCertificateRef recipCert
= NULL
;
1207 CFMutableArrayRef recipArray
= NULL
;
1208 SecCertificateRef generalCert
= NULL
;
1209 CFMutableArrayRef generalCertArray
= NULL
;
1210 SecKeychainRef kcRef
= NULL
;
1211 CFMutableArrayRef anchorArray
= NULL
;
1214 while ((arg
= getopt(argc
, argv
, "i:o:k:pr:R:dD:e:mlqcv:s:E:S:C:f:N:a:A:M12t:Z")) != -1) {
1217 inFileName
= optarg
;
1220 outFileName
= optarg
;
1223 kcRef
= keychain_open(optarg
);
1225 // cssmPerror("SecKeychainOpen", ortn);
1234 SecCertificateRef newCert
= NULL
;
1235 char *recipient
= optarg
;
1236 ortn
= findCert(recipient
, kcRef
, &newCert
);
1240 addThing(newCert
, (CFTypeRef
*)&recipCert
, &recipArray
);
1245 SecCertificateRef certRef
= readCertFile(optarg
);
1246 if(certRef
== NULL
) {
1249 addThing(certRef
, (CFTypeRef
*)&recipCert
, &recipArray
);
1254 SecIdentityRef newId
= NULL
;
1255 SecCertificateRef newCert
= NULL
;
1256 char *signerEmail
= optarg
;
1259 * first find the cert, optionally in the keychain already
1262 ortn
= findCert(signerEmail
, kcRef
, &newCert
);
1267 /* map cert to an identity */
1268 ortn
= SecIdentityCreateWithCertificate(kcRef
, newCert
, &newId
);
1270 cssmPerror("SecIdentityCreateWithCertificate", ortn
);
1274 addThing(newId
, (CFTypeRef
*)&signerId
, &signerArray
);
1280 SecCertificateRef newCert
= readCertFile(optarg
);
1281 if(newCert
== NULL
) {
1284 addThing(newCert
, (CFTypeRef
*)&generalCert
, &generalCertArray
);
1288 parseSignerCert
= true;
1291 if(!strcmp(optarg
, "sign")) {
1294 else if(!strcmp(optarg
, "encr")) {
1295 vfyOp
= CTV_Envelop
;
1297 else if(!strcmp(optarg
, "signEnv")) {
1298 vfyOp
= CTV_SignEnvelop
;
1307 eContentType
= &CSSMOID_PKINIT_AUTH_DATA
;
1310 eContentType
= &CSSMOID_PKINIT_RKEY_DATA
;
1319 eContentVfy
= &CSSMOID_PKINIT_AUTH_DATA
;
1322 eContentVfy
= &CSSMOID_PKINIT_RKEY_DATA
;
1325 eContentVfy
= &CSSMOID_PKCS7_Data
;
1332 if(op
!= CTO_Sign
) {
1333 printf("-d only valid for op sign\n");
1336 detachedContent
= true;
1339 if(op
!= CTO_Parse
) {
1340 printf("-D only valid for op sign\n");
1343 detachedFile
= optarg
;
1352 numSignersVfy
= atoi(optarg
);
1355 certFileBase
= optarg
;
1358 numCertsVfy
= atoi(optarg
);
1364 fetchSecCmsMsg
= true;
1367 for(; *optarg
; optarg
++) {
1370 signedAttrs
|= kCMSAttrSmimeCapabilities
;
1373 signedAttrs
|= kCMSAttrSmimeEncryptionKeyPrefs
;
1376 signedAttrs
|= kCMSAttrSmimeMSEncryptionKeyPrefs
;
1379 signedAttrs
|= kCMSAttrSigningTime
;
1387 anchorFile
= optarg
;
1390 manTrustEval
= true;
1393 if(!strcmp(optarg
, "none")) {
1394 chainMode
= kCMSCertificateNone
;
1396 else if(!strcmp(optarg
, "signer")) {
1397 chainMode
= kCMSCertificateSignerOnly
;
1399 else if(!strcmp(optarg
, "chain")) {
1400 chainMode
= kCMSCertificateChain
;
1402 else if(!strcmp(optarg
, "chainWithRoot")) {
1403 chainMode
= kCMSCertificateChainWithRoot
;
1406 printf("***Bogus cert chain spec***\n");
1422 if(optind
!= argc
) {
1423 /* getopt does not return '?' */
1427 unsigned char *inData
= NULL
;
1428 unsigned inDataLen
= 0;
1429 unsigned char *detachedData
= NULL
;
1430 unsigned detachedDataLen
= 0;
1431 CFDataRef outData
= NULL
;
1432 CFIndex byteCount
= 0;
1434 testStartBanner((char *)"newCmsTool", argc
, argv
);
1438 if(readFile(inFileName
, &inData
, &inDataLen
)) {
1439 fprintf(stderr
, "***Error reading infile %s. Aborting.\n", inFileName
);
1444 if(readFile(detachedFile
, &detachedData
, &detachedDataLen
)) {
1445 fprintf(stderr
, "***Error reading detachedFile %s. Aborting.\n", detachedFile
);
1452 ortn
= sslSimpleIdentPicker(kcRef
, &signerId
);
1454 fprintf(stderr
, "***Error obtaining identity via picker. Aborting.\n");
1459 SecCertificateRef secCert
= readCertFile(anchorFile
);
1460 if(secCert
== NULL
) {
1463 anchorArray
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1464 CFArrayAppendValue(anchorArray
, secCert
);
1469 * In order for signed blobs to contain a full cert chain, and to find
1470 * certs matching a specific recipient cert email address, the keychain
1471 * containing intermediates must be in the user's keychain search list
1472 * (or, alternatively, the intermedaite certs must be added manually).
1473 * To alleviate the burden on test scripts, we'll ensure that an optionally
1474 * specified keychain is in factin the search list when we sign a message
1476 * Careful - make sure to ALWAYS restore the search list!
1478 CFArrayRef originalSearchList
= NULL
;
1480 ortn
= SecKeychainCopySearchList(&originalSearchList
);
1482 cssmPerror("SecKeychainCopySearchList", ortn
);
1485 CFMutableArrayRef newList
= CFArrayCreateMutableCopy(
1486 NULL
, 0, originalSearchList
);
1487 CFArrayAppendValue(newList
, kcRef
);
1488 ortn
= SecKeychainSetSearchList(newList
);
1490 cssmPerror("SecKeychainSetSearchList", ortn
);
1493 /* DO NOT EXIT WITHOUT RESTORING TO originalSearchList */
1498 ortn
= doSign((signerArray
!= NULL
) ?
1499 (CFTypeRef
)signerArray
: (CFTypeRef
)signerId
,
1501 multiUpdate
, detachedContent
, eContentType
, signedAttrs
,
1502 (generalCertArray
!= NULL
) ?
1503 (CFTypeRef
)generalCertArray
: (CFTypeRef
)generalCert
,
1504 customCoder
, fetchSecCmsMsg
, chainMode
, quiet
,
1508 ortn
= doEncrypt(recipArray
?
1509 (CFTypeRef
)recipArray
: (CFTypeRef
)recipCert
,
1511 multiUpdate
, &outData
);
1513 case CTO_SignEnvelop
:
1514 ortn
= doSignEncrypt(recipArray
?
1515 (CFTypeRef
)recipArray
: (CFTypeRef
)recipCert
,
1517 (CFTypeRef
)signerArray
: (CFTypeRef
)signerId
,
1518 eContentType
, signedAttrs
,
1519 inData
, inDataLen
, multiUpdate
,
1520 (generalCertArray
!= NULL
) ?
1521 (CFTypeRef
)generalCertArray
: (CFTypeRef
)generalCert
,
1525 ortn
= makeCertBag((generalCertArray
!= NULL
) ?
1526 (CFTypeRef
)generalCertArray
: (CFTypeRef
)generalCert
,
1530 ortn
= doParse(inData
, inDataLen
,
1531 detachedData
, detachedDataLen
,
1533 vfyOp
, eContentVfy
, numSignersVfy
, numCertsVfy
,
1534 parseSignerCert
, certFileBase
, customCoder
,
1535 manTrustEval
, anchorArray
,
1542 printf("...generated %u bytes of data.\n",
1543 (unsigned)CFDataGetLength(outData
));
1546 printf("q to quit, anything else to loop again: ");
1547 char resp
= getchar();
1556 } while (loopPause
);
1558 if(originalSearchList
) {
1559 ortn
= SecKeychainSetSearchList(originalSearchList
);
1561 cssmPerror("SecKeychainSetSearchList", ortn
);
1570 byteCount
= outData
? CFDataGetLength(outData
) : 0;
1571 if(outData
&& outFileName
) {
1572 if(writeFile(outFileName
, CFDataGetBytePtr(outData
), byteCount
)) {
1573 fprintf(stderr
, "***Error writing to %s.\n", outFileName
);
1578 fprintf(stderr
, "...wrote %u bytes to %s.\n",
1579 (unsigned)byteCount
, outFileName
);
1583 else if(byteCount
) {
1584 fprintf(stderr
, "...generated %u bytes but no place to write it.\n",
1585 (unsigned)byteCount
);
1587 else if(outFileName
) {
1588 fprintf(stderr
, "...nothing to write to file %s.\n", outFileName
);
1589 /* assume this is an error, caller wanted something */
1597 From SecurityTool/keychain_utilites.cpp
1598 This properly supports dynamic keychains, i.e. smartcards
1601 SecKeychainRef
keychain_open(const char *name
)
1603 SecKeychainRef keychain
= NULL
;
1606 // check_obsolete_keychain(name);
1607 if (name
&& name
[0] != '/')
1609 CFArrayRef dynamic
= NULL
;
1610 result
= SecKeychainCopyDomainSearchList(
1611 kSecPreferencesDomainDynamic
, &dynamic
);
1614 // cssmPerror("SecKeychainOpen", ortn);
1615 // sec_error("SecKeychainCopyDomainSearchList %s: %s",
1616 // name, sec_errstr(result));
1617 cssmPerror("SecKeychainCopyDomainSearchList", result
);
1623 uint32_t count
= dynamic
? CFArrayGetCount(dynamic
) : 0;
1625 for (i
= 0; i
< count
; ++i
)
1627 char pathName
[PATH_MAX
];
1628 UInt32 ioPathLength
= sizeof(pathName
);
1629 bzero(pathName
, ioPathLength
);
1630 keychain
= (SecKeychainRef
)CFArrayGetValueAtIndex(dynamic
, i
);
1631 result
= SecKeychainGetPath(keychain
, &ioPathLength
, pathName
);
1634 // sec_error("SecKeychainGetPath %s: %s", name, sec_errstr(result));
1635 cssmPerror("SecKeychainCopyDomainSearchList", result
);
1638 if (!strncmp(pathName
, name
, ioPathLength
))
1649 result
= SecKeychainOpen(name
, &keychain
);
1652 // sec_error("SecKeychainOpen %s: %s", name, sec_errstr(result));
1653 cssmPerror("SecKeychainOpen", result
);