1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
* cmsutil -- A command to work with CMS data
8
* $Id: cmsutil.c,v 1.55 2012/03/20 14:47:20 gerv%gerv.net Exp $
35
char *progName = NULL;
36
static int cms_verbose = 0;
37
static secuPWData pwdata = { PW_NONE, 0 };
38
static PK11PasswordFunc pwcb = NULL;
39
static void *pwcb_arg = NULL;
42
/* XXX stolen from cmsarray.c
43
* nss_CMSArray_Count - count number of elements in array
46
nss_CMSArray_Count(void **array)
51
while (*array++ != NULL)
57
DigestFile(PLArenaPool *poolp, SECItem ***digests, SECItem *input,
58
SECAlgorithmID **algids)
60
NSSCMSDigestContext *digcx;
63
digcx = NSS_CMSDigestContext_StartMultiple(algids);
67
NSS_CMSDigestContext_Update(digcx, input->data, input->len);
69
rv = NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests);
78
"Usage: %s [-C|-D|-E|-O|-S] [<options>] [-d dbdir] [-u certusage]\n"
79
" -C create a CMS encrypted data message\n"
80
" -D decode a CMS message\n"
81
" -b decode a batch of files named in infile\n"
82
" -c content use this detached content\n"
83
" -n suppress output of content\n"
84
" -h num display num levels of CMS message info as email headers\n"
85
" -k keep decoded encryption certs in perm cert db\n"
86
" -E create a CMS enveloped data message\n"
87
" -r id,... create envelope for these recipients,\n"
88
" where id can be a certificate nickname or email address\n"
89
" -S create a CMS signed data message\n"
90
" -G include a signing time attribute\n"
91
" -H hash use hash (default:SHA1)\n"
92
" -N nick use certificate named \"nick\" for signing\n"
93
" -P include a SMIMECapabilities attribute\n"
94
" -T do not include content in CMS message\n"
95
" -Y nick include a EncryptionKeyPreference attribute with cert\n"
96
" (use \"NONE\" to omit)\n"
97
" -O create a CMS signed message containing only certificates\n"
99
" -d dbdir key/cert database directory (default: ~/.netscape)\n"
100
" -e envelope enveloped data message in this file is used for bulk key\n"
101
" -i infile use infile as source of data (default: stdin)\n"
102
" -o outfile use outfile as destination of data (default: stdout)\n"
103
" -p password use password as key db password (default: prompt)\n"
104
" -f pwfile use password file to set password on all PKCS#11 tokens)\n"
105
" -u certusage set type of certificate usage (default: certUsageEmailSigner)\n"
106
" -v print debugging information\n"
108
"Cert usage codes:\n",
110
fprintf(stderr, "%-25s 0 - certUsageSSLClient\n", " ");
111
fprintf(stderr, "%-25s 1 - certUsageSSLServer\n", " ");
112
fprintf(stderr, "%-25s 2 - certUsageSSLServerWithStepUp\n", " ");
113
fprintf(stderr, "%-25s 3 - certUsageSSLCA\n", " ");
114
fprintf(stderr, "%-25s 4 - certUsageEmailSigner\n", " ");
115
fprintf(stderr, "%-25s 5 - certUsageEmailRecipient\n", " ");
116
fprintf(stderr, "%-25s 6 - certUsageObjectSigner\n", " ");
117
fprintf(stderr, "%-25s 7 - certUsageUserCertImport\n", " ");
118
fprintf(stderr, "%-25s 8 - certUsageVerifyCA\n", " ");
119
fprintf(stderr, "%-25s 9 - certUsageProtectedObjectSigner\n", " ");
120
fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", " ");
121
fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", " ");
129
SECCertUsage certUsage;
130
CERTCertDBHandle *certHandle;
133
struct decodeOptionsStr {
134
struct optionsStr *options;
137
PRBool suppressContent;
138
NSSCMSGetDecryptKeyCallback dkcb;
143
struct signOptionsStr {
144
struct optionsStr *options;
146
char *encryptionKeyPreferenceNick;
150
SECOidTag hashAlgTag;
153
struct envelopeOptionsStr {
154
struct optionsStr *options;
158
struct certsonlyOptionsStr {
159
struct optionsStr *options;
163
struct encryptOptionsStr {
164
struct optionsStr *options;
166
NSSCMSMessage *envmsg;
171
SECOidTag bulkalgtag;
175
static NSSCMSMessage *
176
decode(FILE *out, SECItem *input, const struct decodeOptionsStr *decodeOptions)
178
NSSCMSDecoderContext *dcx;
182
SECItem sitem = { 0, 0, 0 };
185
dcx = NSS_CMSDecoder_Start(NULL,
186
NULL, NULL, /* content callback */
187
pwcb, pwcb_arg, /* password callback */
188
decodeOptions->dkcb, /* decrypt key callback */
189
decodeOptions->bulkkey);
191
fprintf(stderr, "%s: failed to set up message decoder.\n", progName);
194
rv = NSS_CMSDecoder_Update(dcx, (char *)input->data, input->len);
195
if (rv != SECSuccess) {
196
fprintf(stderr, "%s: failed to decode message.\n", progName);
197
NSS_CMSDecoder_Cancel(dcx);
200
cmsg = NSS_CMSDecoder_Finish(dcx);
202
fprintf(stderr, "%s: failed to decode message.\n", progName);
206
if (decodeOptions->headerLevel >= 0) {
207
/*fprintf(out, "SMIME: ", decodeOptions->headerLevel, i);*/
208
fprintf(out, "SMIME: ");
211
nlevels = NSS_CMSMessage_ContentLevelCount(cmsg);
212
for (i = 0; i < nlevels; i++) {
213
NSSCMSContentInfo *cinfo;
216
cinfo = NSS_CMSMessage_ContentLevel(cmsg, i);
217
typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
219
if (decodeOptions->headerLevel >= 0)
220
fprintf(out, "\tlevel=%d.%d; ", decodeOptions->headerLevel, nlevels - i);
223
case SEC_OID_PKCS7_SIGNED_DATA:
225
NSSCMSSignedData *sigd = NULL;
230
if (decodeOptions->headerLevel >= 0)
231
fprintf(out, "type=signedData; ");
232
sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
234
SECU_PrintError(progName, "signedData component missing");
238
/* if we have a content file, but no digests for this signedData */
239
if (decodeOptions->content.data != NULL &&
240
!NSS_CMSSignedData_HasDigests(sigd)) {
242
SECAlgorithmID **digestalgs;
244
/* detached content: grab content file */
245
sitem = decodeOptions->content;
247
if ((poolp = PORT_NewArena(1024)) == NULL) {
248
fprintf(stderr, "cmsutil: Out of memory.\n");
251
digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
252
if (DigestFile (poolp, &digests, &sitem, digestalgs)
254
SECU_PrintError(progName,
255
"problem computing message digest");
256
PORT_FreeArena(poolp, PR_FALSE);
259
if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests)
261
SECU_PrintError(progName,
262
"problem setting message digests");
263
PORT_FreeArena(poolp, PR_FALSE);
266
PORT_FreeArena(poolp, PR_FALSE);
269
/* import the certificates */
270
if (NSS_CMSSignedData_ImportCerts(sigd,
271
decodeOptions->options->certHandle,
272
decodeOptions->options->certUsage,
273
decodeOptions->keepCerts)
275
SECU_PrintError(progName, "cert import failed");
279
/* find out about signers */
280
nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
281
if (decodeOptions->headerLevel >= 0)
282
fprintf(out, "nsigners=%d; ", nsigners);
284
/* Might be a cert transport message
285
** or might be an invalid message, such as a QA test message
286
** or a message from an attacker.
289
rv = NSS_CMSSignedData_VerifyCertsOnly(sigd,
290
decodeOptions->options->certHandle,
291
decodeOptions->options->certUsage);
292
if (rv != SECSuccess) {
293
fprintf(stderr, "cmsutil: Verify certs-only failed!\n");
299
/* still no digests? */
300
if (!NSS_CMSSignedData_HasDigests(sigd)) {
301
SECU_PrintError(progName, "no message digests");
305
for (j = 0; j < nsigners; j++) {
307
NSSCMSSignerInfo *si;
308
NSSCMSVerificationStatus vs;
311
si = NSS_CMSSignedData_GetSignerInfo(sigd, j);
312
if (decodeOptions->headerLevel >= 0) {
314
static char empty[] = { "" };
316
signercn = NSS_CMSSignerInfo_GetSignerCommonName(si);
317
if (signercn == NULL)
319
fprintf(out, "\n\t\tsigner%d.id=\"%s\"; ", j, signercn);
320
if (signercn != empty)
323
bad = NSS_CMSSignedData_VerifySignerInfo(sigd, j,
324
decodeOptions->options->certHandle,
325
decodeOptions->options->certUsage);
326
vs = NSS_CMSSignerInfo_GetVerificationStatus(si);
327
svs = NSS_CMSUtil_VerificationStatusToString(vs);
328
if (decodeOptions->headerLevel >= 0) {
329
fprintf(out, "signer%d.status=%s; ", j, svs);
331
} else if (bad && out) {
332
fprintf(stderr, "signer %d status = %s\n", j, svs);
338
case SEC_OID_PKCS7_ENVELOPED_DATA:
340
NSSCMSEnvelopedData *envd;
341
if (decodeOptions->headerLevel >= 0)
342
fprintf(out, "type=envelopedData; ");
343
envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo);
345
SECU_PrintError(progName, "envelopedData component missing");
350
case SEC_OID_PKCS7_ENCRYPTED_DATA:
352
NSSCMSEncryptedData *encd;
353
if (decodeOptions->headerLevel >= 0)
354
fprintf(out, "type=encryptedData; ");
355
encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo);
357
SECU_PrintError(progName, "encryptedData component missing");
362
case SEC_OID_PKCS7_DATA:
363
if (decodeOptions->headerLevel >= 0)
364
fprintf(out, "type=data; ");
369
if (decodeOptions->headerLevel >= 0)
373
if (!decodeOptions->suppressContent && out) {
374
SECItem *item = (sitem.data ? &sitem
375
: NSS_CMSMessage_GetContent(cmsg));
376
if (item && item->data && item->len) {
377
fwrite(item->data, item->len, 1, out);
384
NSS_CMSMessage_Destroy(cmsg);
388
/* example of a callback function to use with encoder */
391
writeout(void *arg, const char *buf, unsigned long len)
393
FILE *f = (FILE *)arg;
395
if (f != NULL && buf != NULL)
396
(void)fwrite(buf, len, 1, f);
400
static NSSCMSMessage *
401
signed_data(struct signOptionsStr *signOptions)
403
NSSCMSMessage *cmsg = NULL;
404
NSSCMSContentInfo *cinfo;
405
NSSCMSSignedData *sigd;
406
NSSCMSSignerInfo *signerinfo;
407
CERTCertificate *cert= NULL, *ekpcert = NULL;
410
fprintf(stderr, "Input to signed_data:\n");
411
if (signOptions->options->password)
412
fprintf(stderr, "password [%s]\n", signOptions->options->password);
413
else if (signOptions->options->pwfile)
414
fprintf(stderr, "password file [%s]\n", signOptions->options->pwfile);
416
fprintf(stderr, "password [NULL]\n");
417
fprintf(stderr, "certUsage [%d]\n", signOptions->options->certUsage);
418
if (signOptions->options->certHandle)
419
fprintf(stderr, "certdb [%p]\n", signOptions->options->certHandle);
421
fprintf(stderr, "certdb [NULL]\n");
422
if (signOptions->nickname)
423
fprintf(stderr, "nickname [%s]\n", signOptions->nickname);
425
fprintf(stderr, "nickname [NULL]\n");
427
if (signOptions->nickname == NULL) {
429
"ERROR: please indicate the nickname of a certificate to sign with.\n");
432
if ((cert = CERT_FindUserCertByUsage(signOptions->options->certHandle,
433
signOptions->nickname,
434
signOptions->options->certUsage,
437
SECU_PrintError(progName,
438
"the corresponding cert for key \"%s\" does not exist",
439
signOptions->nickname);
443
fprintf(stderr, "Found certificate for %s\n", signOptions->nickname);
446
* create the message object
448
cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
450
fprintf(stderr, "ERROR: cannot create CMS message.\n");
454
* build chain of objects: message->signedData->data
456
if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) {
457
fprintf(stderr, "ERROR: cannot create CMS signedData object.\n");
460
cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
461
if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd)
463
fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n");
466
cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
467
/* we're always passing data in and detaching optionally */
468
if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL,
469
signOptions->detached)
471
fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
475
* create & attach signer information
477
signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, signOptions->hashAlgTag);
478
if (signerinfo == NULL) {
479
fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n");
484
"Created CMS message, added signed data w/ signerinfo\n");
486
/* we want the cert chain included for this one */
487
if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain,
488
signOptions->options->certUsage)
490
fprintf(stderr, "ERROR: cannot find cert chain.\n");
494
fprintf(stderr, "imported certificate\n");
496
if (signOptions->signingTime) {
497
if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now())
499
fprintf(stderr, "ERROR: cannot add signingTime attribute.\n");
503
if (signOptions->smimeProfile) {
504
if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) {
505
fprintf(stderr, "ERROR: cannot add SMIMECaps attribute.\n");
510
if (!signOptions->encryptionKeyPreferenceNick) {
511
/* check signing cert for fitness as encryption cert */
512
SECStatus FitForEncrypt = CERT_CheckCertUsage(cert,
513
certUsageEmailRecipient);
515
if (SECSuccess == FitForEncrypt) {
516
/* if yes, add signing cert as EncryptionKeyPreference */
517
if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, cert,
518
signOptions->options->certHandle)
521
"ERROR: cannot add default SMIMEEncKeyPrefs attribute.\n");
524
if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, cert,
525
signOptions->options->certHandle)
528
"ERROR: cannot add default MS SMIMEEncKeyPrefs attribute.\n");
532
/* this is a dual-key cert case, we need to look for the encryption
533
certificate under the same nickname as the signing cert */
534
/* get the cert, add it to the message */
535
if ((ekpcert = CERT_FindUserCertByUsage(
536
signOptions->options->certHandle,
537
signOptions->nickname,
538
certUsageEmailRecipient,
541
SECU_PrintError(progName,
542
"the corresponding cert for key \"%s\" does not exist",
543
signOptions->encryptionKeyPreferenceNick);
546
if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert,
547
signOptions->options->certHandle)
550
"ERROR: cannot add SMIMEEncKeyPrefs attribute.\n");
553
if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert,
554
signOptions->options->certHandle)
557
"ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n");
560
if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) {
561
fprintf(stderr, "ERROR: cannot add encryption certificate.\n");
565
} else if (PL_strcmp(signOptions->encryptionKeyPreferenceNick, "NONE") == 0) {
568
/* get the cert, add it to the message */
569
if ((ekpcert = CERT_FindUserCertByUsage(
570
signOptions->options->certHandle,
571
signOptions->encryptionKeyPreferenceNick,
572
certUsageEmailRecipient, PR_FALSE, &pwdata))
574
SECU_PrintError(progName,
575
"the corresponding cert for key \"%s\" does not exist",
576
signOptions->encryptionKeyPreferenceNick);
579
if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert,
580
signOptions->options->certHandle)
582
fprintf(stderr, "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n");
585
if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert,
586
signOptions->options->certHandle)
588
fprintf(stderr, "ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n");
591
if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) {
592
fprintf(stderr, "ERROR: cannot add encryption certificate.\n");
597
if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
598
fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n");
602
fprintf(stderr, "created signed-data message\n");
605
CERT_DestroyCertificate(ekpcert);
608
CERT_DestroyCertificate(cert);
613
CERT_DestroyCertificate(ekpcert);
616
CERT_DestroyCertificate(cert);
618
NSS_CMSMessage_Destroy(cmsg);
622
static NSSCMSMessage *
623
enveloped_data(struct envelopeOptionsStr *envelopeOptions)
625
NSSCMSMessage *cmsg = NULL;
626
NSSCMSContentInfo *cinfo;
627
NSSCMSEnvelopedData *envd;
628
NSSCMSRecipientInfo *recipientinfo;
629
CERTCertificate **recipientcerts = NULL;
630
CERTCertDBHandle *dbhandle;
631
PLArenaPool *tmppoolp = NULL;
632
SECOidTag bulkalgtag;
635
dbhandle = envelopeOptions->options->certHandle;
636
/* count the recipients */
637
if ((cnt = nss_CMSArray_Count((void **)envelopeOptions->recipients)) == 0) {
638
fprintf(stderr, "ERROR: please name at least one recipient.\n");
641
if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
642
fprintf(stderr, "ERROR: out of memory.\n");
645
/* XXX find the recipient's certs by email address or nickname */
646
if ((recipientcerts =
647
(CERTCertificate **)PORT_ArenaZAlloc(tmppoolp,
648
(cnt+1)*sizeof(CERTCertificate*)))
650
fprintf(stderr, "ERROR: out of memory.\n");
653
for (i=0; envelopeOptions->recipients[i] != NULL; i++) {
654
if ((recipientcerts[i] =
655
CERT_FindCertByNicknameOrEmailAddr(dbhandle,
656
envelopeOptions->recipients[i]))
658
SECU_PrintError(progName, "cannot find certificate for \"%s\"",
659
envelopeOptions->recipients[i]);
664
recipientcerts[i] = NULL;
666
/* find a nice bulk algorithm */
667
if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientcerts, &bulkalgtag,
668
&keysize) != SECSuccess) {
669
fprintf(stderr, "ERROR: cannot find common bulk algorithm.\n");
673
* create the message object
675
cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
677
fprintf(stderr, "ERROR: cannot create CMS message.\n");
681
* build chain of objects: message->envelopedData->data
683
if ((envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, keysize))
685
fprintf(stderr, "ERROR: cannot create CMS envelopedData object.\n");
688
cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
689
if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd)
691
fprintf(stderr, "ERROR: cannot attach CMS envelopedData object.\n");
694
cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd);
695
/* we're always passing data in, so the content is NULL */
696
if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE)
698
fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
702
* create & attach recipient information
704
for (i = 0; recipientcerts[i] != NULL; i++) {
705
if ((recipientinfo = NSS_CMSRecipientInfo_Create(cmsg,
708
fprintf(stderr, "ERROR: cannot create CMS recipientInfo object.\n");
711
if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientinfo)
713
fprintf(stderr, "ERROR: cannot add CMS recipientInfo object.\n");
716
CERT_DestroyCertificate(recipientcerts[i]);
719
PORT_FreeArena(tmppoolp, PR_FALSE);
722
if (recipientcerts) {
723
for (; recipientcerts[i] != NULL; i++) {
724
CERT_DestroyCertificate(recipientcerts[i]);
728
NSS_CMSMessage_Destroy(cmsg);
730
PORT_FreeArena(tmppoolp, PR_FALSE);
734
PK11SymKey *dkcb(void *arg, SECAlgorithmID *algid)
736
return (PK11SymKey*)arg;
740
get_enc_params(struct encryptOptionsStr *encryptOptions)
742
struct envelopeOptionsStr envelopeOptions;
743
SECStatus rv = SECFailure;
744
NSSCMSMessage *env_cmsg;
745
NSSCMSContentInfo *cinfo;
748
* construct an enveloped data message to obtain bulk keys
750
if (encryptOptions->envmsg) {
751
env_cmsg = encryptOptions->envmsg; /* get it from an old message */
753
SECItem dummyOut = { 0, 0, 0 };
754
SECItem dummyIn = { 0, 0, 0 };
755
char str[] = "Hello!";
756
PLArenaPool *tmparena = PORT_NewArena(1024);
757
dummyIn.data = (unsigned char *)str;
758
dummyIn.len = strlen(str);
759
envelopeOptions.options = encryptOptions->options;
760
envelopeOptions.recipients = encryptOptions->recipients;
761
env_cmsg = enveloped_data(&envelopeOptions);
762
NSS_CMSDEREncode(env_cmsg, &dummyIn, &dummyOut, tmparena);
763
PR_Write(encryptOptions->envFile, dummyOut.data, dummyOut.len);
764
PORT_FreeArena(tmparena, PR_FALSE);
767
* get the content info for the enveloped data
769
nlevels = NSS_CMSMessage_ContentLevelCount(env_cmsg);
770
for (i = 0; i < nlevels; i++) {
772
cinfo = NSS_CMSMessage_ContentLevel(env_cmsg, i);
773
typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
774
if (typetag == SEC_OID_PKCS7_DATA) {
776
* get the symmetric key
778
encryptOptions->bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo);
779
encryptOptions->keysize = NSS_CMSContentInfo_GetBulkKeySize(cinfo);
780
encryptOptions->bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo);
786
fprintf(stderr, "%s: could not retrieve enveloped data.", progName);
789
NSS_CMSMessage_Destroy(env_cmsg);
793
static NSSCMSMessage *
794
encrypted_data(struct encryptOptionsStr *encryptOptions)
796
SECStatus rv = SECFailure;
797
NSSCMSMessage *cmsg = NULL;
798
NSSCMSContentInfo *cinfo;
799
NSSCMSEncryptedData *encd;
800
NSSCMSEncoderContext *ecx = NULL;
801
PLArenaPool *tmppoolp = NULL;
802
SECItem derOut = { 0, 0, 0 };
803
/* arena for output */
804
tmppoolp = PORT_NewArena(1024);
806
fprintf(stderr, "%s: out of memory.\n", progName);
810
* create the message object
812
cmsg = NSS_CMSMessage_Create(NULL);
814
fprintf(stderr, "ERROR: cannot create CMS message.\n");
818
* build chain of objects: message->encryptedData->data
820
if ((encd = NSS_CMSEncryptedData_Create(cmsg, encryptOptions->bulkalgtag,
821
encryptOptions->keysize))
823
fprintf(stderr, "ERROR: cannot create CMS encryptedData object.\n");
826
cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
827
if (NSS_CMSContentInfo_SetContent_EncryptedData(cmsg, cinfo, encd)
829
fprintf(stderr, "ERROR: cannot attach CMS encryptedData object.\n");
832
cinfo = NSS_CMSEncryptedData_GetContentInfo(encd);
833
/* we're always passing data in, so the content is NULL */
834
if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE)
836
fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
839
ecx = NSS_CMSEncoder_Start(cmsg, NULL, NULL, &derOut, tmppoolp, NULL, NULL,
840
dkcb, encryptOptions->bulkkey, NULL, NULL);
842
fprintf(stderr, "%s: cannot create encoder context.\n", progName);
845
rv = NSS_CMSEncoder_Update(ecx, (char *)encryptOptions->input->data,
846
encryptOptions->input->len);
848
fprintf(stderr, "%s: failed to add data to encoder.\n", progName);
851
rv = NSS_CMSEncoder_Finish(ecx);
853
fprintf(stderr, "%s: failed to encrypt data.\n", progName);
856
fwrite(derOut.data, derOut.len, 1, encryptOptions->outfile);
859
PK11_FreeSymKey(bulkkey);
862
PORT_FreeArena(tmppoolp, PR_FALSE);
867
PK11_FreeSymKey(bulkkey);
870
PORT_FreeArena(tmppoolp, PR_FALSE);
872
NSS_CMSMessage_Destroy(cmsg);
876
static NSSCMSMessage *
877
signed_data_certsonly(struct certsonlyOptionsStr *certsonlyOptions)
879
NSSCMSMessage *cmsg = NULL;
880
NSSCMSContentInfo *cinfo;
881
NSSCMSSignedData *sigd;
882
CERTCertificate **certs = NULL;
883
CERTCertDBHandle *dbhandle;
884
PLArenaPool *tmppoolp = NULL;
886
dbhandle = certsonlyOptions->options->certHandle;
887
if ((cnt = nss_CMSArray_Count((void**)certsonlyOptions->recipients)) == 0) {
889
"ERROR: please indicate the nickname of a certificate to sign with.\n");
892
if (!(tmppoolp = PORT_NewArena(1024))) {
893
fprintf(stderr, "ERROR: out of memory.\n");
896
if (!(certs = PORT_ArenaZNewArray(tmppoolp, CERTCertificate *, cnt + 1))) {
897
fprintf(stderr, "ERROR: out of memory.\n");
900
for (i=0; certsonlyOptions->recipients[i] != NULL; i++) {
902
CERT_FindCertByNicknameOrEmailAddr(dbhandle,
903
certsonlyOptions->recipients[i]))
905
SECU_PrintError(progName, "cannot find certificate for \"%s\"",
906
certsonlyOptions->recipients[i]);
914
* create the message object
916
cmsg = NSS_CMSMessage_Create(NULL);
918
fprintf(stderr, "ERROR: cannot create CMS message.\n");
922
* build chain of objects: message->signedData->data
924
if ((sigd = NSS_CMSSignedData_CreateCertsOnly(cmsg, certs[0], PR_TRUE))
926
fprintf(stderr, "ERROR: cannot create CMS signedData object.\n");
929
CERT_DestroyCertificate(certs[0]);
930
for (i=1; i<cnt; i++) {
931
if (NSS_CMSSignedData_AddCertChain(sigd, certs[i])) {
932
fprintf(stderr, "ERROR: cannot add cert chain for \"%s\".\n",
933
certsonlyOptions->recipients[i]);
936
CERT_DestroyCertificate(certs[i]);
938
cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
939
if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd)
941
fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n");
944
cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
945
if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE)
947
fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
951
PORT_FreeArena(tmppoolp, PR_FALSE);
956
CERT_DestroyCertificate(certs[i]);
960
NSS_CMSMessage_Destroy(cmsg);
962
PORT_FreeArena(tmppoolp, PR_FALSE);
967
pl_fgets(char * buf, int size, PRFileDesc * fd)
973
nb = PR_Read(fd, bp, 1);
975
/* deal with error */
977
} else if (nb == 0) {
980
} else if (*bp == '\n') {
982
++bp; /* keep EOL character */
985
/* ordinary character */
994
typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT, ENVELOPE, CERTSONLY } Mode;
997
doBatchDecode(FILE *outFile, PRFileDesc *batchFile,
998
const struct decodeOptionsStr *decodeOptions)
1002
char batchLine[512];
1004
while (NULL != (str = pl_fgets(batchLine, sizeof batchLine, batchFile))) {
1005
NSSCMSMessage *cmsg = NULL;
1006
PRFileDesc * inFile;
1007
int len = strlen(str);
1009
SECItem input = {0, 0, 0};
1013
((cc = str[len - 1]) == '\n' || cc == '\r')) {
1016
if (!len) /* skip empty line */
1019
continue; /* skip comment line */
1020
fprintf(outFile, "========== %s ==========\n", str);
1021
inFile = PR_Open(str, PR_RDONLY, 00660);
1022
if (inFile == NULL) {
1023
fprintf(outFile, "%s: unable to open \"%s\" for reading\n",
1028
rv = SECU_FileToItem(&input, inFile);
1030
if (rv != SECSuccess) {
1031
SECU_PrintError(progName, "unable to read infile");
1035
cmsg = decode(outFile, &input, decodeOptions);
1036
SECITEM_FreeItem(&input, PR_FALSE);
1038
NSS_CMSMessage_Destroy(cmsg);
1040
SECU_PrintError(progName, "problem decoding");
1048
main(int argc, char **argv)
1051
NSSCMSMessage *cmsg = NULL;
1053
PLOptState *optstate;
1055
Mode mode = UNKNOWN;
1056
struct decodeOptionsStr decodeOptions = { 0 };
1057
struct signOptionsStr signOptions = { 0 };
1058
struct envelopeOptionsStr envelopeOptions = { 0 };
1059
struct certsonlyOptionsStr certsonlyOptions = { 0 };
1060
struct encryptOptionsStr encryptOptions = { 0 };
1061
struct optionsStr options = { 0 };
1063
static char *ptrarray[128] = { 0 };
1064
int nrecipients = 0;
1067
SECItem input = { 0, 0, 0};
1068
SECItem envmsg = { 0, 0, 0 };
1070
PRFileDesc *contentFile = NULL;
1071
PRBool batch = PR_FALSE;
1074
const char *ev = PR_GetEnv("NSS_DISABLE_ARENA_FREE_LIST");
1076
ev = PR_GetEnv("NSS_STRICT_SHUTDOWN");
1080
progName = strrchr(argv[0], '/');
1082
progName = strrchr(argv[0], '\\');
1083
progName = progName ? progName+1 : argv[0];
1089
decodeOptions.content.data = NULL;
1090
decodeOptions.content.len = 0;
1091
decodeOptions.suppressContent = PR_FALSE;
1092
decodeOptions.headerLevel = -1;
1093
decodeOptions.keepCerts = PR_FALSE;
1094
options.certUsage = certUsageEmailSigner;
1095
options.password = NULL;
1096
options.pwfile = NULL;
1097
signOptions.nickname = NULL;
1098
signOptions.detached = PR_FALSE;
1099
signOptions.signingTime = PR_FALSE;
1100
signOptions.smimeProfile = PR_FALSE;
1101
signOptions.encryptionKeyPreferenceNick = NULL;
1102
signOptions.hashAlgTag = SEC_OID_SHA1;
1103
envelopeOptions.recipients = NULL;
1104
encryptOptions.recipients = NULL;
1105
encryptOptions.envmsg = NULL;
1106
encryptOptions.envFile = NULL;
1107
encryptOptions.bulkalgtag = SEC_OID_UNKNOWN;
1108
encryptOptions.bulkkey = NULL;
1109
encryptOptions.keysize = -1;
1112
* Parse command line arguments
1114
optstate = PL_CreateOptState(argc, argv,
1115
"CDEGH:N:OPSTY:bc:d:e:f:h:i:kno:p:r:s:u:v");
1116
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
1117
switch (optstate->option) {
1130
"%s: option -G only supported with option -S.\n",
1135
signOptions.signingTime = PR_TRUE;
1140
"%s: option -H only supported with option -S.\n",
1145
decodeOptions.suppressContent = PR_TRUE;
1146
if (!strcmp(optstate->value, "MD2"))
1147
signOptions.hashAlgTag = SEC_OID_MD2;
1148
else if (!strcmp(optstate->value, "MD4"))
1149
signOptions.hashAlgTag = SEC_OID_MD4;
1150
else if (!strcmp(optstate->value, "MD5"))
1151
signOptions.hashAlgTag = SEC_OID_MD5;
1152
else if (!strcmp(optstate->value, "SHA1"))
1153
signOptions.hashAlgTag = SEC_OID_SHA1;
1154
else if (!strcmp(optstate->value, "SHA256"))
1155
signOptions.hashAlgTag = SEC_OID_SHA256;
1156
else if (!strcmp(optstate->value, "SHA384"))
1157
signOptions.hashAlgTag = SEC_OID_SHA384;
1158
else if (!strcmp(optstate->value, "SHA512"))
1159
signOptions.hashAlgTag = SEC_OID_SHA512;
1162
"%s: -H requires one of MD2,MD4,MD5,SHA1,SHA256,SHA384,SHA512\n",
1170
"%s: option -N only supported with option -S.\n",
1175
signOptions.nickname = strdup(optstate->value);
1183
"%s: option -P only supported with option -S.\n",
1188
signOptions.smimeProfile = PR_TRUE;
1196
"%s: option -T only supported with option -S.\n",
1201
signOptions.detached = PR_TRUE;
1206
"%s: option -Y only supported with option -S.\n",
1211
signOptions.encryptionKeyPreferenceNick = strdup(optstate->value);
1215
if (mode != DECODE) {
1217
"%s: option -b only supported with option -D.\n",
1226
if (mode != DECODE) {
1228
"%s: option -c only supported with option -D.\n",
1233
contentFile = PR_Open(optstate->value, PR_RDONLY, 006600);
1234
if (contentFile == NULL) {
1235
fprintf(stderr, "%s: unable to open \"%s\" for reading.\n",
1236
progName, optstate->value);
1240
rv = SECU_FileToItem(&decodeOptions.content, contentFile);
1241
PR_Close(contentFile);
1242
if (rv != SECSuccess) {
1243
SECU_PrintError(progName, "problem reading content file");
1246
if (!decodeOptions.content.data) {
1247
/* file was zero length */
1248
decodeOptions.content.data = (unsigned char *)PORT_Strdup("");
1249
decodeOptions.content.len = 0;
1254
SECU_ConfigDirectory(optstate->value);
1257
envFileName = strdup(optstate->value);
1258
encryptOptions.envFile = PR_Open(envFileName, PR_RDONLY, 00660);
1262
if (mode != DECODE) {
1264
"%s: option -h only supported with option -D.\n",
1269
decodeOptions.headerLevel = atoi(optstate->value);
1270
if (decodeOptions.headerLevel < 0) {
1271
fprintf(stderr, "option -h cannot have a negative value.\n");
1276
if (!optstate->value) {
1277
fprintf(stderr, "-i option requires filename argument\n");
1280
inFile = PR_Open(optstate->value, PR_RDONLY, 00660);
1281
if (inFile == NULL) {
1282
fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
1283
progName, optstate->value);
1289
if (mode != DECODE) {
1291
"%s: option -k only supported with option -D.\n",
1296
decodeOptions.keepCerts = PR_TRUE;
1300
if (mode != DECODE) {
1302
"%s: option -n only supported with option -D.\n",
1307
decodeOptions.suppressContent = PR_TRUE;
1310
outFile = fopen(optstate->value, "wb");
1311
if (outFile == NULL) {
1312
fprintf(stderr, "%s: unable to open \"%s\" for writing\n",
1313
progName, optstate->value);
1318
if (!optstate->value) {
1319
fprintf(stderr, "%s: option -p must have a value.\n", progName);
1324
options.password = strdup(optstate->value);
1328
if (!optstate->value) {
1329
fprintf(stderr, "%s: option -f must have a value.\n", progName);
1334
options.pwfile = strdup(optstate->value);
1338
if (!optstate->value) {
1339
fprintf(stderr, "%s: option -r must have a value.\n", progName);
1343
envelopeOptions.recipients = ptrarray;
1344
str = (char *)optstate->value;
1346
tok = strchr(str, ',');
1347
if (tok) *tok = '\0';
1348
envelopeOptions.recipients[nrecipients++] = strdup(str);
1349
if (tok) str = tok + 1;
1351
envelopeOptions.recipients[nrecipients] = NULL;
1352
encryptOptions.recipients = envelopeOptions.recipients;
1353
certsonlyOptions.recipients = envelopeOptions.recipients;
1359
usageType = atoi (strdup(optstate->value));
1360
if (usageType < certUsageSSLClient || usageType > certUsageAnyCA)
1362
options.certUsage = (SECCertUsage)usageType;
1371
if (status == PL_OPT_BAD)
1373
PL_DestroyOptState(optstate);
1375
if (mode == UNKNOWN)
1378
if (mode != CERTSONLY && !batch) {
1379
rv = SECU_FileToItem(&input, inFile);
1380
if (rv != SECSuccess) {
1381
SECU_PrintError(progName, "unable to read infile");
1384
if (inFile != PR_STDIN) {
1389
fprintf(stderr, "received commands\n");
1392
/* Call the NSS initialization routines */
1393
PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
1394
rv = NSS_InitReadWrite(SECU_ConfigDirectory(NULL));
1395
if (SECSuccess != rv) {
1396
SECU_PrintError(progName, "NSS_Init failed");
1400
fprintf(stderr, "NSS has been initialized.\n");
1402
options.certHandle = CERT_GetDefaultCertDB();
1403
if (!options.certHandle) {
1404
SECU_PrintError(progName, "No default cert DB");
1408
fprintf(stderr, "Got default certdb\n");
1410
if (options.password)
1412
pwdata.source = PW_PLAINTEXT;
1413
pwdata.data = options.password;
1417
pwdata.source = PW_FROMFILE;
1418
pwdata.data = options.pwfile;
1420
pwcb = SECU_GetModulePassword;
1421
pwcb_arg = (void *)&pwdata;
1423
PK11_SetPasswordFunc(&SECU_GetModulePassword);
1427
if (outFile == stdout) {
1428
/* If we're going to write binary data to stdout, we must put stdout
1429
** into O_BINARY mode or else outgoing \n's will become \r\n's.
1431
int smrv = _setmode(_fileno(stdout), _O_BINARY);
1434
"%s: Cannot change stdout to binary mode. Use -o option instead.\n",
1443
case DECODE: /* -D */
1444
decodeOptions.options = &options;
1445
if (encryptOptions.envFile) {
1446
/* Decoding encrypted-data, so get the bulkkey from an
1447
* enveloped-data message.
1449
SECU_FileToItem(&envmsg, encryptOptions.envFile);
1450
decodeOptions.options = &options;
1451
encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions);
1452
if (!encryptOptions.envmsg) {
1453
SECU_PrintError(progName, "problem decoding env msg");
1457
rv = get_enc_params(&encryptOptions);
1458
decodeOptions.dkcb = dkcb;
1459
decodeOptions.bulkkey = encryptOptions.bulkkey;
1462
cmsg = decode(outFile, &input, &decodeOptions);
1464
SECU_PrintError(progName, "problem decoding");
1468
exitstatus = doBatchDecode(outFile, inFile, &decodeOptions);
1469
if (inFile != PR_STDIN) {
1475
signOptions.options = &options;
1476
cmsg = signed_data(&signOptions);
1478
SECU_PrintError(progName, "problem signing");
1482
case ENCRYPT: /* -C */
1484
fprintf(stderr, "%s: you must specify an envelope file with -e.\n",
1488
encryptOptions.options = &options;
1489
encryptOptions.input = &input;
1490
encryptOptions.outfile = outFile;
1491
/* decode an enveloped-data message to get the bulkkey (create
1492
* a new one if neccessary)
1494
if (!encryptOptions.envFile) {
1495
encryptOptions.envFile = PR_Open(envFileName,
1496
PR_WRONLY|PR_CREATE_FILE, 00660);
1497
if (!encryptOptions.envFile) {
1498
fprintf(stderr, "%s: failed to create file %s.\n", progName,
1503
SECU_FileToItem(&envmsg, encryptOptions.envFile);
1504
decodeOptions.options = &options;
1505
encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions);
1506
if (encryptOptions.envmsg == NULL) {
1507
SECU_PrintError(progName, "problem decrypting env msg");
1512
rv = get_enc_params(&encryptOptions);
1513
/* create the encrypted-data message */
1514
cmsg = encrypted_data(&encryptOptions);
1516
SECU_PrintError(progName, "problem encrypting");
1519
if (encryptOptions.bulkkey) {
1520
PK11_FreeSymKey(encryptOptions.bulkkey);
1521
encryptOptions.bulkkey = NULL;
1524
case ENVELOPE: /* -E */
1525
envelopeOptions.options = &options;
1526
cmsg = enveloped_data(&envelopeOptions);
1528
SECU_PrintError(progName, "problem enveloping");
1532
case CERTSONLY: /* -O */
1533
certsonlyOptions.options = &options;
1534
cmsg = signed_data_certsonly(&certsonlyOptions);
1536
SECU_PrintError(progName, "problem with certs-only");
1541
fprintf(stderr, "One of options -D, -S or -E must be set.\n");
1545
if ( (mode == SIGN || mode == ENVELOPE || mode == CERTSONLY)
1546
&& (!exitstatus) ) {
1547
PLArenaPool *arena = PORT_NewArena(1024);
1548
NSSCMSEncoderContext *ecx;
1549
SECItem output = { 0, 0, 0 };
1552
fprintf(stderr, "%s: out of memory.\n", progName);
1557
fprintf(stderr, "cmsg [%p]\n", cmsg);
1558
fprintf(stderr, "arena [%p]\n", arena);
1559
if (pwcb_arg && (PW_PLAINTEXT == ((secuPWData*)pwcb_arg)->source))
1560
fprintf(stderr, "password [%s]\n",
1561
((secuPWData*)pwcb_arg)->data);
1563
fprintf(stderr, "password [NULL]\n");
1565
ecx = NSS_CMSEncoder_Start(cmsg,
1566
NULL, NULL, /* DER output callback */
1567
&output, arena, /* destination storage */
1568
pwcb, pwcb_arg, /* password callback */
1569
NULL, NULL, /* decrypt key callback */
1570
NULL, NULL ); /* detached digests */
1572
fprintf(stderr, "%s: cannot create encoder context.\n", progName);
1576
fprintf(stderr, "input len [%d]\n", input.len);
1578
for(j=0;j<input.len;j++)
1579
fprintf(stderr, "%2x%c", input.data[j], (j>0&&j%35==0)?'\n':' ');
1582
if (input.len > 0) { /* skip if certs-only (or other zero content) */
1583
rv = NSS_CMSEncoder_Update(ecx, (char *)input.data, input.len);
1586
"%s: failed to add data to encoder.\n", progName);
1590
rv = NSS_CMSEncoder_Finish(ecx);
1592
SECU_PrintError(progName, "failed to encode data");
1597
fprintf(stderr, "encoding passed\n");
1599
fwrite(output.data, output.len, 1, outFile);
1601
fprintf(stderr, "wrote to file\n");
1603
PORT_FreeArena(arena, PR_FALSE);
1606
NSS_CMSMessage_Destroy(cmsg);
1607
if (outFile != stdout)
1610
SECITEM_FreeItem(&decodeOptions.content, PR_FALSE);
1611
SECITEM_FreeItem(&envmsg, PR_FALSE);
1612
SECITEM_FreeItem(&input, PR_FALSE);
1613
if (NSS_Shutdown() != SECSuccess) {
1614
SECU_PrintError(progName, "NSS_Shutdown failed");