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 <security_cdsa_utils/cuFileIo.h>
12 #include <utilLib/common.h>
13 #include <security_cdsa_utils/cuFileIo.h>
14 #include <security_cdsa_utils/cuPrintCert.h>
15 #include <clAppUtils/identPicker.h>
16 #include <clAppUtils/sslAppUtils.h>
17 #include <security_cdsa_utils/cuOidParser.h>
18 #include <CoreFoundation/CoreFoundation.h>
19 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
20 #include <Security/SecCmsEncoder.h>
21 #include <Security/SecCmsDecoder.h>
22 #include <Security/SecCmsEncryptedData.h>
23 #include <Security/SecCmsEnvelopedData.h>
24 #include <Security/SecCmsMessage.h>
25 #include <Security/SecCmsRecipientInfo.h>
26 #include <Security/SecCmsSignedData.h>
27 #include <Security/SecCmsSignerInfo.h>
28 #include <Security/SecCmsContentInfo.h>
29 #include <Security/SecCmsDigestContext.h>
30 #include <Security/SecTrustPriv.h>
31 #include <Security/SecKeychainItemPriv.h>
32 #include <Security/SecCertificate.h>
33 #include <Security/SecSMIME.h>
34 #include <Security/oidsattr.h>
35 #include <Security/SecAsn1Coder.h>
36 #include <Security/secasn1t.h>
37 #include <Security/SecAsn1Templates.h>
39 static void usage(char **argv
)
41 printf("Usage: %s cmd [option ...]\n", argv
[0]);
42 printf("cmd values:\n");
43 printf(" sign -- create signedData\n");
44 printf(" envel -- create envelopedData\n");
45 printf(" signEnv -- create nested EnvelopedData(signedData(data))\n");
46 printf(" parse -- parse a CMS message file\n");
48 printf(" -i infile\n");
49 printf(" -o outfile\n");
50 printf(" -k keychain -- Keychain to search for certs\n");
51 printf(" -p -- Use identity picker\n");
52 printf(" -r recipient -- specify recipient of enveloped data\n");
53 printf(" -c -- parse signer cert\n");
54 printf(" -v sign|encr -- verify message is signed/encrypted\n");
55 printf(" -e eContentType -- a(uthData)|r(keyData)\n");
56 printf(" -d detached -- infile contains detached content (sign only)\n");
57 printf(" -D detachedContent -- detached content (parse only)\n");
58 printf(" -q -- quiet\n");
78 /* additional OIDS to specify as eContentType */
79 #define OID_PKINIT 0x2B, 6, 1, 5, 2, 3
80 #define OID_PKINIT_LEN 6
82 static const uint8 OID_PKINIT_AUTH_DATA
[] = {OID_PKINIT
, 1};
83 static const uint8 OID_PKINIT_DH_KEY_DATA
[] = {OID_PKINIT
, 2};
84 static const uint8 OID_PKINIT_RKEY_DATA
[] = {OID_PKINIT
, 3};
85 static const uint8 OID_PKINIT_KP_CLIENTAUTH
[] = {OID_PKINIT
, 3};
86 static const uint8 OID_PKINIT_KPKDC
[] = {OID_PKINIT
, 5};
88 static const CSSM_OID CSSMOID_PKINIT_AUTH_DATA
=
89 {OID_PKINIT_LEN
+1, (uint8
*)OID_PKINIT_AUTH_DATA
};
90 static const CSSM_OID CSSMOID_PKINIT_DH_KEY_DATA
=
91 {OID_PKINIT_LEN
+1, (uint8
*)OID_PKINIT_DH_KEY_DATA
};
92 static const CSSM_OID CSSMOID_PKINIT_RKEY_DATA
=
93 {OID_PKINIT_LEN
+1, (uint8
*)OID_PKINIT_RKEY_DATA
};
94 static const CSSM_OID CSSMOID_PKINIT_KP_CLIENTAUTH
=
95 {OID_PKINIT_LEN
+1, (uint8
*)OID_PKINIT_KP_CLIENTAUTH
};
96 static const CSSM_OID CSSMOID_PKINIT_KPKDC
=
97 {OID_PKINIT_LEN
+1, (uint8
*)OID_PKINIT_KPKDC
};
100 CSSM_OID contentType
;
104 const SecAsn1Template SimpleContentInfoTemplate
[] = {
105 { SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(SimpleContentInfo
) },
106 { SEC_ASN1_OBJECT_ID
, offsetof(SimpleContentInfo
, contentType
) },
107 { SEC_ASN1_EXPLICIT
| SEC_ASN1_CONSTRUCTED
| SEC_ASN1_CONTEXT_SPECIFIC
| 0,
108 offsetof(SimpleContentInfo
, content
),
109 kSecAsn1AnyTemplate
},
114 * Obtain the content of a contentInfo, This basically strips off the contentType OID
115 * and returns a mallocd copy of the ASN_ANY content.
117 static OSStatus
ContentInfoContent(
118 const unsigned char *contentInfo
,
119 unsigned contentInfoLen
,
120 unsigned char **content
, /* mallocd and RETURNED */
121 unsigned *contentLen
) /* RETURNED */
123 SecAsn1CoderRef coder
= NULL
;
125 SimpleContentInfo decodedInfo
;
127 ortn
= SecAsn1CoderCreate(&coder
);
131 memset(&decodedInfo
, 0, sizeof(decodedInfo
));
132 ortn
= SecAsn1Decode(coder
, contentInfo
, contentInfoLen
,
133 SimpleContentInfoTemplate
, &decodedInfo
);
137 if(decodedInfo
.content
.Data
== NULL
) {
138 printf("***Error decoding contentInfo: no content\n");
139 ortn
= internalComponentErr
;
142 *content
= (unsigned char *)malloc(decodedInfo
.content
.Length
);
143 memmove(*content
, decodedInfo
.content
.Data
, decodedInfo
.content
.Length
);
144 *contentLen
= decodedInfo
.content
.Length
;
146 SecAsn1CoderRelease(coder
);
151 * Find a cert in specified keychain or keychain list matching specified
152 * email address. We happen to knopw that the email address is stored with the
153 * kSecKeyAlias attribute.
155 static OSStatus
findCert(
156 const char *emailAddress
,
157 CFTypeRef kcArArray
, // kc, array, or even NULL
158 SecCertificateRef
*cert
)
161 SecKeychainSearchRef srch
;
162 SecKeychainAttributeList attrList
;
163 SecKeychainAttribute attr
;
165 attr
.tag
= kSecKeyAlias
;
166 attr
.length
= strlen(emailAddress
);
167 attr
.data
= (void *)emailAddress
;
169 attrList
.attr
= &attr
;
171 ortn
= SecKeychainSearchCreateFromAttributes(kcArArray
,
172 kSecCertificateItemClass
,
176 cssmPerror("SecKeychainSearchCreateFromAttributes", ortn
);
180 ortn
= SecKeychainSearchCopyNext(srch
, (SecKeychainItemRef
*)cert
);
182 printf("***No certs founmd matching recipient %s. Aborting.\n",
190 static void evalSecTrust(
191 SecTrustRef secTrust
,
195 SecTrustResultType secTrustResult
;
197 ortn
= SecTrustEvaluate(secTrust
, &secTrustResult
);
199 /* should never happen */
200 cssmPerror("SecTrustEvaluate", ortn
);
203 switch(secTrustResult
) {
204 case kSecTrustResultUnspecified
:
205 /* cert chain valid, no special UserTrust assignments */
206 case kSecTrustResultProceed
:
207 /* cert chain valid AND user explicitly trusts this */
209 fprintf(stderr
, "Successful\n");
212 case kSecTrustResultDeny
:
213 case kSecTrustResultConfirm
:
215 * Cert chain may well have verified OK, but user has flagged
216 * one of these certs as untrustable.
218 printf("Not trusted per user-specified Trust level\n");
222 /* get low-level TP error */
224 ortn
= SecTrustGetCssmResultCode(secTrust
, &tpStatus
);
226 cssmPerror("SecTrustGetCssmResultCode", ortn
);
230 case CSSMERR_TP_INVALID_ANCHOR_CERT
:
231 fprintf(stderr
, "Untrusted root\n");
233 case CSSMERR_TP_NOT_TRUSTED
:
234 /* no root, not even in implicit SSL roots */
235 fprintf(stderr
, "No root cert found\n");
237 case CSSMERR_TP_CERT_EXPIRED
:
238 fprintf(stderr
, "Expired cert\n");
240 case CSSMERR_TP_CERT_NOT_VALID_YET
:
241 fprintf(stderr
, "Cert not valid yet\n");
244 printf("Other cert failure: ");
245 cssmPerror("", tpStatus
);
249 } /* SecTrustEvaluate error */
252 static OSStatus
parseSignedData(
253 SecCmsSignedDataRef signedData
,
254 SecArenaPoolRef arena
, /* used for detached content only */
255 const unsigned char *detachedData
,
256 unsigned detachedDataLen
,
259 bool parseSignerCert
)
262 b
= SecCmsSignedDataHasDigests(signedData
);
264 printf(" has digests : %s\n", b
? "true" : "false");
267 SecTrustRef secTrust
= NULL
;
269 SecPolicyRef policy
= NULL
;
270 SecPolicySearchRef policySearch
= NULL
;
272 ortn
= SecPolicySearchCreate(CSSM_CERT_X_509v3
,
273 &CSSMOID_APPLE_X509_BASIC
,
277 cssmPerror("SecPolicySearchCreate", ortn
);
280 ortn
= SecPolicySearchCopyNext(policySearch
, &policy
);
282 cssmPerror("SecPolicySearchCopyNext", ortn
);
286 int numSigners
= SecCmsSignedDataSignerInfoCount(signedData
);
288 printf(" num signers : %d\n", numSigners
);
290 for(int dex
=0; dex
<numSigners
; dex
++) {
292 fprintf(stderr
, " signer %d :\n", dex
);
293 fprintf(stderr
, " vfy status : ");
295 Boolean b
= SecCmsSignedDataHasDigests(signedData
);
297 if(detachedData
!= NULL
) {
298 fprintf(stderr
, "<provided detachedContent, but msg has digests> ");
299 /* FIXME - does this make sense? Error? */
302 else if(detachedData
!= NULL
) {
303 /* digest the detached content */
304 SECAlgorithmID
**digestAlgorithms
= SecCmsSignedDataGetDigestAlgs(signedData
);
305 SecCmsDigestContextRef digcx
= SecCmsDigestContextStartMultiple(digestAlgorithms
);
306 CSSM_DATA
**digests
= NULL
;
308 SecCmsDigestContextUpdate(digcx
, detachedData
, detachedDataLen
);
309 ortn
= SecCmsDigestContextFinishMultiple(digcx
, arena
, &digests
);
311 fprintf(stderr
, "SecCmsDigestContextFinishMultiple() returned %d\n", (int)ortn
);
314 SecCmsSignedDataSetDigests(signedData
, digestAlgorithms
, digests
);
318 fprintf(stderr
, "<Msg has no digest: need detachedContent> ");
320 ortn
= SecCmsSignedDataVerifySignerInfo(signedData
, dex
, NULL
,
323 fprintf(stderr
, "vfSignerInfo() returned %d\n", (int)ortn
);
324 fprintf(stderr
, " vfy status : ");
326 if(secTrust
== NULL
) {
327 fprintf(stderr
, "***NO SecTrust available!\n");
330 evalSecTrust(secTrust
, quiet
);
333 SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, dex
);
334 CFStringRef emailAddrs
= SecCmsSignerInfoGetSignerCommonName(signerInfo
);
337 fprintf(stderr
, " signer : ");
339 if(emailAddrs
== NULL
) {
340 fprintf(stderr
, "<<SecCmsSignerInfoGetSignerCommonName returned NULL)>>\n");
343 if(!CFStringGetCString(emailAddrs
, emailStr
, 1000, kCFStringEncodingASCII
)) {
344 fprintf(stderr
, "*** Error converting email address to C string\n");
348 fprintf(stderr
, "%s\n", emailStr
);
351 if(parseSignerCert
) {
352 SecCertificateRef signer
;
353 signer
= SecCmsSignerInfoGetSigningCertificate(signerInfo
, NULL
);
356 ortn
= SecCertificateGetData(signer
, &certData
);
358 fprintf(stderr
, "***Error getting signing cert data***\n");
359 cssmPerror("SecCertificateGetData", ortn
);
362 printf("========== Signer Cert==========\n\n");
363 printCert(certData
.Data
, certData
.Length
, CSSM_FALSE
);
364 printf("========== End Signer Cert==========\n\n");
368 fprintf(stderr
, "***Error getting signing cert ***\n");
375 static OSStatus
doParse(
376 const unsigned char *data
,
378 const unsigned char *detachedData
,
379 unsigned detachedDataLen
,
381 bool parseSignerCert
,
383 unsigned char **outData
, // mallocd and RETURNED
384 unsigned *outDataLen
) // RETURNED
386 if((data
== NULL
) || (dataLen
== 0)) {
387 fprintf(stderr
, "***Parse requires input file. Aborting.\n");
391 SecArenaPoolRef arena
= NULL
;
392 SecArenaPoolCreate(1024, &arena
);
393 SecCmsMessageRef cmsMsg
= NULL
;
394 SecCmsDecoderRef decoder
;
396 OSStatus ourRtn
= noErr
;
397 bool foundOneSigned
= false;
398 bool foundOneEnveloped
= false;
400 ortn
= SecCmsDecoderCreate(arena
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &decoder
);
402 cssmPerror("SecCmsDecoderCreate", ortn
);
405 ortn
= SecCmsDecoderUpdate(decoder
, data
, dataLen
);
407 cssmPerror("SecCmsDecoderUpdate", ortn
);
410 ortn
= SecCmsDecoderFinish(decoder
, &cmsMsg
);
412 cssmPerror("SecCmsDecoderFinish", ortn
);
416 Boolean b
= SecCmsMessageIsSigned(cmsMsg
);
422 fprintf(stderr
, "***Expected SignedData, but !SecCmsMessageIsSigned()\n");
426 case CTV_SignEnvelop
:
428 fprintf(stderr
, "***Expected Signed&Enveloped, but !SecCmsMessageIsSigned()\n");
434 fprintf(stderr
, "***Expected EnvelopedData, but SecCmsMessageIsSigned() "
440 int numContentInfos
= SecCmsMessageContentLevelCount(cmsMsg
);
442 fprintf(stderr
, "=== CMS message info ===\n");
443 fprintf(stderr
, " Signed : %s\n", b
? "true" : "false");
444 b
= SecCmsMessageIsEncrypted(cmsMsg
);
445 fprintf(stderr
, " Encrypted : %s\n", b
? "true" : "false");
446 b
= SecCmsMessageContainsCertsOrCrls(cmsMsg
);
447 fprintf(stderr
, " certs/crls : %s\n", b
? "present" : "not present");
448 fprintf(stderr
, " Num ContentInfos : %d\n", numContentInfos
);
451 /* FIXME needs work for CTV_SignEnvelop */
453 for(int dex
=0; dex
<numContentInfos
; dex
++) {
454 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsMsg
, dex
);
456 /* can't use stderr - oidparser is fixed w/stdout */
457 printf(" Content Info %d :\n", dex
);
458 CSSM_OID
*typeOid
= SecCmsContentInfoGetContentTypeOID(ci
);
459 printf(" OID Tag : ");
460 if(typeOid
== NULL
) {
461 printf("***NONE FOUND***]n");
463 else if(typeOid
->Length
== 0) {
464 printf("***EMPTY***\n");
467 char str
[OID_PARSER_STRING_SIZE
];
468 oidParser
.oidParse(typeOid
->Data
, typeOid
->Length
, str
);
472 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
474 case SEC_OID_PKCS7_SIGNED_DATA
:
477 case CTV_None
: // caller doesn't care
478 case CTV_Sign
: // got what we wanted
481 fprintf(stderr
, "***Expected EnvelopedData, got SignedData\n");
484 case CTV_SignEnvelop
:
485 printf("CTV_SignEnvelop code on demand\n");
488 foundOneSigned
= true;
489 SecCmsSignedDataRef sd
=
490 (SecCmsSignedDataRef
) SecCmsContentInfoGetContent(ci
);
491 parseSignedData(sd
, arena
,
492 detachedData
, detachedDataLen
,
493 vfyOp
, quiet
, parseSignerCert
);
496 case SEC_OID_PKCS7_DATA
:
499 case SEC_OID_PKCS7_ENVELOPED_DATA
:
500 foundOneEnveloped
= true;
501 if(vfyOp
== CTV_Sign
) {
502 fprintf(stderr
, "***Expected SignedData, EnvelopedData\n");
506 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
511 fprintf(stderr
, "***Expected SignedData, got EncryptedData\n");
515 fprintf(stderr
, "***Expected EnvelopedData, got EncryptedData\n");
518 case CTV_SignEnvelop
:
519 printf("CTV_SignEnvelop code on demand\n");
524 fprintf(stderr
, " other content type TBD\n");
528 CSSM_DATA_PTR odata
= SecCmsMessageGetContent(cmsMsg
);
530 fprintf(stderr
, "***No inner content available\n");
533 *outData
= (unsigned char *)malloc(odata
->Length
);
534 memmove(*outData
, odata
->Data
, odata
->Length
);
535 *outDataLen
= odata
->Length
;
539 SecArenaPoolFree(arena
, false);
545 if(!foundOneSigned
) {
546 fprintf(stderr
, "Expected signed, never saw a SignedData\n");
551 if(!foundOneEnveloped
) {
552 fprintf(stderr
, "Expected enveloped, never saw an EnvelopedData\n");
556 case CTV_SignEnvelop
:
557 if(!foundOneSigned
) {
558 fprintf(stderr
, "Expected signed, never saw a SignedData\n");
561 if(!foundOneEnveloped
) {
562 fprintf(stderr
, "Expected enveloped, never saw an EnvelopedData\n");
567 /* free decoder? cmsMsg? */
572 * Common encode routine.
575 /* the simple way, when 3655861 is fixed */
576 static OSStatus
encodeCms(
577 SecCmsMessageRef cmsMsg
,
578 const unsigned char *inData
, // add in this
580 unsigned char **outData
, // mallocd and RETURNED
581 unsigned *outDataLen
) // RETURNED
583 SecArenaPoolRef arena
= NULL
;
584 SecArenaPoolCreate(1024, &arena
);
585 CSSM_DATA cdataIn
= {inDataLen
, (uint8
*)inData
};
586 CSSM_DATA cdataOut
= {0, NULL
};
588 OSStatus ortn
= SecCmsMessageEncode(cmsMsg
, &cdataIn
, arena
, &cdataOut
);
589 if((ortn
== noErr
) && (cdataOut
.Length
!= 0)) {
590 *outData
= (unsigned char *)malloc(cdataOut
.Length
);
591 memmove(*outData
, cdataOut
.Data
, cdataOut
.Length
);
592 *outDataLen
= cdataOut
.Length
;
595 cssmPerror("SecCmsMessageEncode", ortn
);
599 SecArenaPoolFree(arena
, false);
605 /* the hard way back when SecCmsMessageEncode() didn't work */
606 static OSStatus
encodeCms(
607 SecCmsMessageRef cmsMsg
,
608 const unsigned char *inData
, // add in this
610 unsigned char **outData
, // mallocd and RETURNED
611 unsigned *outDataLen
) // RETURNED
613 SecArenaPoolRef arena
= NULL
;
614 SecArenaPoolCreate(1024, &arena
);
615 SecCmsEncoderRef cmsEnc
= NULL
;
616 CSSM_DATA output
= { 0, NULL
};
619 ortn
= SecCmsEncoderCreate(cmsMsg
,
620 NULL
, NULL
, // no callback
621 &output
, arena
, // data goes here
622 NULL
, NULL
, // no password callback (right?)
623 NULL
, NULL
, // decrypt key callback
624 NULL
, NULL
, // detached digests
627 cssmPerror("SecKeychainItemCopyKeychain", ortn
);
630 ortn
= SecCmsEncoderUpdate(cmsEnc
, (char *)inData
, inDataLen
);
632 cssmPerror("SecCmsEncoderUpdate", ortn
);
635 ortn
= SecCmsEncoderFinish(cmsEnc
);
637 cssmPerror("SecCMsEncoderFinish", ortn
);
641 /* Did we get any data? */
643 *outData
= (unsigned char *)malloc(output
.Length
);
644 memmove(*outData
, output
.Data
, output
.Length
);
645 *outDataLen
= output
.Length
;
653 SecArenaPoolFree(arena
, false);
660 static OSStatus
doSign(
661 SecIdentityRef signerId
,
662 const unsigned char *inData
,
664 bool detachedContent
,
665 const CSSM_OID
*eContentType
, // OPTIONAL
666 unsigned char **outData
, // mallocd and RETURNED
667 unsigned *outDataLen
) // RETURNED
669 if((inData
== NULL
) || (inDataLen
== 0) || (outData
== NULL
)) {
670 fprintf(stderr
, "***Sign requires input file. Aborting.\n");
673 if(signerId
== NULL
) {
674 fprintf(stderr
, "***Sign requires a signing identity. Aborting.\n");
678 SecCmsMessageRef cmsMsg
= NULL
;
679 SecCmsContentInfoRef contentInfo
= NULL
;
680 SecCmsSignedDataRef signedData
= NULL
;
681 SecCertificateRef ourCert
= NULL
;
682 SecCmsSignerInfoRef signerInfo
;
684 SecKeychainRef ourKc
= NULL
;
686 ortn
= SecIdentityCopyCertificate(signerId
, &ourCert
);
688 cssmPerror("SecIdentityCopyCertificate", ortn
);
691 ortn
= SecKeychainItemCopyKeychain((SecKeychainItemRef
)ourCert
, &ourKc
);
693 cssmPerror("SecKeychainItemCopyKeychain", ortn
);
697 // build chain of objects: message->signedData->data
698 cmsMsg
= SecCmsMessageCreate(NULL
);
700 fprintf(stderr
, "***Error creating SecCmsMessageRef\n");
704 signedData
= SecCmsSignedDataCreate(cmsMsg
);
705 if(signedData
== NULL
) {
706 printf("***Error creating SecCmsSignedDataRef\n");
710 contentInfo
= SecCmsMessageGetContentInfo(cmsMsg
);
711 ortn
= SecCmsContentInfoSetContentSignedData(cmsMsg
, contentInfo
, signedData
);
713 cssmPerror("SecCmsContentInfoSetContentSignedData", ortn
);
716 contentInfo
= SecCmsSignedDataGetContentInfo(signedData
);
717 if(eContentType
!= NULL
) {
718 ortn
= SecCmsContentInfoSetContentOther(cmsMsg
, contentInfo
,
723 cssmPerror("SecCmsContentInfoSetContentData", ortn
);
728 ortn
= SecCmsContentInfoSetContentData(cmsMsg
, contentInfo
, NULL
/* data */,
731 cssmPerror("SecCmsContentInfoSetContentData", ortn
);
737 * create & attach signer information
739 signerInfo
= SecCmsSignerInfoCreate(cmsMsg
, signerId
, SEC_OID_SHA1
);
740 if (signerInfo
== NULL
) {
741 fprintf(stderr
, "***Error on SecCmsSignerInfoCreate\n");
745 /* we want the cert chain included for this one */
746 /* FIXME - what's the significance of the usage? */
747 ortn
= SecCmsSignerInfoIncludeCerts(signerInfo
, SecCmsCMCertChain
, certUsageEmailSigner
);
749 cssmPerror("SecCmsSignerInfoIncludeCerts", ortn
);
753 /* other options go here - signing time, etc. */
755 ortn
= SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo
, ourCert
, ourKc
);
757 cssmPerror("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn
);
760 ortn
= SecCmsSignedDataAddCertificate(signedData
, ourCert
);
762 cssmPerror("SecCmsSignedDataAddCertificate", ortn
);
766 ortn
= SecCmsSignedDataAddSignerInfo(signedData
, signerInfo
);
768 cssmPerror("SecCmsSignedDataAddSignerInfo", ortn
);
773 ortn
= encodeCms(cmsMsg
, inData
, inDataLen
, outData
, outDataLen
);
777 SecCmsMessageDestroy(cmsMsg
);
788 static OSStatus
doEncrypt(
789 SecCertificateRef recipCert
, // eventually more than one
790 const unsigned char *inData
,
792 unsigned char **outData
, // mallocd and RETURNED
793 unsigned *outDataLen
) // RETURNED
795 if((inData
== NULL
) || (inDataLen
== 0) || (outData
== NULL
)) {
796 fprintf(stderr
, "***Encrypt requires input file. Aborting.\n");
799 if(recipCert
== NULL
) {
800 fprintf(stderr
, "***Encrypt requires a recipient certificate. Aborting.\n");
804 SecCmsMessageRef cmsMsg
= NULL
;
805 SecCmsContentInfoRef contentInfo
= NULL
;
806 SecCmsEnvelopedDataRef envelopedData
= NULL
;
807 SecCmsRecipientInfoRef recipientInfo
= NULL
;
809 SecCertificateRef allCerts
[2] = { recipCert
, NULL
};
811 SECOidTag algorithmTag
;
814 ortn
= SecSMIMEFindBulkAlgForRecipients(allCerts
, &algorithmTag
, &keySize
);
816 cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn
);
820 // build chain of objects: message->envelopedData->data
821 cmsMsg
= SecCmsMessageCreate(NULL
);
823 fprintf(stderr
, "***Error creating SecCmsMessageRef\n");
827 envelopedData
= SecCmsEnvelopedDataCreate(cmsMsg
, algorithmTag
, keySize
);
828 if(envelopedData
== NULL
) {
829 fprintf(stderr
, "***Error creating SecCmsEnvelopedDataRef\n");
833 contentInfo
= SecCmsMessageGetContentInfo(cmsMsg
);
834 ortn
= SecCmsContentInfoSetContentEnvelopedData(cmsMsg
, contentInfo
, envelopedData
);
836 cssmPerror("SecCmsContentInfoSetContentEnvelopedData", ortn
);
839 contentInfo
= SecCmsEnvelopedDataGetContentInfo(envelopedData
);
840 ortn
= SecCmsContentInfoSetContentData(cmsMsg
, contentInfo
, NULL
/* data */, false);
842 cssmPerror("SecCmsContentInfoSetContentData", ortn
);
847 * create & attach recipient information
849 recipientInfo
= SecCmsRecipientInfoCreate(cmsMsg
, recipCert
);
850 ortn
= SecCmsEnvelopedDataAddRecipient(envelopedData
, recipientInfo
);
852 cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn
);
858 ortn
= encodeCms(cmsMsg
, inData
, inDataLen
, outData
, outDataLen
);
862 SecCmsMessageDestroy(cmsMsg
);
867 /* create nested message: msg = EnvelopedData(SignedData(inData)) */
868 static OSStatus
doSignEncrypt(
869 SecCertificateRef recipCert
, // encryption recipient
870 SecIdentityRef signerId
, // signer
871 const CSSM_OID
*eContentType
, // OPTIONAL - for signedData
872 const unsigned char *inData
,
874 unsigned char **outData
, // mallocd and RETURNED
875 unsigned *outDataLen
) // RETURNED
877 if((inData
== NULL
) || (inDataLen
== 0) || (outData
== NULL
)) {
878 fprintf(stderr
, "***Sign/Encrypt requires input file. Aborting.\n");
881 if(recipCert
== NULL
) {
882 fprintf(stderr
, "***Sign/Encrypt requires a recipient certificate. Aborting.\n");
885 if(signerId
== NULL
) {
886 fprintf(stderr
, "***Sign/Encrypt requires a signer Identity. Aborting.\n");
891 unsigned char *signedData
= NULL
;
892 unsigned signedDataLen
= 0;
893 SecCmsMessageRef cmsMsg
= NULL
;
894 SecCmsContentInfoRef contentInfo
= NULL
;
895 SecCmsEnvelopedDataRef envelopedData
= NULL
;
896 SecCmsRecipientInfoRef recipientInfo
= NULL
;
897 SecCertificateRef allCerts
[2] = { recipCert
, NULL
};
898 SECOidTag algorithmTag
;
901 /* first get a SignedData */
902 ortn
= doSign(signerId
, inData
, inDataLen
,
903 false, /* can't do detached content here */
905 &signedData
, &signedDataLen
);
907 printf("***Error generating inner signedData. Aborting.\n");
911 /* extract just the content - don't need the whole ContentINfo */
912 unsigned char *signedDataContent
= NULL
;
913 unsigned signedDataContentLen
= 0;
914 ortn
= ContentInfoContent(signedData
, signedDataLen
, &signedDataContent
, &signedDataContentLen
);
919 /* now wrap that in an EnvelopedData */
920 ortn
= SecSMIMEFindBulkAlgForRecipients(allCerts
, &algorithmTag
, &keySize
);
922 cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn
);
926 // build chain of objects: message->envelopedData->data
927 cmsMsg
= SecCmsMessageCreate(NULL
);
929 fprintf(stderr
, "***Error creating SecCmsMessageRef\n");
933 envelopedData
= SecCmsEnvelopedDataCreate(cmsMsg
, algorithmTag
, keySize
);
934 if(envelopedData
== NULL
) {
935 fprintf(stderr
, "***Error creating SecCmsEnvelopedDataRef\n");
939 contentInfo
= SecCmsMessageGetContentInfo(cmsMsg
);
940 ortn
= SecCmsContentInfoSetContentEnvelopedData(cmsMsg
, contentInfo
, envelopedData
);
942 cssmPerror("SecCmsContentInfoSetContentEnvelopedData", ortn
);
945 contentInfo
= SecCmsEnvelopedDataGetContentInfo(envelopedData
);
947 /* here's the difference: we override the 'data' content with a SignedData type,
948 * but we fool the smime lib into thinking it's a plain old data so it doesn't try
949 * to encode the SignedData */
950 ortn
= SecCmsContentInfoSetContentOther(cmsMsg
, contentInfo
,
953 &CSSMOID_PKCS7_SignedData
);
955 cssmPerror("SecCmsContentInfoSetContentData", ortn
);
960 * create & attach recipient information
962 recipientInfo
= SecCmsRecipientInfoCreate(cmsMsg
, recipCert
);
963 ortn
= SecCmsEnvelopedDataAddRecipient(envelopedData
, recipientInfo
);
965 cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn
);
971 ortn
= encodeCms(cmsMsg
, signedDataContent
, signedDataContentLen
, outData
, outDataLen
);
975 SecCmsMessageDestroy(cmsMsg
);
980 if(signedDataContent
) {
981 free(signedDataContent
);
986 int main(int argc
, char **argv
)
994 if(!strcmp(argv
[1], "sign")) {
998 else if(!strcmp(argv
[1], "envel")) {
1001 else if(!strcmp(argv
[1], "signEnv")) {
1002 op
= CTO_SignEnvelop
;
1005 else if(!strcmp(argv
[1], "parse")) {
1009 fprintf(stderr
, "***Unrecognized cmd.\n");
1014 extern char *optarg
;
1018 const char *keychainName
= NULL
;
1019 char *inFileName
= NULL
;
1020 char *outFileName
= NULL
;
1021 bool detachedContent
= false;
1022 char *detachedFile
= NULL
;
1023 bool useIdPicker
= false;
1024 char *recipient
= NULL
;
1026 bool parseSignerCert
= false;
1027 CT_Vfy vfyOp
= CTV_None
;
1028 const CSSM_OID
*eContentType
= NULL
;
1031 while ((arg
= getopt(argc
, argv
, "i:o:k:pr:e:dD:qcv:")) != -1) {
1034 inFileName
= optarg
;
1037 outFileName
= optarg
;
1040 keychainName
= optarg
;
1049 parseSignerCert
= true;
1052 if(!strcmp(optarg
, "sign")) {
1055 else if(!strcmp(optarg
, "encr")) {
1056 vfyOp
= CTV_Envelop
;
1058 else if(!strcmp(optarg
, "signEnv")) {
1059 vfyOp
= CTV_SignEnvelop
;
1068 eContentType
= &CSSMOID_PKINIT_AUTH_DATA
;
1071 eContentType
= &CSSMOID_PKINIT_RKEY_DATA
;
1078 if(op
!= CTO_Sign
) {
1079 printf("-d only valid for op sign\n");
1082 detachedContent
= true;
1085 if(op
!= CTO_Parse
) {
1086 printf("-D only valid for op sign\n");
1089 detachedFile
= optarg
;
1099 if(optind
!= argc
) {
1100 /* getopt does not return '?' */
1104 SecIdentityRef idRef
= NULL
;
1105 SecKeychainRef kcRef
= NULL
;
1106 SecCertificateRef recipientCert
= NULL
;
1107 unsigned char *inData
= NULL
;
1108 unsigned inDataLen
= 0;
1109 unsigned char *outData
= NULL
;
1110 unsigned outDataLen
= 0;
1111 unsigned char *detachedData
= NULL
;
1112 unsigned detachedDataLen
= 0;
1116 if(readFile(inFileName
, &inData
, &inDataLen
)) {
1117 fprintf(stderr
, "***Error reading infile %s. Aborting.\n", inFileName
);
1122 if(readFile(detachedFile
, &detachedData
, &detachedDataLen
)) {
1123 fprintf(stderr
, "***Error reading detachedFile %s. Aborting.\n", detachedFile
);
1128 ortn
= SecKeychainOpen(keychainName
, &kcRef
);
1130 cssmPerror("SecKeychainOpen", ortn
);
1135 ortn
= sslSimpleIdentPicker(kcRef
, &idRef
);
1137 fprintf(stderr
, "***Error obtaining identity via picker. Aborting.\n");
1142 /* use first identity in specified keychain */
1143 CFArrayRef array
= sslKcRefToCertArray(kcRef
, CSSM_FALSE
, CSSM_FALSE
,
1144 NULL
, // no verify policy
1147 fprintf(stderr
, "***Error finding a signing cert. Aborting.\n");
1150 idRef
= (SecIdentityRef
)CFArrayGetValueAtIndex(array
, 0);
1152 fprintf(stderr
, "***No identities found. Aborting.\n");
1159 ortn
= findCert(recipient
, kcRef
, &recipientCert
);
1167 ortn
= doSign(idRef
, inData
, inDataLen
,
1168 detachedContent
, eContentType
,
1169 &outData
, &outDataLen
);
1172 if(recipientCert
== NULL
) {
1174 printf("***Need a recipient or an identity to encrypt\n");
1177 ortn
= SecIdentityCopyCertificate(idRef
, &recipientCert
);
1179 cssmPerror("SecIdentityCopyCertificate", ortn
);
1183 ortn
= doEncrypt(recipientCert
, inData
, inDataLen
, &outData
, &outDataLen
);
1185 case CTO_SignEnvelop
:
1186 ortn
= doSignEncrypt(recipientCert
, idRef
, eContentType
,
1187 inData
, inDataLen
, &outData
, &outDataLen
);
1190 ortn
= doParse(inData
, inDataLen
,
1191 detachedData
, detachedDataLen
,
1192 vfyOp
, parseSignerCert
, quiet
,
1193 &outData
, &outDataLen
);
1199 if(outData
&& outFileName
) {
1200 if(writeFile(outFileName
, outData
, outDataLen
)) {
1201 fprintf(stderr
, "***Error writing to %s.\n", outFileName
);
1206 fprintf(stderr
, "...wrote %u bytes to %s.\n", outDataLen
, outFileName
);
1211 fprintf(stderr
, "...generated %u bytes but no place to write it.\n", outDataLen
);
1213 else if(outFileName
) {
1214 fprintf(stderr
, "...nothing to write to file %s.\n", outFileName
);
1215 /* assume this is an error, caller wanted something */