summaryrefslogtreecommitdiff
path: root/security/nss/cmd/smimetools/cmsutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/cmd/smimetools/cmsutil.c')
-rw-r--r--security/nss/cmd/smimetools/cmsutil.c1430
1 files changed, 1430 insertions, 0 deletions
diff --git a/security/nss/cmd/smimetools/cmsutil.c b/security/nss/cmd/smimetools/cmsutil.c
new file mode 100644
index 000000000..ec2fe56e8
--- /dev/null
+++ b/security/nss/cmd/smimetools/cmsutil.c
@@ -0,0 +1,1430 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * cmsutil -- A command to work with CMS data
+ *
+ * $Id$
+ */
+
+#include "nspr.h"
+#include "secutil.h"
+#include "plgetopt.h"
+#include "secpkcs7.h"
+#include "cert.h"
+#include "certdb.h"
+#include "secoid.h"
+#include "cms.h"
+#include "nss.h"
+#include "smime.h"
+
+#if defined(XP_UNIX)
+#include <unistd.h>
+#endif
+
+#if defined(_WIN32)
+#include "fcntl.h"
+#include "io.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+extern void SEC_Init(void); /* XXX */
+char *progName = NULL;
+static int cms_verbose = 0;
+
+/* XXX stolen from cmsarray.c
+ * nss_CMSArray_Count - count number of elements in array
+ */
+int
+nss_CMSArray_Count(void **array)
+{
+ int n = 0;
+ if (array == NULL)
+ return 0;
+ while (*array++ != NULL)
+ n++;
+ return n;
+}
+
+static SECStatus
+DigestFile(PLArenaPool *poolp, SECItem ***digests, SECItem *input,
+ SECAlgorithmID **algids)
+{
+ NSSCMSDigestContext *digcx;
+
+ digcx = NSS_CMSDigestContext_StartMultiple(algids);
+ if (digcx == NULL)
+ return SECFailure;
+
+ NSS_CMSDigestContext_Update(digcx, input->data, input->len);
+
+ return NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests);
+}
+
+
+static void
+Usage(char *progName)
+{
+ fprintf(stderr, "Usage: %s [-D|-S|-E] [<options>] [-d dbdir] [-u certusage]\n", progName);
+ fprintf(stderr, " -i infile use infile as source of data (default: stdin)\n");
+ fprintf(stderr, " -o outfile use outfile as destination of data (default: stdout)\n");
+ fprintf(stderr, " -d dbdir key/cert database directory (default: ~/.netscape)\n");
+ fprintf(stderr, " -p password use password as key db password (default: prompt)\n");
+ fprintf(stderr, " -u certusage set type of certificate usage (default: certUsageEmailSigner)\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -D decode a CMS message\n");
+ fprintf(stderr, " -c content use this detached content\n");
+ fprintf(stderr, " -n suppress output of content\n");
+ fprintf(stderr, " -h num generate email headers with info about CMS message\n");
+ fprintf(stderr, " -S create a CMS signed message\n");
+ fprintf(stderr, " -N nick use certificate named \"nick\" for signing\n");
+ fprintf(stderr, " -T do not include content in CMS message\n");
+ fprintf(stderr, " -G include a signing time attribute\n");
+ fprintf(stderr, " -P include a SMIMECapabilities attribute\n");
+ fprintf(stderr, " -Y nick include a EncryptionKeyPreference attribute with cert\n");
+ fprintf(stderr, " -E create a CMS enveloped message (NYI)\n");
+ fprintf(stderr, " -r id,... create envelope for these recipients,\n");
+ fprintf(stderr, " where id can be a certificate nickname or email address\n");
+ fprintf(stderr, " -v print debugging information\n");
+ fprintf(stderr, "\nCert usage codes:\n");
+ fprintf(stderr, "%-25s 0 - certUsageSSLClient\n", " ");
+ fprintf(stderr, "%-25s 1 - certUsageSSLServer\n", " ");
+ fprintf(stderr, "%-25s 2 - certUsageSSLServerWithStepUp\n", " ");
+ fprintf(stderr, "%-25s 3 - certUsageSSLCA\n", " ");
+ fprintf(stderr, "%-25s 4 - certUsageEmailSigner\n", " ");
+ fprintf(stderr, "%-25s 5 - certUsageEmailRecipient\n", " ");
+ fprintf(stderr, "%-25s 6 - certUsageObjectSigner\n", " ");
+ fprintf(stderr, "%-25s 7 - certUsageUserCertImport\n", " ");
+ fprintf(stderr, "%-25s 8 - certUsageVerifyCA\n", " ");
+ fprintf(stderr, "%-25s 9 - certUsageProtectedObjectSigner\n", " ");
+ fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", " ");
+ fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", " ");
+
+ exit(-1);
+}
+
+char *
+ownpw(PK11SlotInfo *info, PRBool retry, void *arg)
+{
+ char * passwd = NULL;
+
+ if ( (!retry) && arg ) {
+ passwd = PL_strdup((char *)arg);
+ }
+
+ return passwd;
+}
+
+struct optionsStr {
+ char *password;
+ SECCertUsage certUsage;
+ CERTCertDBHandle *certHandle;
+};
+
+struct decodeOptionsStr {
+ struct optionsStr *options;
+ PRFileDesc *contentFile;
+ int headerLevel;
+ PRBool suppressContent;
+ NSSCMSGetDecryptKeyCallback dkcb;
+ PK11SymKey *bulkkey;
+};
+
+struct signOptionsStr {
+ struct optionsStr *options;
+ char *nickname;
+ char *encryptionKeyPreferenceNick;
+ PRBool signingTime;
+ PRBool smimeProfile;
+ PRBool detached;
+};
+
+struct envelopeOptionsStr {
+ struct optionsStr *options;
+ char **recipients;
+};
+
+struct certsonlyOptionsStr {
+ struct optionsStr *options;
+ char **recipients;
+};
+
+struct encryptOptionsStr {
+ struct optionsStr *options;
+ char **recipients;
+ NSSCMSMessage *envmsg;
+ SECItem *input;
+ FILE *outfile;
+ PRFileDesc *envFile;
+ PK11SymKey *bulkkey;
+ SECOidTag bulkalgtag;
+ int keysize;
+};
+
+static NSSCMSMessage *
+decode(FILE *out, SECItem *output, SECItem *input,
+ struct decodeOptionsStr *decodeOptions)
+{
+ NSSCMSDecoderContext *dcx;
+ NSSCMSMessage *cmsg;
+ NSSCMSContentInfo *cinfo;
+ NSSCMSSignedData *sigd = NULL;
+ NSSCMSEnvelopedData *envd;
+ NSSCMSEncryptedData *encd;
+ SECAlgorithmID **digestalgs;
+ int nlevels, i, nsigners, j;
+ char *signercn;
+ NSSCMSSignerInfo *si;
+ SECOidTag typetag;
+ SECItem **digests;
+ PLArenaPool *poolp;
+ PK11PasswordFunc pwcb;
+ void *pwcb_arg;
+ SECItem *item, sitem = { 0, 0, 0 };
+
+ pwcb = (decodeOptions->options->password != NULL) ? ownpw : NULL;
+ pwcb_arg = (decodeOptions->options->password != NULL) ?
+ (void *)decodeOptions->options->password : NULL;
+
+ if (decodeOptions->contentFile) {
+ /* detached content: grab content file */
+ SECU_FileToItem(&sitem, decodeOptions->contentFile);
+ item = &sitem;
+ }
+
+ dcx = NSS_CMSDecoder_Start(NULL,
+ NULL, NULL, /* content callback */
+ pwcb, pwcb_arg, /* password callback */
+ decodeOptions->dkcb, /* decrypt key callback */
+ decodeOptions->bulkkey);
+ (void)NSS_CMSDecoder_Update(dcx, (char *)input->data, input->len);
+ cmsg = NSS_CMSDecoder_Finish(dcx);
+ if (cmsg == NULL) {
+ fprintf(stderr, "%s: failed to decode message.\n", progName);
+ return NULL;
+ }
+
+ if (decodeOptions->headerLevel >= 0) {
+ /*fprintf(out, "SMIME: ", decodeOptions->headerLevel, i);*/
+ fprintf(out, "SMIME: ");
+ }
+
+ nlevels = NSS_CMSMessage_ContentLevelCount(cmsg);
+ for (i = 0; i < nlevels; i++) {
+ cinfo = NSS_CMSMessage_ContentLevel(cmsg, i);
+ typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+
+ if (decodeOptions->headerLevel >= 0)
+ fprintf(out, "\tlevel=%d.%d; ", decodeOptions->headerLevel, nlevels - i);
+
+ switch (typetag) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ if (decodeOptions->headerLevel >= 0)
+ fprintf(out, "type=signedData; ");
+ sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
+ if (sigd == NULL) {
+ SECU_PrintError(progName,
+ "problem finding signedData component");
+ goto loser;
+ }
+
+ /* if we have a content file, but no digests for this signedData */
+ if (decodeOptions->contentFile != NULL && !NSS_CMSSignedData_HasDigests(sigd)) {
+ if ((poolp = PORT_NewArena(1024)) == NULL) {
+ fprintf(stderr, "cmsutil: Out of memory.\n");
+ goto loser;
+ }
+ digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
+ if (DigestFile (poolp, &digests, item, digestalgs)
+ != SECSuccess) {
+ SECU_PrintError(progName,
+ "problem computing message digest");
+ goto loser;
+ }
+ if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) != SECSuccess) {
+
+ SECU_PrintError(progName,
+ "problem setting message digests");
+ goto loser;
+ }
+ PORT_FreeArena(poolp, PR_FALSE);
+ }
+
+ /* import the certificates */
+ if (NSS_CMSSignedData_ImportCerts(sigd,
+ decodeOptions->options->certHandle,
+ decodeOptions->options->certUsage,
+ PR_FALSE)
+ != SECSuccess) {
+ SECU_PrintError(progName, "cert import failed");
+ goto loser;
+ }
+
+ /* find out about signers */
+ nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
+ if (decodeOptions->headerLevel >= 0)
+ fprintf(out, "nsigners=%d; ", nsigners);
+ if (nsigners == 0) {
+ /* must be a cert transport message */
+ SECStatus rv;
+ /* XXX workaround for bug #54014 */
+ NSS_CMSSignedData_ImportCerts(sigd,
+ decodeOptions->options->certHandle,
+ decodeOptions->options->certUsage,
+ PR_TRUE);
+ rv = NSS_CMSSignedData_VerifyCertsOnly(sigd,
+ decodeOptions->options->certHandle,
+ decodeOptions->options->certUsage);
+ if (rv != SECSuccess) {
+ fprintf(stderr, "cmsutil: Verify certs-only failed!\n");
+ goto loser;
+ }
+ return cmsg;
+ }
+
+ /* still no digests? */
+ if (!NSS_CMSSignedData_HasDigests(sigd)) {
+ SECU_PrintError(progName, "no message digests");
+ goto loser;
+ }
+
+ for (j = 0; j < nsigners; j++) {
+ si = NSS_CMSSignedData_GetSignerInfo(sigd, j);
+ signercn = NSS_CMSSignerInfo_GetSignerCommonName(si);
+ if (signercn == NULL)
+ signercn = "";
+ if (decodeOptions->headerLevel >= 0)
+ fprintf(out, "\n\t\tsigner%d.id=\"%s\"; ", j, signercn);
+ (void)NSS_CMSSignedData_VerifySignerInfo(sigd, j,
+ decodeOptions->options->certHandle,
+ decodeOptions->options->certUsage);
+ if (decodeOptions->headerLevel >= 0)
+ fprintf(out, "signer%d.status=%s; ", j,
+ NSS_CMSUtil_VerificationStatusToString(
+ NSS_CMSSignerInfo_GetVerificationStatus(si)));
+ /* XXX what do we do if we don't print headers? */
+ }
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ if (decodeOptions->headerLevel >= 0)
+ fprintf(out, "type=envelopedData; ");
+ envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo);
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ if (decodeOptions->headerLevel >= 0)
+ fprintf(out, "type=encryptedData; ");
+ encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo);
+ break;
+ case SEC_OID_PKCS7_DATA:
+ if (decodeOptions->headerLevel >= 0)
+ fprintf(out, "type=data; ");
+ break;
+ default:
+ break;
+ }
+ if (decodeOptions->headerLevel >= 0)
+ fprintf(out, "\n");
+ }
+
+ if (!decodeOptions->suppressContent) {
+ if (!decodeOptions->contentFile)
+ item = NSS_CMSMessage_GetContent(cmsg);
+ SECITEM_CopyItem(NULL, output, item);
+ }
+
+ return cmsg;
+loser:
+ if (cmsg)
+ NSS_CMSMessage_Destroy(cmsg);
+ return NULL;
+}
+
+/* example of a callback function to use with encoder */
+/*
+static void
+writeout(void *arg, const char *buf, unsigned long len)
+{
+ FILE *f = (FILE *)arg;
+
+ if (f != NULL && buf != NULL)
+ (void)fwrite(buf, len, 1, f);
+}
+*/
+
+static NSSCMSMessage *
+signed_data(struct signOptionsStr *signOptions)
+{
+ NSSCMSMessage *cmsg = NULL;
+ NSSCMSContentInfo *cinfo;
+ NSSCMSSignedData *sigd;
+ NSSCMSSignerInfo *signerinfo;
+ CERTCertificate *cert= NULL, *ekpcert = NULL;
+
+ if (cms_verbose) {
+ fprintf(stderr, "Input to signed_data:\n");
+ if (signOptions->options->password)
+ fprintf(stderr, "password [%s]\n", signOptions->options->password);
+ else
+ fprintf(stderr, "password [NULL]\n");
+ fprintf(stderr, "certUsage [%d]\n", signOptions->options->certUsage);
+ if (signOptions->options->certHandle)
+ fprintf(stderr, "certdb [%p]\n", signOptions->options->certHandle);
+ else
+ fprintf(stderr, "certdb [NULL]\n");
+ if (signOptions->nickname)
+ fprintf(stderr, "nickname [%s]\n", signOptions->nickname);
+ else
+ fprintf(stderr, "nickname [NULL]\n");
+ }
+ if (signOptions->nickname == NULL) {
+ fprintf(stderr,
+ "ERROR: please indicate the nickname of a certificate to sign with.\n");
+ return NULL;
+ }
+ if ((cert = CERT_FindUserCertByUsage(signOptions->options->certHandle,
+ signOptions->nickname,
+ certUsageEmailSigner,
+ PR_FALSE,
+ NULL)) == NULL) {
+ SECU_PrintError(progName,
+ "the corresponding cert for key \"%s\" does not exist",
+ signOptions->nickname);
+ return NULL;
+ }
+ if (cms_verbose) {
+ fprintf(stderr, "Found certificate for %s\n", signOptions->nickname);
+ }
+ /*
+ * create the message object
+ */
+ cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
+ if (cmsg == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS message.\n");
+ return NULL;
+ }
+ /*
+ * build chain of objects: message->signedData->data
+ */
+ if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS signedData object.\n");
+ goto loser;
+ }
+ cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
+ if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd)
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n");
+ goto loser;
+ }
+ cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
+ /* we're always passing data in and detaching optionally */
+ if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL,
+ signOptions->detached)
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
+ goto loser;
+ }
+ /*
+ * create & attach signer information
+ */
+ if ((signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, SEC_OID_SHA1))
+ == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n");
+ goto loser;
+ }
+ if (cms_verbose) {
+ fprintf(stderr,
+ "Created CMS message, added signed data w/ signerinfo\n");
+ }
+ /* we want the cert chain included for this one */
+ if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain,
+ signOptions->options->certUsage)
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot find cert chain.\n");
+ goto loser;
+ }
+ if (cms_verbose) {
+ fprintf(stderr, "imported certificate\n");
+ }
+ if (signOptions->signingTime) {
+ if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now())
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot add signingTime attribute.\n");
+ goto loser;
+ }
+ }
+ if (signOptions->smimeProfile) {
+ if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot add SMIMECaps attribute.\n");
+ goto loser;
+ }
+ }
+
+ if (signOptions->encryptionKeyPreferenceNick) {
+ /* get the cert, add it to the message */
+ if ((ekpcert = CERT_FindUserCertByUsage(
+ signOptions->options->certHandle,
+ signOptions->encryptionKeyPreferenceNick,
+ certUsageEmailRecipient, PR_FALSE, NULL))
+ == NULL) {
+ SECU_PrintError(progName,
+ "the corresponding cert for key \"%s\" does not exist",
+ signOptions->encryptionKeyPreferenceNick);
+ goto loser;
+ }
+ if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert,
+ signOptions->options->certHandle)
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n");
+ goto loser;
+ }
+ if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot add encryption certificate.\n");
+ goto loser;
+ }
+ } else {
+ /* check signing cert for fitness as encryption cert */
+ SECStatus FitForEncrypt = CERT_CheckCertUsage(cert,
+ certUsageEmailRecipient);
+
+ if (SECSuccess == FitForEncrypt) {
+ /* if yes, add signing cert as EncryptionKeyPreference */
+ if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, cert,
+ signOptions->options->certHandle)
+ != SECSuccess) {
+ fprintf(stderr,
+ "ERROR: cannot add default SMIMEEncKeyPrefs attribute.\n");
+ goto loser;
+ }
+ } else {
+ /* this is a dual-key cert case, we need to look for the encryption
+ certificate under the same nickname as the signing cert */
+ /* get the cert, add it to the message */
+ if ((ekpcert = CERT_FindUserCertByUsage(
+ signOptions->options->certHandle,
+ signOptions->nickname,
+ certUsageEmailRecipient,
+ PR_FALSE,
+ NULL)) == NULL) {
+ SECU_PrintError(progName,
+ "the corresponding cert for key \"%s\" does not exist",
+ signOptions->encryptionKeyPreferenceNick);
+ goto loser;
+ }
+ if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert,
+ signOptions->options->certHandle)
+ != SECSuccess) {
+ fprintf(stderr,
+ "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n");
+ goto loser;
+ }
+ if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot add encryption certificate.\n");
+ goto loser;
+ }
+ }
+ }
+
+ if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n");
+ goto loser;
+ }
+ if (cms_verbose) {
+ fprintf(stderr, "created signed-date message\n");
+ }
+ if (ekpcert) {
+ CERT_DestroyCertificate(ekpcert);
+ }
+ if (cert) {
+ CERT_DestroyCertificate(cert);
+ }
+ return cmsg;
+loser:
+ if (ekpcert) {
+ CERT_DestroyCertificate(ekpcert);
+ }
+ if (cert) {
+ CERT_DestroyCertificate(cert);
+ }
+ NSS_CMSMessage_Destroy(cmsg);
+ return NULL;
+}
+
+static NSSCMSMessage *
+enveloped_data(struct envelopeOptionsStr *envelopeOptions)
+{
+ NSSCMSMessage *cmsg = NULL;
+ NSSCMSContentInfo *cinfo;
+ NSSCMSEnvelopedData *envd;
+ NSSCMSRecipientInfo *recipientinfo;
+ CERTCertificate **recipientcerts = NULL;
+ CERTCertDBHandle *dbhandle;
+ PLArenaPool *tmppoolp = NULL;
+ SECOidTag bulkalgtag;
+ int keysize, i = 0;
+ int cnt;
+ dbhandle = envelopeOptions->options->certHandle;
+ /* count the recipients */
+ if ((cnt = nss_CMSArray_Count((void **)envelopeOptions->recipients)) == 0) {
+ fprintf(stderr, "ERROR: please name at least one recipient.\n");
+ goto loser;
+ }
+ if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
+ fprintf(stderr, "ERROR: out of memory.\n");
+ goto loser;
+ }
+ /* XXX find the recipient's certs by email address or nickname */
+ if ((recipientcerts =
+ (CERTCertificate **)PORT_ArenaZAlloc(tmppoolp,
+ (cnt+1)*sizeof(CERTCertificate*)))
+ == NULL) {
+ fprintf(stderr, "ERROR: out of memory.\n");
+ goto loser;
+ }
+ for (i=0; envelopeOptions->recipients[i] != NULL; i++) {
+ if ((recipientcerts[i] =
+ CERT_FindCertByNicknameOrEmailAddr(dbhandle,
+ envelopeOptions->recipients[i]))
+ == NULL) {
+ SECU_PrintError(progName, "cannot find certificate for \"%s\"",
+ envelopeOptions->recipients[i]);
+ i=0;
+ goto loser;
+ }
+ }
+ recipientcerts[i] = NULL;
+ i=0;
+ /* find a nice bulk algorithm */
+ if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientcerts, &bulkalgtag,
+ &keysize) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot find common bulk algorithm.\n");
+ goto loser;
+ }
+ /*
+ * create the message object
+ */
+ cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
+ if (cmsg == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS message.\n");
+ goto loser;
+ }
+ /*
+ * build chain of objects: message->envelopedData->data
+ */
+ if ((envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, keysize))
+ == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS envelopedData object.\n");
+ goto loser;
+ }
+ cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
+ if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd)
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS envelopedData object.\n");
+ goto loser;
+ }
+ cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd);
+ /* we're always passing data in, so the content is NULL */
+ if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE)
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
+ goto loser;
+ }
+ /*
+ * create & attach recipient information
+ */
+ for (i = 0; recipientcerts[i] != NULL; i++) {
+ if ((recipientinfo = NSS_CMSRecipientInfo_Create(cmsg,
+ recipientcerts[i]))
+ == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS recipientInfo object.\n");
+ goto loser;
+ }
+ if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientinfo)
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot add CMS recipientInfo object.\n");
+ goto loser;
+ }
+ CERT_DestroyCertificate(recipientcerts[i]);
+ }
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
+ return cmsg;
+loser:
+ if (recipientcerts) {
+ for (; recipientcerts[i] != NULL; i++) {
+ CERT_DestroyCertificate(recipientcerts[i]);
+ }
+ }
+ if (cmsg)
+ NSS_CMSMessage_Destroy(cmsg);
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
+ return NULL;
+}
+
+PK11SymKey *dkcb(void *arg, SECAlgorithmID *algid)
+{
+ return (PK11SymKey*)arg;
+}
+
+static SECStatus
+get_enc_params(struct encryptOptionsStr *encryptOptions)
+{
+ struct envelopeOptionsStr envelopeOptions;
+ SECStatus rv = SECFailure;
+ NSSCMSMessage *env_cmsg;
+ NSSCMSContentInfo *cinfo;
+ PK11SymKey *bulkkey = NULL;
+ SECOidTag bulkalgtag;
+ int keysize;
+ int i, nlevels;
+ /*
+ * construct an enveloped data message to obtain bulk keys
+ */
+ if (encryptOptions->envmsg) {
+ env_cmsg = encryptOptions->envmsg; /* get it from an old message */
+ } else {
+ SECItem dummyOut = { 0, 0, 0 };
+ SECItem dummyIn = { 0, 0, 0 };
+ char str[] = "Hello!";
+ PLArenaPool *tmparena = PORT_NewArena(1024);
+ dummyIn.data = (unsigned char *)str;
+ dummyIn.len = strlen(str);
+ envelopeOptions.options = encryptOptions->options;
+ envelopeOptions.recipients = encryptOptions->recipients;
+ env_cmsg = enveloped_data(&envelopeOptions);
+ NSS_CMSDEREncode(env_cmsg, &dummyIn, &dummyOut, tmparena);
+ PR_Write(encryptOptions->envFile, dummyOut.data, dummyOut.len);
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ /*
+ * get the content info for the enveloped data
+ */
+ nlevels = NSS_CMSMessage_ContentLevelCount(env_cmsg);
+ for (i = 0; i < nlevels; i++) {
+ SECOidTag typetag;
+ cinfo = NSS_CMSMessage_ContentLevel(env_cmsg, i);
+ typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+ if (typetag == SEC_OID_PKCS7_DATA) {
+ /*
+ * get the symmetric key
+ */
+ bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo);
+ keysize = NSS_CMSContentInfo_GetBulkKeySize(cinfo);
+ bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo);
+ break;
+ }
+ }
+ if (i == nlevels) {
+ fprintf(stderr, "%s: could not retrieve enveloped data.", progName);
+ goto loser;
+ }
+ encryptOptions->bulkalgtag = bulkalgtag;
+ encryptOptions->bulkkey = bulkkey;
+ encryptOptions->keysize = keysize;
+ rv = SECSuccess;
+loser:
+ if (env_cmsg)
+ NSS_CMSMessage_Destroy(env_cmsg);
+ return rv;
+}
+
+static NSSCMSMessage *
+encrypted_data(struct encryptOptionsStr *encryptOptions)
+{
+ SECStatus rv = SECFailure;
+ NSSCMSMessage *cmsg = NULL;
+ NSSCMSContentInfo *cinfo;
+ NSSCMSEncryptedData *encd;
+ NSSCMSEncoderContext *ecx = NULL;
+ PLArenaPool *tmppoolp = NULL;
+ SECItem derOut = { 0, 0, 0 };
+ /* arena for output */
+ tmppoolp = PORT_NewArena(1024);
+ if (!tmppoolp) {
+ fprintf(stderr, "%s: out of memory.\n", progName);
+ return NULL;
+ }
+ /*
+ * create the message object
+ */
+ cmsg = NSS_CMSMessage_Create(NULL);
+ if (cmsg == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS message.\n");
+ goto loser;
+ }
+ /*
+ * build chain of objects: message->encryptedData->data
+ */
+ if ((encd = NSS_CMSEncryptedData_Create(cmsg, encryptOptions->bulkalgtag,
+ encryptOptions->keysize))
+ == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS encryptedData object.\n");
+ goto loser;
+ }
+ cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
+ if (NSS_CMSContentInfo_SetContent_EncryptedData(cmsg, cinfo, encd)
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS encryptedData object.\n");
+ goto loser;
+ }
+ cinfo = NSS_CMSEncryptedData_GetContentInfo(encd);
+ /* we're always passing data in, so the content is NULL */
+ if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE)
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
+ goto loser;
+ }
+ ecx = NSS_CMSEncoder_Start(cmsg, NULL, NULL, &derOut, tmppoolp, NULL, NULL,
+ dkcb, encryptOptions->bulkkey, NULL, NULL);
+ if (!ecx) {
+ fprintf(stderr, "%s: cannot create encoder context.\n", progName);
+ goto loser;
+ }
+ rv = NSS_CMSEncoder_Update(ecx, (char *)encryptOptions->input->data,
+ encryptOptions->input->len);
+ if (rv) {
+ fprintf(stderr, "%s: failed to add data to encoder.\n", progName);
+ goto loser;
+ }
+ rv = NSS_CMSEncoder_Finish(ecx);
+ if (rv) {
+ fprintf(stderr, "%s: failed to encrypt data.\n", progName);
+ goto loser;
+ }
+ fwrite(derOut.data, derOut.len, 1, encryptOptions->outfile);
+ /*
+ if (bulkkey)
+ PK11_FreeSymKey(bulkkey);
+ */
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
+ return cmsg;
+loser:
+ /*
+ if (bulkkey)
+ PK11_FreeSymKey(bulkkey);
+ */
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
+ if (cmsg)
+ NSS_CMSMessage_Destroy(cmsg);
+ return NULL;
+}
+
+static NSSCMSMessage *
+signed_data_certsonly(struct certsonlyOptionsStr *certsonlyOptions)
+{
+ NSSCMSMessage *cmsg = NULL;
+ NSSCMSContentInfo *cinfo;
+ NSSCMSSignedData *sigd;
+ CERTCertificate **certs = NULL;
+ CERTCertDBHandle *dbhandle;
+ PLArenaPool *tmppoolp = NULL;
+ int i = 0, cnt;
+ dbhandle = certsonlyOptions->options->certHandle;
+ if ((cnt = nss_CMSArray_Count((void**)certsonlyOptions->recipients)) == 0) {
+ fprintf(stderr,
+ "ERROR: please indicate the nickname of a certificate to sign with.\n");
+ goto loser;
+ }
+ if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
+ fprintf(stderr, "ERROR: out of memory.\n");
+ goto loser;
+ }
+ if ((certs =
+ (CERTCertificate **)PORT_ArenaZAlloc(tmppoolp,
+ (cnt+1)*sizeof(CERTCertificate*)))
+ == NULL) {
+ fprintf(stderr, "ERROR: out of memory.\n");
+ goto loser;
+ }
+ for (i=0; certsonlyOptions->recipients[i] != NULL; i++) {
+ if ((certs[i] =
+ CERT_FindCertByNicknameOrEmailAddr(dbhandle,
+ certsonlyOptions->recipients[i]))
+ == NULL) {
+ SECU_PrintError(progName, "cannot find certificate for \"%s\"",
+ certsonlyOptions->recipients[i]);
+ i=0;
+ goto loser;
+ }
+ }
+ certs[i] = NULL;
+ i=0;
+ /*
+ * create the message object
+ */
+ cmsg = NSS_CMSMessage_Create(NULL);
+ if (cmsg == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS message.\n");
+ goto loser;
+ }
+ /*
+ * build chain of objects: message->signedData->data
+ */
+ if ((sigd = NSS_CMSSignedData_CreateCertsOnly(cmsg, certs[0], PR_TRUE))
+ == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS signedData object.\n");
+ goto loser;
+ }
+ CERT_DestroyCertificate(certs[0]);
+ for (i=1; i<cnt; i++) {
+ if (NSS_CMSSignedData_AddCertChain(sigd, certs[i])) {
+ fprintf(stderr, "ERROR: cannot add cert chain for \"%s\".\n",
+ certsonlyOptions->recipients[i]);
+ goto loser;
+ }
+ CERT_DestroyCertificate(certs[i]);
+ }
+ cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
+ if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd)
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n");
+ goto loser;
+ }
+ cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
+ if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE)
+ != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
+ goto loser;
+ }
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
+ return cmsg;
+loser:
+ if (certs) {
+ for (; i<cnt; i++) {
+ CERT_DestroyCertificate(certs[i]);
+ }
+ }
+ if (cmsg)
+ NSS_CMSMessage_Destroy(cmsg);
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
+ return NULL;
+}
+
+typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT, ENVELOPE, CERTSONLY } Mode;
+
+#if 0
+void
+parse_message_for_recipients(PRFileDesc *inFile,
+ struct envelopeOptionsStr *envelopeOptions)
+{
+ SECItem filedata;
+ SECStatus rv;
+ rv = SECU_FileToItem(&filedata, inFile);
+}
+#endif
+
+int
+main(int argc, char **argv)
+{
+ FILE *outFile;
+ NSSCMSMessage *cmsg = NULL;
+ PRFileDesc *inFile;
+ PLOptState *optstate;
+ PLOptStatus status;
+ Mode mode = UNKNOWN;
+ PK11PasswordFunc pwcb;
+ void *pwcb_arg;
+ struct decodeOptionsStr decodeOptions = { 0 };
+ struct signOptionsStr signOptions = { 0 };
+ struct envelopeOptionsStr envelopeOptions = { 0 };
+ struct certsonlyOptionsStr certsonlyOptions = { 0 };
+ struct encryptOptionsStr encryptOptions = { 0 };
+ struct optionsStr options = { 0 };
+ int exitstatus;
+ static char *ptrarray[128] = { 0 };
+ int nrecipients = 0;
+ char *str, *tok;
+ char *envFileName;
+ SECItem input = { 0, 0, 0};
+ SECItem output = { 0, 0, 0};
+ SECItem dummy = { 0, 0, 0 };
+ SECItem envmsg = { 0, 0, 0 };
+ SECStatus rv;
+
+ progName = strrchr(argv[0], '/');
+ progName = progName ? progName+1 : argv[0];
+
+ inFile = PR_STDIN;
+ outFile = stdout;
+ envFileName = NULL;
+ mode = UNKNOWN;
+ decodeOptions.contentFile = NULL;
+ decodeOptions.suppressContent = PR_FALSE;
+ decodeOptions.headerLevel = -1;
+ options.certUsage = certUsageEmailSigner;
+ options.password = NULL;
+ signOptions.nickname = NULL;
+ signOptions.detached = PR_FALSE;
+ signOptions.signingTime = PR_FALSE;
+ signOptions.smimeProfile = PR_FALSE;
+ signOptions.encryptionKeyPreferenceNick = NULL;
+ envelopeOptions.recipients = NULL;
+ encryptOptions.recipients = NULL;
+ encryptOptions.envmsg = NULL;
+ encryptOptions.envFile = NULL;
+ encryptOptions.bulkalgtag = SEC_OID_UNKNOWN;
+ encryptOptions.bulkkey = NULL;
+ encryptOptions.keysize = -1;
+
+ /*
+ * Parse command line arguments
+ */
+ optstate = PL_CreateOptState(argc, argv,
+ "CDSEOnN:TGPYv:h:p:i:c:d:e:o:s:u:r:");
+ while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
+ switch (optstate->option) {
+ case '?':
+ Usage(progName);
+ break;
+
+ case 'C':
+ mode = ENCRYPT;
+ break;
+ case 'D':
+ mode = DECODE;
+ break;
+ case 'S':
+ mode = SIGN;
+ break;
+ case 'E':
+ mode = ENVELOPE;
+ break;
+ case 'O':
+ mode = CERTSONLY;
+ break;
+ case 'v':
+ cms_verbose = 1;
+ break;
+
+ case 'n':
+ if (mode != DECODE) {
+ fprintf(stderr,
+ "%s: option -n only supported with option -D.\n",
+ progName);
+ Usage(progName);
+ exit(1);
+ }
+ decodeOptions.suppressContent = PR_TRUE;
+ break;
+
+ case 'N':
+ if (mode != SIGN) {
+ fprintf(stderr,
+ "%s: option -N only supported with option -S.\n",
+ progName);
+ Usage(progName);
+ exit(1);
+ }
+ signOptions.nickname = strdup(optstate->value);
+ break;
+
+ case 'Y':
+ if (mode != SIGN) {
+ fprintf(stderr,
+ "%s: option -Y only supported with option -S.\n",
+ progName);
+ Usage(progName);
+ exit(1);
+ }
+ signOptions.encryptionKeyPreferenceNick = strdup(optstate->value);
+ break;
+
+ case 'T':
+ if (mode != SIGN) {
+ fprintf(stderr,
+ "%s: option -T only supported with option -S.\n",
+ progName);
+ Usage(progName);
+ exit(1);
+ }
+ signOptions.detached = PR_TRUE;
+ break;
+
+ case 'G':
+ if (mode != SIGN) {
+ fprintf(stderr,
+ "%s: option -G only supported with option -S.\n",
+ progName);
+ Usage(progName);
+ exit(1);
+ }
+ signOptions.signingTime = PR_TRUE;
+ break;
+
+ case 'P':
+ if (mode != SIGN) {
+ fprintf(stderr,
+ "%s: option -P only supported with option -S.\n",
+ progName);
+ Usage(progName);
+ exit(1);
+ }
+ signOptions.smimeProfile = PR_TRUE;
+ break;
+
+ case 'h':
+ if (mode != DECODE) {
+ fprintf(stderr,
+ "%s: option -h only supported with option -D.\n",
+ progName);
+ Usage(progName);
+ exit(1);
+ }
+ decodeOptions.headerLevel = atoi(optstate->value);
+ if (decodeOptions.headerLevel < 0) {
+ fprintf(stderr, "option -h cannot have a negative value.\n");
+ exit(1);
+ }
+ break;
+
+ case 'p':
+ if (!optstate->value) {
+ fprintf(stderr, "%s: option -p must have a value.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+
+ options.password = strdup(optstate->value);
+ break;
+
+ case 'i':
+ inFile = PR_Open(optstate->value, PR_RDONLY, 00660);
+ if (inFile == NULL) {
+ fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
+ progName, optstate->value);
+ exit(1);
+ }
+ break;
+
+ case 'c':
+ if (mode != DECODE) {
+ fprintf(stderr,
+ "%s: option -c only supported with option -D.\n",
+ progName);
+ Usage(progName);
+ exit(1);
+ }
+ if ((decodeOptions.contentFile =
+ PR_Open(optstate->value, PR_RDONLY, 006600)) == NULL) {
+ fprintf(stderr, "%s: unable to open \"%s\" for reading.\n",
+ progName, optstate->value);
+ exit(1);
+ }
+ break;
+
+ case 'o':
+#if 0
+ if (mode == DECODE) {
+ outFile = fopen(optstate->value, "w");
+ } else {
+ outFile = fopen(optstate->value, "wb");
+ }
+#endif
+ outFile = fopen(optstate->value, "wb");
+ if (outFile == NULL) {
+ fprintf(stderr, "%s: unable to open \"%s\" for writing\n",
+ progName, optstate->value);
+ exit(1);
+ }
+ break;
+
+ case 'r':
+ if (!optstate->value) {
+ fprintf(stderr, "%s: option -r must have a value.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+#if 0
+ fprintf(stderr, "recipient = %s\n", optstate->value);
+#endif
+ envelopeOptions.recipients = ptrarray;
+ str = (char *)optstate->value;
+ do {
+ tok = strchr(str, ',');
+ if (tok) *tok = '\0';
+ envelopeOptions.recipients[nrecipients++] = strdup(str);
+ if (tok) str = tok + 1;
+ } while (tok);
+ envelopeOptions.recipients[nrecipients] = NULL;
+ encryptOptions.recipients = envelopeOptions.recipients;
+ certsonlyOptions.recipients = envelopeOptions.recipients;
+ break;
+
+ case 'd':
+ SECU_ConfigDirectory(optstate->value);
+ break;
+
+ case 'e':
+ envFileName = strdup(optstate->value);
+ encryptOptions.envFile = PR_Open(envFileName, PR_RDONLY, 00660);
+ break;
+
+ case 'u': {
+ int usageType;
+
+ usageType = atoi (strdup(optstate->value));
+ if (usageType < certUsageSSLClient || usageType > certUsageAnyCA)
+ return -1;
+ options.certUsage = (SECCertUsage)usageType;
+ break;
+ }
+
+ }
+ }
+
+ if (mode == UNKNOWN)
+ Usage(progName);
+
+ if (mode != CERTSONLY)
+ SECU_FileToItem(&input, inFile);
+ if (inFile != PR_STDIN)
+ PR_Close(inFile);
+ if (cms_verbose) {
+ fprintf(stderr, "received commands\n");
+ }
+
+ /* Call the libsec initialization routines */
+ PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+ rv = NSS_InitReadWrite(SECU_ConfigDirectory(NULL));
+ if (SECSuccess != rv) {
+ SECU_PrintError(progName, "NSS_Init failed");
+ exit(1);
+ }
+ if (cms_verbose) {
+ fprintf(stderr, "NSS has been initialized.\n");
+ }
+ options.certHandle = CERT_GetDefaultCertDB();
+ if (!options.certHandle) {
+ SECU_PrintError(progName, "No default cert DB");
+ exit(1);
+ }
+ if (cms_verbose) {
+ fprintf(stderr, "Got default certdb\n");
+ }
+
+#if defined(_WIN32)
+ /*if (outFile == stdout && mode != DECODE) {*/
+ if (outFile == stdout) {
+ /* If we're going to write binary data to stdout, we must put stdout
+ ** into O_BINARY mode or else outgoing \n's will become \r\n's.
+ */
+ int smrv = _setmode(_fileno(stdout), _O_BINARY);
+ if (smrv == -1) {
+ fprintf(stderr,
+ "%s: Cannot change stdout to binary mode. Use -o option instead.\n",
+ progName);
+ return smrv;
+ }
+ }
+#endif
+
+ exitstatus = 0;
+ switch (mode) {
+ case DECODE:
+ decodeOptions.options = &options;
+ if (encryptOptions.envFile) {
+ /* Decoding encrypted-data, so get the bulkkey from an
+ * enveloped-data message.
+ */
+ SECU_FileToItem(&envmsg, encryptOptions.envFile);
+ decodeOptions.options = &options;
+ encryptOptions.envmsg = decode(NULL, &dummy, &envmsg,
+ &decodeOptions);
+ if (!encryptOptions.envmsg) {
+ SECU_PrintError(progName, "problem decoding env msg");
+ exitstatus = 1;
+ break;
+ }
+ rv = get_enc_params(&encryptOptions);
+ decodeOptions.dkcb = dkcb;
+ decodeOptions.bulkkey = encryptOptions.bulkkey;
+ }
+ cmsg = decode(outFile, &output, &input, &decodeOptions);
+ if (!cmsg) {
+ SECU_PrintError(progName, "problem decoding");
+ exitstatus = 1;
+ }
+ fwrite(output.data, output.len, 1, outFile);
+ break;
+ case SIGN:
+ signOptions.options = &options;
+ cmsg = signed_data(&signOptions);
+ if (!cmsg) {
+ SECU_PrintError(progName, "problem signing");
+ exitstatus = 1;
+ }
+ break;
+ case ENCRYPT:
+ if (!envFileName) {
+ fprintf(stderr, "%s: you must specify an envelope file with -e.\n",
+ progName);
+ exit(1);
+ }
+ encryptOptions.options = &options;
+ encryptOptions.input = &input;
+ encryptOptions.outfile = outFile;
+ if (!encryptOptions.envFile) {
+ encryptOptions.envFile = PR_Open(envFileName,
+ PR_WRONLY|PR_CREATE_FILE, 00660);
+ if (!encryptOptions.envFile) {
+ fprintf(stderr, "%s: failed to create file %s.\n", progName,
+ envFileName);
+ exit(1);
+ }
+ } else {
+ SECU_FileToItem(&envmsg, encryptOptions.envFile);
+ decodeOptions.options = &options;
+ encryptOptions.envmsg = decode(NULL, &dummy, &envmsg,
+ &decodeOptions);
+ if (encryptOptions.envmsg == NULL) {
+ SECU_PrintError(progName, "problem decrypting env msg");
+ exitstatus = 1;
+ break;
+ }
+ }
+ /* decode an enveloped-data message to get the bulkkey (create
+ * a new one if neccessary)
+ */
+ rv = get_enc_params(&encryptOptions);
+ /* create the encrypted-data message */
+ cmsg = encrypted_data(&encryptOptions);
+ if (!cmsg) {
+ SECU_PrintError(progName, "problem encrypting");
+ exitstatus = 1;
+ }
+ if (encryptOptions.bulkkey) {
+ PK11_FreeSymKey(encryptOptions.bulkkey);
+ encryptOptions.bulkkey = NULL;
+ }
+ break;
+ case ENVELOPE:
+ envelopeOptions.options = &options;
+#if 0
+ if (!envelopeOptions.recipients)
+ parse_message_for_recipients(myIn, &envelopeOptions);
+#endif
+ cmsg = enveloped_data(&envelopeOptions);
+ if (!cmsg) {
+ SECU_PrintError(progName, "problem enveloping");
+ exitstatus = 1;
+ }
+ break;
+ case CERTSONLY:
+ certsonlyOptions.options = &options;
+ cmsg = signed_data_certsonly(&certsonlyOptions);
+ if (!cmsg) {
+ SECU_PrintError(progName, "problem with certs-only");
+ exitstatus = 1;
+ }
+ break;
+ default:
+ fprintf(stderr, "One of options -D, -S or -E must be set.\n");
+ Usage(progName);
+ exitstatus = 1;
+ }
+ if ( (mode == SIGN || mode == ENVELOPE || mode == CERTSONLY)
+ && (!exitstatus) ) {
+ PLArenaPool *arena = PORT_NewArena(1024);
+ NSSCMSEncoderContext *ecx;
+ SECItem output = { 0, 0, 0 };
+ if (!arena) {
+ fprintf(stderr, "%s: out of memory.\n", progName);
+ exit(1);
+ }
+ pwcb = (options.password != NULL) ? ownpw : NULL;
+ pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
+ if (cms_verbose) {
+ fprintf(stderr, "cmsg [%p]\n", cmsg);
+ fprintf(stderr, "arena [%p]\n", arena);
+ if (pwcb_arg)
+ fprintf(stderr, "password [%s]\n", (char *)pwcb_arg);
+ else
+ fprintf(stderr, "password [NULL]\n");
+ }
+ ecx = NSS_CMSEncoder_Start(cmsg,
+ NULL, NULL, /* DER output callback */
+ &output, arena, /* destination storage */
+ pwcb, pwcb_arg, /* password callback */
+ NULL, NULL, /* decrypt key callback */
+ NULL, NULL ); /* detached digests */
+ if (!ecx) {
+ fprintf(stderr, "%s: cannot create encoder context.\n", progName);
+ exit(1);
+ }
+ if (cms_verbose) {
+ fprintf(stderr, "input len [%d]\n", input.len);
+ { int j;
+ for(j=0;j<input.len;j++)
+ fprintf(stderr, "%2x%c", input.data[j], (j>0&&j%35==0)?'\n':' ');
+ }
+ }
+ if (input.len > 0) { /* skip if certs-only (or other zero content) */
+ rv = NSS_CMSEncoder_Update(ecx, (char *)input.data, input.len);
+ if (rv) {
+ fprintf(stderr,
+ "%s: failed to add data to encoder.\n", progName);
+ exit(1);
+ }
+ }
+ rv = NSS_CMSEncoder_Finish(ecx);
+ if (rv) {
+ SECU_PrintError(progName, "failed to encode data");
+ exit(1);
+ }
+
+ if (cms_verbose) {
+ fprintf(stderr, "encoding passed\n");
+ }
+ /*PR_Write(output.data, output.len);*/
+ fwrite(output.data, output.len, 1, outFile);
+ if (cms_verbose) {
+ fprintf(stderr, "wrote to file\n");
+ }
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ if (cmsg)
+ NSS_CMSMessage_Destroy(cmsg);
+ if (outFile != stdout)
+ fclose(outFile);
+
+ if (decodeOptions.contentFile)
+ PR_Close(decodeOptions.contentFile);
+ NSS_Shutdown();
+ exit(exitstatus);
+}