#include <Security/Security.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utilLib/common.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <security_cdsa_utils/cuPrintCert.h>
#include <clAppUtils/identPicker.h>
#include <clAppUtils/sslAppUtils.h>
#include <security_cdsa_utils/cuOidParser.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <Security/SecCmsEncoder.h>
#include <Security/SecCmsDecoder.h>
#include <Security/SecCmsEncryptedData.h>
#include <Security/SecCmsEnvelopedData.h>
#include <Security/SecCmsMessage.h>
#include <Security/SecCmsRecipientInfo.h>
#include <Security/SecCmsSignedData.h>
#include <Security/SecCmsSignerInfo.h>
#include <Security/SecCmsContentInfo.h>
#include <Security/SecCmsDigestContext.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecKeychainItemPriv.h>
#include <Security/SecCertificate.h>
#include <Security/SecSMIME.h>
#include <Security/oidsattr.h>
#include <Security/SecAsn1Coder.h>
#include <Security/secasn1t.h>
#include <Security/SecAsn1Templates.h>
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);
}
typedef enum {
CTO_Sign,
CTO_Envelop,
CTO_SignEnvelop,
CTO_Parse
} CT_Op;
typedef enum {
CTV_None,
CTV_Sign,
CTV_Envelop,
CTV_SignEnvelop
} CT_Vfy;
#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, }
};
static OSStatus ContentInfoContent(
const unsigned char *contentInfo,
unsigned contentInfoLen,
unsigned char **content,
unsigned *contentLen)
{
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;
}
static OSStatus findCert(
const char *emailAddress,
CFTypeRef kcArArray, 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) {
cssmPerror("SecTrustEvaluate", ortn);
return;
}
switch(secTrustResult) {
case kSecTrustResultUnspecified:
case kSecTrustResultProceed:
if(!quiet) {
fprintf(stderr, "Successful\n");
}
return;
case kSecTrustResultDeny:
case kSecTrustResultConfirm:
printf("Not trusted per user-specified Trust level\n");
return;
default:
{
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:
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;
}
}
}
}
static OSStatus parseSignedData(
SecCmsSignedDataRef signedData,
SecArenaPoolRef arena,
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<numSigners; dex++) {
if(!quiet) {
fprintf(stderr, " signer %d :\n", dex);
fprintf(stderr, " vfy status : ");
}
Boolean b = SecCmsSignedDataHasDigests(signedData);
if(b) {
if(detachedData != NULL) {
fprintf(stderr, "<provided detachedContent, but msg has digests> ");
}
}
else if(detachedData != NULL) {
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, "<Msg has no digest: need detachedContent> ");
}
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, "<<SecCmsSignerInfoGetSignerCommonName returned NULL)>>\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, unsigned *outDataLen) {
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);
}
OidParser oidParser;
for(int dex=0; dex<numContentInfos; dex++) {
SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsMsg, dex);
if(!quiet) {
printf(" Content Info %d :\n", dex);
CSSM_OID *typeOid = SecCmsContentInfoGetContentTypeOID(ci);
printf(" OID Tag : ");
if(typeOid == NULL) {
printf("***NONE FOUND***]n");
}
else if(typeOid->Length == 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: case CTV_Sign: 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;
}
return ourRtn;
}
#if 1
static OSStatus encodeCms(
SecCmsMessageRef cmsMsg,
const unsigned char *inData, unsigned inDataLen,
unsigned char **outData, unsigned *outDataLen) {
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
static OSStatus encodeCms(
SecCmsMessageRef cmsMsg,
const unsigned char *inData, unsigned inDataLen,
unsigned char **outData, unsigned *outDataLen) {
SecArenaPoolRef arena = NULL;
SecArenaPoolCreate(1024, &arena);
SecCmsEncoderRef cmsEnc = NULL;
CSSM_DATA output = { 0, NULL };
OSStatus ortn;
ortn = SecCmsEncoderCreate(cmsMsg,
NULL, NULL, &output, arena, NULL, NULL, NULL, NULL, NULL, NULL, &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;
}
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, unsigned char **outData, unsigned *outDataLen) {
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;
}
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 ,
detachedContent,
eContentType);
if(ortn) {
cssmPerror("SecCmsContentInfoSetContentData", ortn);
goto errOut;
}
}
else {
ortn = SecCmsContentInfoSetContentData(cmsMsg, contentInfo, NULL ,
detachedContent);
if(ortn) {
cssmPerror("SecCmsContentInfoSetContentData", ortn);
goto errOut;
}
}
signerInfo = SecCmsSignerInfoCreate(cmsMsg, signerId, SEC_OID_SHA1);
if (signerInfo == NULL) {
fprintf(stderr, "***Error on SecCmsSignerInfoCreate\n");
ortn = -1;
goto errOut;
}
ortn = SecCmsSignerInfoIncludeCerts(signerInfo, SecCmsCMCertChain, certUsageEmailSigner);
if(ortn) {
cssmPerror("SecCmsSignerInfoIncludeCerts", ortn);
goto errOut;
}
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;
}
ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen);
errOut:
if(cmsMsg) {
SecCmsMessageDestroy(cmsMsg);
}
if(ourCert) {
CFRelease(ourCert);
}
if(ourKc) {
CFRelease(ourKc);
}
return ortn;
}
static OSStatus doEncrypt(
SecCertificateRef recipCert, const unsigned char *inData,
unsigned inDataLen,
unsigned char **outData, unsigned *outDataLen) {
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;
}
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 , false);
if(ortn) {
cssmPerror("SecCmsContentInfoSetContentData", ortn);
goto errOut;
}
recipientInfo = SecCmsRecipientInfoCreate(cmsMsg, recipCert);
ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
if(ortn) {
cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn);
goto errOut;
}
ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen);
errOut:
if(cmsMsg) {
SecCmsMessageDestroy(cmsMsg);
}
return ortn;
}
static OSStatus doSignEncrypt(
SecCertificateRef recipCert, SecIdentityRef signerId, const CSSM_OID *eContentType, const unsigned char *inData,
unsigned inDataLen,
unsigned char **outData, unsigned *outDataLen) {
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;
ortn = doSign(signerId, inData, inDataLen,
false,
eContentType,
&signedData, &signedDataLen);
if(ortn) {
printf("***Error generating inner signedData. Aborting.\n");
return ortn;
}
unsigned char *signedDataContent = NULL;
unsigned signedDataContentLen = 0;
ortn = ContentInfoContent(signedData, signedDataLen, &signedDataContent, &signedDataContentLen);
if(ortn) {
goto errOut;
}
ortn = SecSMIMEFindBulkAlgForRecipients(allCerts, &algorithmTag, &keySize);
if(ortn) {
cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn);
return ortn;
}
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 = SecCmsContentInfoSetContentOther(cmsMsg, contentInfo,
NULL ,
false,
&CSSMOID_PKCS7_SignedData);
if(ortn) {
cssmPerror("SecCmsContentInfoSetContentData", ortn);
goto errOut;
}
recipientInfo = SecCmsRecipientInfoCreate(cmsMsg, recipCert);
ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
if(ortn) {
cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn);
goto errOut;
}
ortn = encodeCms(cmsMsg, signedDataContent, signedDataContentLen, outData, outDataLen);
errOut:
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;
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) {
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) {
CFArrayRef array = sslKcRefToCertArray(kcRef, CSSM_FALSE, CSSM_FALSE,
NULL, 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);
ortn = -1;
}
errOut:
return ortn;
}