/* * cmstool.cpp - manipluate CMS messages, intended to be an alternate for the * currently useless cms command in /usr/bin/security */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void usage(char **argv) { printf("Usage: %s cmd [option ...]\n", argv[0]); printf("cmd values:\n"); printf(" sign -- create signedData\n"); printf(" envel -- create envelopedData\n"); printf(" signEnv -- create nested EnvelopedData(signedData(data))\n"); printf(" parse -- parse a CMS message file\n"); printf("Options:\n"); printf(" -i infile\n"); printf(" -o outfile\n"); printf(" -k keychain -- Keychain to search for certs\n"); printf(" -p -- Use identity picker\n"); printf(" -r recipient -- specify recipient of enveloped data\n"); printf(" -c -- parse signer cert\n"); printf(" -v sign|encr -- verify message is signed/encrypted\n"); printf(" -e eContentType -- a(uthData)|r(keyData)\n"); printf(" -d detached -- infile contains detached content (sign only)\n"); printf(" -D detachedContent -- detached content (parse only)\n"); printf(" -q -- quiet\n"); exit(1); } /* high level op */ typedef enum { CTO_Sign, CTO_Envelop, CTO_SignEnvelop, CTO_Parse } CT_Op; /* to verify */ typedef enum { CTV_None, CTV_Sign, CTV_Envelop, CTV_SignEnvelop } CT_Vfy; /* additional OIDS to specify as eContentType */ #define OID_PKINIT 0x2B, 6, 1, 5, 2, 3 #define OID_PKINIT_LEN 6 static const uint8 OID_PKINIT_AUTH_DATA[] = {OID_PKINIT, 1}; static const uint8 OID_PKINIT_DH_KEY_DATA[] = {OID_PKINIT, 2}; static const uint8 OID_PKINIT_RKEY_DATA[] = {OID_PKINIT, 3}; static const uint8 OID_PKINIT_KP_CLIENTAUTH[] = {OID_PKINIT, 3}; static const uint8 OID_PKINIT_KPKDC[] = {OID_PKINIT, 5}; static const CSSM_OID CSSMOID_PKINIT_AUTH_DATA = {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_AUTH_DATA}; static const CSSM_OID CSSMOID_PKINIT_DH_KEY_DATA = {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_DH_KEY_DATA}; static const CSSM_OID CSSMOID_PKINIT_RKEY_DATA = {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_RKEY_DATA}; static const CSSM_OID CSSMOID_PKINIT_KP_CLIENTAUTH = {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KP_CLIENTAUTH}; static const CSSM_OID CSSMOID_PKINIT_KPKDC = {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KPKDC}; typedef struct { CSSM_OID contentType; CSSM_DATA content; } SimpleContentInfo; const SecAsn1Template SimpleContentInfoTemplate[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SimpleContentInfo) }, { SEC_ASN1_OBJECT_ID, offsetof(SimpleContentInfo, contentType) }, { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(SimpleContentInfo, content), kSecAsn1AnyTemplate }, { 0, } }; /* * Obtain the content of a contentInfo, This basically strips off the contentType OID * and returns a mallocd copy of the ASN_ANY content. */ static OSStatus ContentInfoContent( const unsigned char *contentInfo, unsigned contentInfoLen, unsigned char **content, /* mallocd and RETURNED */ unsigned *contentLen) /* RETURNED */ { SecAsn1CoderRef coder = NULL; OSStatus ortn; SimpleContentInfo decodedInfo; ortn = SecAsn1CoderCreate(&coder); if(ortn) { return ortn; } memset(&decodedInfo, 0, sizeof(decodedInfo)); ortn = SecAsn1Decode(coder, contentInfo, contentInfoLen, SimpleContentInfoTemplate, &decodedInfo); if(ortn) { goto errOut; } if(decodedInfo.content.Data == NULL) { printf("***Error decoding contentInfo: no content\n"); ortn = internalComponentErr; goto errOut; } *content = (unsigned char *)malloc(decodedInfo.content.Length); memmove(*content, decodedInfo.content.Data, decodedInfo.content.Length); *contentLen = decodedInfo.content.Length; errOut: SecAsn1CoderRelease(coder); return ortn; } /* * Find a cert in specified keychain or keychain list matching specified * email address. We happen to knopw that the email address is stored with the * kSecKeyAlias attribute. */ static OSStatus findCert( const char *emailAddress, CFTypeRef kcArArray, // kc, array, or even NULL SecCertificateRef *cert) { OSStatus ortn; SecKeychainSearchRef srch; SecKeychainAttributeList attrList; SecKeychainAttribute attr; attr.tag = kSecKeyAlias; attr.length = strlen(emailAddress); attr.data = (void *)emailAddress; attrList.count = 1; attrList.attr = &attr; ortn = SecKeychainSearchCreateFromAttributes(kcArArray, kSecCertificateItemClass, &attrList, &srch); if(ortn) { cssmPerror("SecKeychainSearchCreateFromAttributes", ortn); return ortn; } ortn = SecKeychainSearchCopyNext(srch, (SecKeychainItemRef *)cert); if(ortn) { printf("***No certs founmd matching recipient %s. Aborting.\n", emailAddress); return ortn; } CFRelease(srch); return noErr; } static void evalSecTrust( SecTrustRef secTrust, bool quiet) { OSStatus ortn; SecTrustResultType secTrustResult; ortn = SecTrustEvaluate(secTrust, &secTrustResult); if(ortn) { /* should never happen */ cssmPerror("SecTrustEvaluate", ortn); return; } switch(secTrustResult) { case kSecTrustResultUnspecified: /* cert chain valid, no special UserTrust assignments */ case kSecTrustResultProceed: /* cert chain valid AND user explicitly trusts this */ if(!quiet) { fprintf(stderr, "Successful\n"); } return; case kSecTrustResultDeny: case kSecTrustResultConfirm: /* * Cert chain may well have verified OK, but user has flagged * one of these certs as untrustable. */ printf("Not trusted per user-specified Trust level\n"); return; default: { /* get low-level TP error */ OSStatus tpStatus; ortn = SecTrustGetCssmResultCode(secTrust, &tpStatus); if(ortn) { cssmPerror("SecTrustGetCssmResultCode", ortn); return; } switch(tpStatus) { case CSSMERR_TP_INVALID_ANCHOR_CERT: fprintf(stderr, "Untrusted root\n"); return; case CSSMERR_TP_NOT_TRUSTED: /* no root, not even in implicit SSL roots */ fprintf(stderr, "No root cert found\n"); return; case CSSMERR_TP_CERT_EXPIRED: fprintf(stderr, "Expired cert\n"); return; case CSSMERR_TP_CERT_NOT_VALID_YET: fprintf(stderr, "Cert not valid yet\n"); break; default: printf("Other cert failure: "); cssmPerror("", tpStatus); return; } } } /* SecTrustEvaluate error */ } static OSStatus parseSignedData( SecCmsSignedDataRef signedData, SecArenaPoolRef arena, /* used for detached content only */ const unsigned char *detachedData, unsigned detachedDataLen, CT_Vfy vfyOp, bool quiet, bool parseSignerCert) { Boolean b; b = SecCmsSignedDataHasDigests(signedData); if(!quiet) { printf(" has digests : %s\n", b ? "true" : "false"); } SecTrustRef secTrust = NULL; OSStatus ortn; SecPolicyRef policy = NULL; SecPolicySearchRef policySearch = NULL; ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &policySearch); if(ortn) { cssmPerror("SecPolicySearchCreate", ortn); return ortn; } ortn = SecPolicySearchCopyNext(policySearch, &policy); if(ortn) { cssmPerror("SecPolicySearchCopyNext", ortn); return ortn; } int numSigners = SecCmsSignedDataSignerInfoCount(signedData); if(!quiet) { printf(" num signers : %d\n", numSigners); } for(int dex=0; dex "); /* FIXME - does this make sense? Error? */ } } else if(detachedData != NULL) { /* digest the detached content */ SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(signedData); SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestAlgorithms); CSSM_DATA **digests = NULL; SecCmsDigestContextUpdate(digcx, detachedData, detachedDataLen); ortn = SecCmsDigestContextFinishMultiple(digcx, arena, &digests); if(ortn) { fprintf(stderr, "SecCmsDigestContextFinishMultiple() returned %d\n", (int)ortn); } else { SecCmsSignedDataSetDigests(signedData, digestAlgorithms, digests); } } else { fprintf(stderr, " "); } ortn = SecCmsSignedDataVerifySignerInfo(signedData, dex, NULL, policy, &secTrust); if(ortn) { fprintf(stderr, "vfSignerInfo() returned %d\n", (int)ortn); fprintf(stderr, " vfy status : "); } if(secTrust == NULL) { fprintf(stderr, "***NO SecTrust available!\n"); } else { evalSecTrust(secTrust, quiet); } SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, dex); CFStringRef emailAddrs = SecCmsSignerInfoGetSignerCommonName(signerInfo); char emailStr[1000]; if(!quiet) { fprintf(stderr, " signer : "); } if(emailAddrs == NULL) { fprintf(stderr, "<>\n"); } else { if(!CFStringGetCString(emailAddrs, emailStr, 1000, kCFStringEncodingASCII)) { fprintf(stderr, "*** Error converting email address to C string\n"); } else if(!quiet) { fprintf(stderr, "%s\n", emailStr); } } if(parseSignerCert) { SecCertificateRef signer; signer = SecCmsSignerInfoGetSigningCertificate(signerInfo, NULL); if(signer) { CSSM_DATA certData; ortn = SecCertificateGetData(signer, &certData); if(ortn) { fprintf(stderr, "***Error getting signing cert data***\n"); cssmPerror("SecCertificateGetData", ortn); } else { printf("========== Signer Cert==========\n\n"); printCert(certData.Data, certData.Length, CSSM_FALSE); printf("========== End Signer Cert==========\n\n"); } } else { fprintf(stderr, "***Error getting signing cert ***\n"); } } } return ortn; } static OSStatus doParse( const unsigned char *data, unsigned dataLen, const unsigned char *detachedData, unsigned detachedDataLen, CT_Vfy vfyOp, bool parseSignerCert, bool quiet, unsigned char **outData, // mallocd and RETURNED unsigned *outDataLen) // RETURNED { if((data == NULL) || (dataLen == 0)) { fprintf(stderr, "***Parse requires input file. Aborting.\n"); return paramErr; } SecArenaPoolRef arena = NULL; SecArenaPoolCreate(1024, &arena); SecCmsMessageRef cmsMsg = NULL; SecCmsDecoderRef decoder; OSStatus ortn; OSStatus ourRtn = noErr; bool foundOneSigned = false; bool foundOneEnveloped = false; ortn = SecCmsDecoderCreate(arena, NULL, NULL, NULL, NULL, NULL, NULL, &decoder); if(ortn) { cssmPerror("SecCmsDecoderCreate", ortn); return ortn; } ortn = SecCmsDecoderUpdate(decoder, data, dataLen); if(ortn) { cssmPerror("SecCmsDecoderUpdate", ortn); return ortn; } ortn = SecCmsDecoderFinish(decoder, &cmsMsg); if(ortn) { cssmPerror("SecCmsDecoderFinish", ortn); return ortn; } Boolean b = SecCmsMessageIsSigned(cmsMsg); switch(vfyOp) { case CTV_None: break; case CTV_Sign: if(!b) { fprintf(stderr, "***Expected SignedData, but !SecCmsMessageIsSigned()\n"); ourRtn = -1; } break; case CTV_SignEnvelop: if(!b) { fprintf(stderr, "***Expected Signed&Enveloped, but !SecCmsMessageIsSigned()\n"); ourRtn = -1; } break; case CTV_Envelop: if(b) { fprintf(stderr, "***Expected EnvelopedData, but SecCmsMessageIsSigned() " "TRUE\n"); ourRtn = -1; } break; } int numContentInfos = SecCmsMessageContentLevelCount(cmsMsg); if(!quiet) { fprintf(stderr, "=== CMS message info ===\n"); fprintf(stderr, " Signed : %s\n", b ? "true" : "false"); b = SecCmsMessageIsEncrypted(cmsMsg); fprintf(stderr, " Encrypted : %s\n", b ? "true" : "false"); b = SecCmsMessageContainsCertsOrCrls(cmsMsg); fprintf(stderr, " certs/crls : %s\n", b ? "present" : "not present"); fprintf(stderr, " Num ContentInfos : %d\n", numContentInfos); } /* FIXME needs work for CTV_SignEnvelop */ OidParser oidParser; for(int dex=0; dexLength == 0) { printf("***EMPTY***\n"); } else { char str[OID_PARSER_STRING_SIZE]; oidParser.oidParse(typeOid->Data, typeOid->Length, str); printf("%s\n", str); } } SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); switch(tag) { case SEC_OID_PKCS7_SIGNED_DATA: { switch(vfyOp) { case CTV_None: // caller doesn't care case CTV_Sign: // got what we wanted break; case CTV_Envelop: fprintf(stderr, "***Expected EnvelopedData, got SignedData\n"); ourRtn = -1; break; case CTV_SignEnvelop: printf("CTV_SignEnvelop code on demand\n"); break; } foundOneSigned = true; SecCmsSignedDataRef sd = (SecCmsSignedDataRef) SecCmsContentInfoGetContent(ci); parseSignedData(sd, arena, detachedData, detachedDataLen, vfyOp, quiet, parseSignerCert); break; } case SEC_OID_PKCS7_DATA: case SEC_OID_OTHER: break; case SEC_OID_PKCS7_ENVELOPED_DATA: foundOneEnveloped = true; if(vfyOp == CTV_Sign) { fprintf(stderr, "***Expected SignedData, EnvelopedData\n"); ourRtn = -1; break; } case SEC_OID_PKCS7_ENCRYPTED_DATA: switch(vfyOp) { case CTV_None: break; case CTV_Sign: fprintf(stderr, "***Expected SignedData, got EncryptedData\n"); ourRtn = -1; break; case CTV_Envelop: fprintf(stderr, "***Expected EnvelopedData, got EncryptedData\n"); ourRtn = -1; break; case CTV_SignEnvelop: printf("CTV_SignEnvelop code on demand\n"); break; } break; default: fprintf(stderr, " other content type TBD\n"); } } if(outData) { CSSM_DATA_PTR odata = SecCmsMessageGetContent(cmsMsg); if(odata == NULL) { fprintf(stderr, "***No inner content available\n"); } else { *outData = (unsigned char *)malloc(odata->Length); memmove(*outData, odata->Data, odata->Length); *outDataLen = odata->Length; } } if(arena) { SecArenaPoolFree(arena, false); } switch(vfyOp) { case CTV_None: break; case CTV_Sign: if(!foundOneSigned) { fprintf(stderr, "Expected signed, never saw a SignedData\n"); ourRtn = -1; } break; case CTV_Envelop: if(!foundOneEnveloped) { fprintf(stderr, "Expected enveloped, never saw an EnvelopedData\n"); ourRtn = -1; } break; case CTV_SignEnvelop: if(!foundOneSigned) { fprintf(stderr, "Expected signed, never saw a SignedData\n"); ourRtn = -1; } if(!foundOneEnveloped) { fprintf(stderr, "Expected enveloped, never saw an EnvelopedData\n"); ourRtn = -1; } break; } /* free decoder? cmsMsg? */ return ourRtn; } /* * Common encode routine. */ #if 1 /* the simple way, when 3655861 is fixed */ static OSStatus encodeCms( SecCmsMessageRef cmsMsg, const unsigned char *inData, // add in this unsigned inDataLen, unsigned char **outData, // mallocd and RETURNED unsigned *outDataLen) // RETURNED { SecArenaPoolRef arena = NULL; SecArenaPoolCreate(1024, &arena); CSSM_DATA cdataIn = {inDataLen, (uint8 *)inData}; CSSM_DATA cdataOut = {0, NULL}; OSStatus ortn = SecCmsMessageEncode(cmsMsg, &cdataIn, arena, &cdataOut); if((ortn == noErr) && (cdataOut.Length != 0)) { *outData = (unsigned char *)malloc(cdataOut.Length); memmove(*outData, cdataOut.Data, cdataOut.Length); *outDataLen = cdataOut.Length; } else { cssmPerror("SecCmsMessageEncode", ortn); *outData = NULL; *outDataLen = 0; } SecArenaPoolFree(arena, false); return ortn; } #else /* the hard way back when SecCmsMessageEncode() didn't work */ static OSStatus encodeCms( SecCmsMessageRef cmsMsg, const unsigned char *inData, // add in this unsigned inDataLen, unsigned char **outData, // mallocd and RETURNED unsigned *outDataLen) // RETURNED { SecArenaPoolRef arena = NULL; SecArenaPoolCreate(1024, &arena); SecCmsEncoderRef cmsEnc = NULL; CSSM_DATA output = { 0, NULL }; OSStatus ortn; ortn = SecCmsEncoderCreate(cmsMsg, NULL, NULL, // no callback &output, arena, // data goes here NULL, NULL, // no password callback (right?) NULL, NULL, // decrypt key callback NULL, NULL, // detached digests &cmsEnc); if(ortn) { cssmPerror("SecKeychainItemCopyKeychain", ortn); goto errOut; } ortn = SecCmsEncoderUpdate(cmsEnc, (char *)inData, inDataLen); if(ortn) { cssmPerror("SecCmsEncoderUpdate", ortn); goto errOut; } ortn = SecCmsEncoderFinish(cmsEnc); if(ortn) { cssmPerror("SecCMsEncoderFinish", ortn); goto errOut; } /* Did we get any data? */ if(output.Length) { *outData = (unsigned char *)malloc(output.Length); memmove(*outData, output.Data, output.Length); *outDataLen = output.Length; } else { *outData = NULL; *outDataLen = 0; } errOut: if(arena) { SecArenaPoolFree(arena, false); } return ortn; } #endif static OSStatus doSign( SecIdentityRef signerId, const unsigned char *inData, unsigned inDataLen, bool detachedContent, const CSSM_OID *eContentType, // OPTIONAL unsigned char **outData, // mallocd and RETURNED unsigned *outDataLen) // RETURNED { if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) { fprintf(stderr, "***Sign requires input file. Aborting.\n"); return paramErr; } if(signerId == NULL) { fprintf(stderr, "***Sign requires a signing identity. Aborting.\n"); return paramErr; } SecCmsMessageRef cmsMsg = NULL; SecCmsContentInfoRef contentInfo = NULL; SecCmsSignedDataRef signedData = NULL; SecCertificateRef ourCert = NULL; SecCmsSignerInfoRef signerInfo; OSStatus ortn; SecKeychainRef ourKc = NULL; ortn = SecIdentityCopyCertificate(signerId, &ourCert); if(ortn) { cssmPerror("SecIdentityCopyCertificate", ortn); return ortn; } ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)ourCert, &ourKc); if(ortn) { cssmPerror("SecKeychainItemCopyKeychain", ortn); goto errOut; } // build chain of objects: message->signedData->data cmsMsg = SecCmsMessageCreate(NULL); if(cmsMsg == NULL) { fprintf(stderr, "***Error creating SecCmsMessageRef\n"); ortn = -1; goto errOut; } signedData = SecCmsSignedDataCreate(cmsMsg); if(signedData == NULL) { printf("***Error creating SecCmsSignedDataRef\n"); ortn = -1; goto errOut; } contentInfo = SecCmsMessageGetContentInfo(cmsMsg); ortn = SecCmsContentInfoSetContentSignedData(cmsMsg, contentInfo, signedData); if(ortn) { cssmPerror("SecCmsContentInfoSetContentSignedData", ortn); goto errOut; } contentInfo = SecCmsSignedDataGetContentInfo(signedData); if(eContentType != NULL) { ortn = SecCmsContentInfoSetContentOther(cmsMsg, contentInfo, NULL /* data */, detachedContent, eContentType); if(ortn) { cssmPerror("SecCmsContentInfoSetContentData", ortn); goto errOut; } } else { ortn = SecCmsContentInfoSetContentData(cmsMsg, contentInfo, NULL /* data */, detachedContent); if(ortn) { cssmPerror("SecCmsContentInfoSetContentData", ortn); goto errOut; } } /* * create & attach signer information */ signerInfo = SecCmsSignerInfoCreate(cmsMsg, signerId, SEC_OID_SHA1); if (signerInfo == NULL) { fprintf(stderr, "***Error on SecCmsSignerInfoCreate\n"); ortn = -1; goto errOut; } /* we want the cert chain included for this one */ /* FIXME - what's the significance of the usage? */ ortn = SecCmsSignerInfoIncludeCerts(signerInfo, SecCmsCMCertChain, certUsageEmailSigner); if(ortn) { cssmPerror("SecCmsSignerInfoIncludeCerts", ortn); goto errOut; } /* other options go here - signing time, etc. */ ortn = SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc); if(ortn) { cssmPerror("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn); goto errOut; } ortn = SecCmsSignedDataAddCertificate(signedData, ourCert); if(ortn) { cssmPerror("SecCmsSignedDataAddCertificate", ortn); goto errOut; } ortn = SecCmsSignedDataAddSignerInfo(signedData, signerInfo); if(ortn) { cssmPerror("SecCmsSignedDataAddSignerInfo", ortn); goto errOut; } /* go */ ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen); errOut: /* free resources */ if(cmsMsg) { SecCmsMessageDestroy(cmsMsg); } if(ourCert) { CFRelease(ourCert); } if(ourKc) { CFRelease(ourKc); } return ortn; } static OSStatus doEncrypt( SecCertificateRef recipCert, // eventually more than one const unsigned char *inData, unsigned inDataLen, unsigned char **outData, // mallocd and RETURNED unsigned *outDataLen) // RETURNED { if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) { fprintf(stderr, "***Encrypt requires input file. Aborting.\n"); return paramErr; } if(recipCert == NULL) { fprintf(stderr, "***Encrypt requires a recipient certificate. Aborting.\n"); return paramErr; } SecCmsMessageRef cmsMsg = NULL; SecCmsContentInfoRef contentInfo = NULL; SecCmsEnvelopedDataRef envelopedData = NULL; SecCmsRecipientInfoRef recipientInfo = NULL; OSStatus ortn; SecCertificateRef allCerts[2] = { recipCert, NULL}; SECOidTag algorithmTag; int keySize; ortn = SecSMIMEFindBulkAlgForRecipients(allCerts, &algorithmTag, &keySize); if(ortn) { cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn); return ortn; } // build chain of objects: message->envelopedData->data cmsMsg = SecCmsMessageCreate(NULL); if(cmsMsg == NULL) { fprintf(stderr, "***Error creating SecCmsMessageRef\n"); ortn = -1; goto errOut; } envelopedData = SecCmsEnvelopedDataCreate(cmsMsg, algorithmTag, keySize); if(envelopedData == NULL) { fprintf(stderr, "***Error creating SecCmsEnvelopedDataRef\n"); ortn = -1; goto errOut; } contentInfo = SecCmsMessageGetContentInfo(cmsMsg); ortn = SecCmsContentInfoSetContentEnvelopedData(cmsMsg, contentInfo, envelopedData); if(ortn) { cssmPerror("SecCmsContentInfoSetContentEnvelopedData", ortn); goto errOut; } contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData); ortn = SecCmsContentInfoSetContentData(cmsMsg, contentInfo, NULL /* data */, false); if(ortn) { cssmPerror("SecCmsContentInfoSetContentData", ortn); goto errOut; } /* * create & attach recipient information */ recipientInfo = SecCmsRecipientInfoCreate(cmsMsg, recipCert); ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo); if(ortn) { cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn); goto errOut; } /* go */ ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen); errOut: /* free resources */ if(cmsMsg) { SecCmsMessageDestroy(cmsMsg); } return ortn; } /* create nested message: msg = EnvelopedData(SignedData(inData)) */ static OSStatus doSignEncrypt( SecCertificateRef recipCert, // encryption recipient SecIdentityRef signerId, // signer const CSSM_OID *eContentType, // OPTIONAL - for signedData const unsigned char *inData, unsigned inDataLen, unsigned char **outData, // mallocd and RETURNED unsigned *outDataLen) // RETURNED { if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) { fprintf(stderr, "***Sign/Encrypt requires input file. Aborting.\n"); return paramErr; } if(recipCert == NULL) { fprintf(stderr, "***Sign/Encrypt requires a recipient certificate. Aborting.\n"); return paramErr; } if(signerId == NULL) { fprintf(stderr, "***Sign/Encrypt requires a signer Identity. Aborting.\n"); return paramErr; } OSStatus ortn; unsigned char *signedData = NULL; unsigned signedDataLen = 0; SecCmsMessageRef cmsMsg = NULL; SecCmsContentInfoRef contentInfo = NULL; SecCmsEnvelopedDataRef envelopedData = NULL; SecCmsRecipientInfoRef recipientInfo = NULL; SecCertificateRef allCerts[2] = { recipCert, NULL}; SECOidTag algorithmTag; int keySize; /* first get a SignedData */ ortn = doSign(signerId, inData, inDataLen, false, /* can't do detached content here */ eContentType, &signedData, &signedDataLen); if(ortn) { printf("***Error generating inner signedData. Aborting.\n"); return ortn; } /* extract just the content - don't need the whole ContentINfo */ unsigned char *signedDataContent = NULL; unsigned signedDataContentLen = 0; ortn = ContentInfoContent(signedData, signedDataLen, &signedDataContent, &signedDataContentLen); if(ortn) { goto errOut; } /* now wrap that in an EnvelopedData */ ortn = SecSMIMEFindBulkAlgForRecipients(allCerts, &algorithmTag, &keySize); if(ortn) { cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn); return ortn; } // build chain of objects: message->envelopedData->data cmsMsg = SecCmsMessageCreate(NULL); if(cmsMsg == NULL) { fprintf(stderr, "***Error creating SecCmsMessageRef\n"); ortn = -1; goto errOut; } envelopedData = SecCmsEnvelopedDataCreate(cmsMsg, algorithmTag, keySize); if(envelopedData == NULL) { fprintf(stderr, "***Error creating SecCmsEnvelopedDataRef\n"); ortn = -1; goto errOut; } contentInfo = SecCmsMessageGetContentInfo(cmsMsg); ortn = SecCmsContentInfoSetContentEnvelopedData(cmsMsg, contentInfo, envelopedData); if(ortn) { cssmPerror("SecCmsContentInfoSetContentEnvelopedData", ortn); goto errOut; } contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData); /* here's the difference: we override the 'data' content with a SignedData type, * but we fool the smime lib into thinking it's a plain old data so it doesn't try * to encode the SignedData */ ortn = SecCmsContentInfoSetContentOther(cmsMsg, contentInfo, NULL /* data */, false, &CSSMOID_PKCS7_SignedData); if(ortn) { cssmPerror("SecCmsContentInfoSetContentData", ortn); goto errOut; } /* * create & attach recipient information */ recipientInfo = SecCmsRecipientInfoCreate(cmsMsg, recipCert); ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo); if(ortn) { cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn); goto errOut; } /* go */ ortn = encodeCms(cmsMsg, signedDataContent, signedDataContentLen, outData, outDataLen); errOut: /* free resources */ if(cmsMsg) { SecCmsMessageDestroy(cmsMsg); } if(signedData) { free(signedData); } if(signedDataContent) { free(signedDataContent); } return ortn; } int main(int argc, char **argv) { if(argc < 2) { usage(argv); } CT_Op op; bool needId = false; if(!strcmp(argv[1], "sign")) { op = CTO_Sign; needId = true; } else if(!strcmp(argv[1], "envel")) { op = CTO_Envelop; } else if(!strcmp(argv[1], "signEnv")) { op = CTO_SignEnvelop; needId = true; } else if(!strcmp(argv[1], "parse")) { op = CTO_Parse; } else { fprintf(stderr, "***Unrecognized cmd.\n"); usage(argv); } extern int optind; extern char *optarg; int arg; /* optional args */ const char *keychainName = NULL; char *inFileName = NULL; char *outFileName = NULL; bool detachedContent = false; char *detachedFile = NULL; bool useIdPicker = false; char *recipient = NULL; bool quiet = false; bool parseSignerCert = false; CT_Vfy vfyOp = CTV_None; const CSSM_OID *eContentType = NULL; optind = 2; while ((arg = getopt(argc, argv, "i:o:k:pr:e:dD:qcv:")) != -1) { switch (arg) { case 'i': inFileName = optarg; break; case 'o': outFileName = optarg; break; case 'k': keychainName = optarg; break; case 'p': useIdPicker = true; break; case 'r': recipient = optarg; break; case 'c': parseSignerCert = true; break; case 'v': if(!strcmp(optarg, "sign")) { vfyOp = CTV_Sign; } else if(!strcmp(optarg, "encr")) { vfyOp = CTV_Envelop; } else if(!strcmp(optarg, "signEnv")) { vfyOp = CTV_SignEnvelop; } else { usage(argv); } break; case 'e': switch(optarg[0]) { case 'a': eContentType = &CSSMOID_PKINIT_AUTH_DATA; break; case 'r': eContentType = &CSSMOID_PKINIT_RKEY_DATA; break; default: usage(argv); } break; case 'd': if(op != CTO_Sign) { printf("-d only valid for op sign\n"); exit(1); } detachedContent = true; break; case 'D': if(op != CTO_Parse) { printf("-D only valid for op sign\n"); exit(1); } detachedFile = optarg; break; case 'q': quiet = true; break; default: case '?': usage(argv); } } if(optind != argc) { /* getopt does not return '?' */ usage(argv); } SecIdentityRef idRef = NULL; SecKeychainRef kcRef = NULL; SecCertificateRef recipientCert = NULL; unsigned char *inData = NULL; unsigned inDataLen = 0; unsigned char *outData = NULL; unsigned outDataLen = 0; unsigned char *detachedData = NULL; unsigned detachedDataLen = 0; OSStatus ortn; if(inFileName) { if(readFile(inFileName, &inData, &inDataLen)) { fprintf(stderr, "***Error reading infile %s. Aborting.\n", inFileName); exit(1); } } if(detachedFile) { if(readFile(detachedFile, &detachedData, &detachedDataLen)) { fprintf(stderr, "***Error reading detachedFile %s. Aborting.\n", detachedFile); exit(1); } } if(keychainName) { ortn = SecKeychainOpen(keychainName, &kcRef); if(ortn) { cssmPerror("SecKeychainOpen", ortn); exit(1); } } if(useIdPicker) { ortn = sslSimpleIdentPicker(kcRef, &idRef); if(ortn) { fprintf(stderr, "***Error obtaining identity via picker. Aborting.\n"); exit(1); } } else if(needId) { /* use first identity in specified keychain */ CFArrayRef array = sslKcRefToCertArray(kcRef, CSSM_FALSE, CSSM_FALSE, NULL, // no verify policy NULL); if(array == NULL) { fprintf(stderr, "***Error finding a signing cert. Aborting.\n"); exit(1); } idRef = (SecIdentityRef)CFArrayGetValueAtIndex(array, 0); if(idRef == NULL) { fprintf(stderr, "***No identities found. Aborting.\n"); exit(1); } CFRetain(idRef); CFRelease(array); } if(recipient) { ortn = findCert(recipient, kcRef, &recipientCert); if(ortn) { exit(1); } } switch(op) { case CTO_Sign: ortn = doSign(idRef, inData, inDataLen, detachedContent, eContentType, &outData, &outDataLen); break; case CTO_Envelop: if(recipientCert == NULL) { if(idRef == NULL) { printf("***Need a recipient or an identity to encrypt\n"); exit(1); } ortn = SecIdentityCopyCertificate(idRef, &recipientCert); if(ortn) { cssmPerror("SecIdentityCopyCertificate", ortn); exit(1); } } ortn = doEncrypt(recipientCert, inData, inDataLen, &outData, &outDataLen); break; case CTO_SignEnvelop: ortn = doSignEncrypt(recipientCert, idRef, eContentType, inData, inDataLen, &outData, &outDataLen); break; case CTO_Parse: ortn = doParse(inData, inDataLen, detachedData, detachedDataLen, vfyOp, parseSignerCert, quiet, &outData, &outDataLen); break; } if(ortn) { goto errOut; } if(outData && outFileName) { if(writeFile(outFileName, outData, outDataLen)) { fprintf(stderr, "***Error writing to %s.\n", outFileName); ortn = -1; } else { if(!quiet) { fprintf(stderr, "...wrote %u bytes to %s.\n", outDataLen, outFileName); } } } else if(outData) { fprintf(stderr, "...generated %u bytes but no place to write it.\n", outDataLen); } else if(outFileName) { fprintf(stderr, "...nothing to write to file %s.\n", outFileName); /* assume this is an error, caller wanted something */ ortn = -1; } errOut: return ortn; }