2 * cmstool.cpp - manipluate CMS messages, intended to be an alternate for the
3 * currently useless cms command in /usr/bin/security
6 #include <Security/Security.h>
7 #include <utilLib/fileIo.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/SecTrustPriv.h>
30 #include <Security/SecKeychainItemPriv.h>
31 #include <Security/SecCertificate.h>
32 #include <Security/SecSMIME.h>
34 static void usage(char **argv)
36 printf("Usage: %s cmd [option ...]\n", argv[0]);
37 printf("cmd values:\n");
38 printf(" sign -- create signedData\n");
39 printf(" envel -- create envelopedData\n");
40 printf(" parse -- parse a CMS message file\n");
42 printf(" -i infile\n");
43 printf(" -o outfile\n");
44 printf(" -k keychain -- Keychain to search for certs\n");
45 printf(" -p -- Use identity picker\n");
46 printf(" -r recipient -- specify recipient of enveloped data\n");
47 printf(" -c -- parse signer cert\n");
48 printf(" -q -- quiet\n");
59 * Find a cert in specified keychain or keychain list matching specified
60 * email address. We happen to knopw that the email address is stored with the
61 * kSecAliasItemAttr attribute.
63 static OSStatus findCert(
64 const char *emailAddress,
65 CFTypeRef kcArArray, // kc, array, or even NULL
66 SecCertificateRef *cert)
69 SecKeychainSearchRef srch;
70 SecKeychainAttributeList attrList;
71 SecKeychainAttribute attr;
73 attr.tag = kSecAliasItemAttr;
74 attr.length = strlen(emailAddress);
75 attr.data = (void *)emailAddress;
77 attrList.attr = &attr;
79 ortn = SecKeychainSearchCreateFromAttributes(kcArArray,
80 kSecCertificateItemClass,
84 cssmPerror("SecKeychainSearchCreateFromAttributes", ortn);
88 ortn = SecKeychainSearchCopyNext(srch, (SecKeychainItemRef *)cert);
90 printf("***No certs founmd matching recipient %s. Aborting.\n");
97 static void evalSecTrust(
101 SecTrustResultType secTrustResult;
103 ortn = SecTrustEvaluate(secTrust, &secTrustResult);
105 /* should never happen */
106 cssmPerror("SecTrustEvaluate", ortn);
109 switch(secTrustResult) {
110 case kSecTrustResultUnspecified:
111 /* cert chain valid, no special UserTrust assignments */
112 case kSecTrustResultProceed:
113 /* cert chain valid AND user explicitly trusts this */
114 fprintf(stderr, "Successful\n");
116 case kSecTrustResultDeny:
117 case kSecTrustResultConfirm:
119 * Cert chain may well have verified OK, but user has flagged
120 * one of these certs as untrustable.
122 printf("Not trusted per user-specified Trust level\n");
126 /* get low-level TP error */
128 ortn = SecTrustGetCssmResultCode(secTrust, &tpStatus);
130 cssmPerror("SecTrustGetCssmResultCode", ortn);
134 case CSSMERR_TP_INVALID_ANCHOR_CERT:
135 fprintf(stderr, "Untrusted root\n");
137 case CSSMERR_TP_NOT_TRUSTED:
138 /* no root, not even in implicit SSL roots */
139 fprintf(stderr, "No root cert found\n");
141 case CSSMERR_TP_CERT_EXPIRED:
142 fprintf(stderr, "Expired cert\n");
144 case CSSMERR_TP_CERT_NOT_VALID_YET:
145 fprintf(stderr, "Cert not valid yet\n");
148 printf("Other cert failure: ");
149 cssmPerror("", tpStatus);
153 } /* SecTrustEvaluate error */
156 static OSStatus parseSignedData(
157 SecCmsSignedDataRef signedData,
158 bool parseSignerCert)
161 b = SecCmsSignedDataHasDigests(signedData);
162 printf(" has digests : %s\n", b ? "true" : "false");
164 SecTrustRef secTrust = NULL;
166 SecPolicyRef policy = NULL;
167 SecPolicySearchRef policySearch = NULL;
169 ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
170 &CSSMOID_APPLE_X509_BASIC,
174 cssmPerror("SecPolicySearchCreate", ortn);
177 ortn = SecPolicySearchCopyNext(policySearch, &policy);
179 cssmPerror("SecPolicySearchCopyNext", ortn);
183 int numSigners = SecCmsSignedDataSignerInfoCount(signedData);
184 printf(" num signers : %d\n", numSigners);
185 for(int dex=0; dex<numSigners; dex++) {
186 fprintf(stderr, " signer %d :\n", dex);
187 Boolean b = SecCmsSignedDataHasDigests(signedData);
188 fprintf(stderr, " vfy status : ");
190 ortn = SecCmsSignedDataVerifySignerInfo(signedData, dex, NULL,
193 fprintf(stderr, "vfSignerInfo() returned %d\n", (int)ortn);
194 fprintf(stderr, " vfy status : ");
196 if(secTrust == NULL) {
197 fprintf(stderr, "***NO SecTrust available!\n");
200 evalSecTrust(secTrust);
204 fprintf(stderr, "<<SecCmsSignedDataHasDigests returned false>>\n");
206 SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, dex);
207 CFStringRef emailAddrs = SecCmsSignerInfoGetSignerCommonName(signerInfo);
209 fprintf(stderr, " signer : ");
210 if(emailAddrs == NULL) {
211 printf("<<SecCmsSignerInfoGetSignerCommonName returned NULL)>>\n");
214 if(!CFStringGetCString(emailAddrs, emailStr, 1000, kCFStringEncodingASCII)) {
215 printf("*** Error converting email address to C string\n");
218 printf("%s\n", emailStr);
221 if(parseSignerCert) {
222 SecCertificateRef signer;
223 signer = SecCmsSignerInfoGetSigningCertificate(signerInfo, NULL);
226 ortn = SecCertificateGetData(signer, &certData);
228 fprintf(stderr, "***Error getting signing cert data***\n");
229 cssmPerror("SecCertificateGetData", ortn);
232 printf("========== Signer Cert==========\n\n");
233 printCert(certData.Data, certData.Length, CSSM_FALSE);
234 printf("========== End Signer Cert==========\n\n");
238 fprintf(stderr, "***Error getting signing cert ***\n");
245 static OSStatus doParse(
246 const unsigned char *data,
248 bool parseSignerCert,
249 unsigned char **outData, // mallocd and RETURNED
250 unsigned *outDataLen) // RETURNED
252 if((data == NULL) || (dataLen == 0)) {
253 printf("***Parse requires input file. Aborting.\n");
257 SecArenaPoolRef arena = NULL;
258 SecArenaPoolCreate(1024, &arena);
259 SecCmsMessageRef cmsMsg = NULL;
260 SecCmsDecoderRef decoder;
263 ortn = SecCmsDecoderCreate(arena, NULL, NULL, NULL, NULL, NULL, NULL, &decoder);
265 cssmPerror("SecCmsDecoderCreate", ortn);
268 ortn = SecCmsDecoderUpdate(decoder, data, dataLen);
270 cssmPerror("SecCmsDecoderUpdate", ortn);
273 ortn = SecCmsDecoderFinish(decoder, &cmsMsg);
275 cssmPerror("SecCmsDecoderFinish", ortn);
279 Boolean b = SecCmsMessageIsSigned(cmsMsg);
280 printf("=== CMS message info ===\n");
281 printf(" Signed : %s\n", b ? "true" : "false");
282 b = SecCmsMessageIsEncrypted(cmsMsg);
283 printf(" Encrypted : %s\n", b ? "true" : "false");
284 b = SecCmsMessageContainsCertsOrCrls(cmsMsg);
285 printf(" certs/crls : %s\n", b ? "present" : "not present");
286 int numContentInfos = SecCmsMessageContentLevelCount(cmsMsg);
287 printf(" Num ContentInfos : %d\n", numContentInfos);
290 for(int dex=0; dex<numContentInfos; dex++) {
291 SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsMsg, dex);
292 printf(" Content Info %d :\n", dex);
293 CSSM_OID *typeOid = SecCmsContentInfoGetContentTypeOID(ci);
294 printf(" OID Tag : ");
295 if(typeOid == NULL) {
296 printf("***NONE FOUND***]n");
298 else if(typeOid->Length == 0) {
299 printf("***EMPTY***\n");
302 char str[OID_PARSER_STRING_SIZE];
303 oidParser.oidParse(typeOid->Data, typeOid->Length, str);
306 SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
308 case SEC_OID_PKCS7_SIGNED_DATA:
310 SecCmsSignedDataRef sd =
311 (SecCmsSignedDataRef) SecCmsContentInfoGetContent(ci);
312 parseSignedData(sd, parseSignerCert);
315 case SEC_OID_PKCS7_DATA:
316 case SEC_OID_PKCS7_ENVELOPED_DATA:
317 case SEC_OID_PKCS7_ENCRYPTED_DATA:
320 printf(" other content type TBD\n");
324 CSSM_DATA_PTR odata = SecCmsMessageGetContent(cmsMsg);
326 printf("***No inner content available\n");
329 *outData = (unsigned char *)malloc(odata->Length);
330 memmove(*outData, odata->Data, odata->Length);
331 *outDataLen = odata->Length;
335 SecArenaPoolFree(arena, false);
337 /* free decoder? cmsMsg? */
342 * Common encode routine.
345 /* the simple way, when 3655861 is fixed */
346 static OSStatus encodeCms(
347 SecCmsMessageRef cmsMsg,
348 const unsigned char *inData, // add in this
350 unsigned char **outData, // mallocd and RETURNED
351 unsigned *outDataLen) // RETURNED
353 SecArenaPoolRef arena = NULL;
354 SecArenaPoolCreate(1024, &arena);
355 CSSM_DATA cdataIn = {inDataLen, (uint8 *)inData};
356 CSSM_DATA cdataOut = {0, NULL};
358 OSStatus ortn = SecCmsMessageEncode(cmsMsg, &cdataIn, arena, &cdataOut);
359 if((ortn == noErr) && (cdataOut.Length != 0)) {
360 *outData = (unsigned char *)malloc(cdataOut.Length);
361 memmove(*outData, cdataOut.Data, cdataOut.Length);
362 *outDataLen = cdataOut.Length;
368 SecArenaPoolFree(arena, false);
374 static OSStatus encodeCms(
375 SecCmsMessageRef cmsMsg,
376 const unsigned char *inData, // add in this
378 unsigned char **outData, // mallocd and RETURNED
379 unsigned *outDataLen) // RETURNED
381 SecArenaPoolRef arena = NULL;
382 SecArenaPoolCreate(1024, &arena);
383 SecCmsEncoderRef cmsEnc = NULL;
384 CSSM_DATA output = { 0, NULL };
387 ortn = SecCmsEncoderCreate(cmsMsg,
388 NULL, NULL, // no callback
389 &output, arena, // data goes here
390 NULL, NULL, // no password callback (right?)
391 NULL, NULL, // decrypt key callback
392 NULL, NULL, // detached digests
395 cssmPerror("SecKeychainItemCopyKeychain", ortn);
398 ortn = SecCmsEncoderUpdate(cmsEnc, (char *)inData, inDataLen);
400 cssmPerror("SecCmsEncoderUpdate", ortn);
403 ortn = SecCmsEncoderFinish(cmsEnc);
405 cssmPerror("SecCMsEncoderFinish", ortn);
409 /* Did we get any data? */
411 *outData = (unsigned char *)malloc(output.Length);
412 memmove(*outData, output.Data, output.Length);
413 *outDataLen = output.Length;
421 SecArenaPoolFree(arena, false);
428 static OSStatus doSign(
429 SecIdentityRef signerId,
430 const unsigned char *inData,
432 unsigned char **outData, // mallocd and RETURNED
433 unsigned *outDataLen) // RETURNED
435 if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
436 printf("***Sign requires input file. Aborting.\n");
439 if(signerId == NULL) {
440 printf("***Sign requires a signing identity. Aborting.\n");
444 SecCmsMessageRef cmsMsg = NULL;
445 SecCmsContentInfoRef contentInfo = NULL;
446 SecCmsSignedDataRef signedData = NULL;
447 SecCertificateRef ourCert = NULL;
448 SecCmsSignerInfoRef signerInfo;
450 SecKeychainRef ourKc = NULL;
452 ortn = SecIdentityCopyCertificate(signerId, &ourCert);
454 cssmPerror("SecIdentityCopyCertificate", ortn);
457 ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)ourCert, &ourKc);
459 cssmPerror("SecKeychainItemCopyKeychain", ortn);
463 // build chain of objects: message->signedData->data
464 cmsMsg = SecCmsMessageCreate(NULL);
466 printf("***Error creating SecCmsMessageRef\n");
470 signedData = SecCmsSignedDataCreate(cmsMsg);
471 if(signedData == NULL) {
472 printf("***Error creating SecCmsSignedDataRef\n");
476 contentInfo = SecCmsMessageGetContentInfo(cmsMsg);
477 ortn = SecCmsContentInfoSetContentSignedData(cmsMsg, contentInfo, signedData);
479 cssmPerror("SecCmsContentInfoSetContentSignedData", ortn);
482 contentInfo = SecCmsSignedDataGetContentInfo(signedData);
483 ortn = SecCmsContentInfoSetContentData(cmsMsg, contentInfo, NULL /* data */, false);
485 cssmPerror("SecCmsContentInfoSetContentData", ortn);
490 * create & attach signer information
492 signerInfo = SecCmsSignerInfoCreate(cmsMsg, signerId, SEC_OID_SHA1);
493 if (signerInfo == NULL) {
494 printf("***Error on SecCmsSignerInfoCreate\n");
498 /* we want the cert chain included for this one */
499 /* FIXME - what's the significance of the usage? */
500 ortn = SecCmsSignerInfoIncludeCerts(signerInfo, SecCmsCMCertChain, certUsageEmailSigner);
502 cssmPerror("SecCmsSignerInfoIncludeCerts", ortn);
506 /* other options go here - signing time, etc. */
508 ortn = SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc);
510 cssmPerror("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
513 ortn = SecCmsSignedDataAddCertificate(signedData, ourCert);
515 cssmPerror("SecCmsSignedDataAddCertificate", ortn);
519 ortn = SecCmsSignedDataAddSignerInfo(signedData, signerInfo);
521 cssmPerror("SecCmsSignedDataAddSignerInfo", ortn);
526 ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen);
530 SecCmsMessageDestroy(cmsMsg);
541 static OSStatus doEncrypt(
542 SecCertificateRef recipCert, // eventually more than one
543 const unsigned char *inData,
545 unsigned char **outData, // mallocd and RETURNED
546 unsigned *outDataLen) // RETURNED
548 if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
549 printf("***Sign requires input file. Aborting.\n");
552 if(recipCert == NULL) {
553 printf("***Encrypt requires a recipient certificate. Aborting.\n");
557 SecCmsMessageRef cmsMsg = NULL;
558 SecCmsContentInfoRef contentInfo = NULL;
559 SecCmsEnvelopedDataRef envelopedData = NULL;
560 SecCmsRecipientInfoRef recipientInfo = NULL;
562 SecCertificateRef allCerts[2] = { recipCert, NULL};
564 SECOidTag algorithmTag;
567 ortn = SecSMIMEFindBulkAlgForRecipients(allCerts, &algorithmTag, &keySize);
569 cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn);
573 // build chain of objects: message->envelopedData->data
574 cmsMsg = SecCmsMessageCreate(NULL);
576 printf("***Error creating SecCmsMessageRef\n");
580 envelopedData = SecCmsEnvelopedDataCreate(cmsMsg, algorithmTag, keySize);
581 if(envelopedData == NULL) {
582 printf("***Error creating SecCmsEnvelopedDataRef\n");
586 contentInfo = SecCmsMessageGetContentInfo(cmsMsg);
587 ortn = SecCmsContentInfoSetContentEnvelopedData(cmsMsg, contentInfo, envelopedData);
589 cssmPerror("SecCmsContentInfoSetContentEnvelopedData", ortn);
592 contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData);
593 ortn = SecCmsContentInfoSetContentData(cmsMsg, contentInfo, NULL /* data */, false);
595 cssmPerror("SecCmsContentInfoSetContentData", ortn);
600 * create & attach recipient information
602 recipientInfo = SecCmsRecipientInfoCreate(cmsMsg, recipCert);
603 ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
605 cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn);
611 ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen);
615 SecCmsMessageDestroy(cmsMsg);
620 int main(int argc, char **argv)
628 if(!strcmp(argv[1], "sign")) {
632 else if(!strcmp(argv[1], "envel")) {
635 else if(!strcmp(argv[1], "parse")) {
639 printf("***Unrecognized cmd.\n");
649 const char *keychainName = NULL;
650 char *inFileName = NULL;
651 char *outFileName = NULL;
652 bool useIdPicker = false;
653 char *recipient = NULL;
655 bool parseSignerCert = false;
658 while ((arg = getopt(argc, argv, "i:o:k:pr:qc")) != -1) {
664 outFileName = optarg;
667 keychainName = optarg;
676 parseSignerCert = true;
687 /* getopt does not return '?' */
691 SecIdentityRef idRef = NULL;
692 SecKeychainRef kcRef = NULL;
693 SecCertificateRef recipientCert;
694 unsigned char *inData = NULL;
695 unsigned inDataLen = 0;
696 unsigned char *outData = NULL;
697 unsigned outDataLen = 0;
701 if(readFile(inFileName, &inData, &inDataLen)) {
702 printf("***Error reading infile %s. Aborting.\n", inFileName);
707 ortn = SecKeychainOpen(keychainName, &kcRef);
709 cssmPerror("SecKeychainOpen", ortn);
714 ortn = sslSimpleIdentPicker(kcRef, &idRef);
716 printf("Error obtaining idenity via picker. Aborting.\n");
721 /* use first identity in specified keychain */
722 CFArrayRef array = sslKcRefToCertArray(kcRef, CSSM_FALSE, CSSM_FALSE, NULL);
724 printf("***Error finding a signing cert. Aborting.\n");
727 idRef = (SecIdentityRef)CFArrayGetValueAtIndex(array, 0);
729 printf("***No identities found. Aborting.\n");
736 ortn = findCert(recipient, kcRef, &recipientCert);
744 ortn = doSign(idRef, inData, inDataLen, &outData, &outDataLen);
747 ortn = doEncrypt(recipientCert, inData, inDataLen, &outData, &outDataLen);
750 ortn = doParse(inData, inDataLen, parseSignerCert, &outData, &outDataLen);
756 if(outData && outFileName) {
757 if(writeFile(outFileName, outData, outDataLen)) {
758 printf("***Error writing to %s.\n", outFileName);
761 printf("...wrote %lu bytes to %s.\n", outDataLen, outFileName);
765 printf("...generated %lu bytes but no place to write it.\n", outDataLen);
767 else if(outFileName) {
768 printf("...nothing to write to file %s.\n", outFileName);