mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-28 12:45:27 +00:00
d705ca0332
by itself.
878 lines
26 KiB
C
878 lines
26 KiB
C
/*
|
|
* 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: cmsutil.c,v 1.5 2000/06/23 16:40:30 chrisk%netscape.com Exp $
|
|
*/
|
|
|
|
#include "nspr.h"
|
|
#include "secutil.h"
|
|
#include "plgetopt.h"
|
|
#include "secpkcs7.h"
|
|
#include "cert.h"
|
|
#include "certdb.h"
|
|
#include "cdbhdl.h"
|
|
#include "secoid.h"
|
|
#include "cms.h"
|
|
#include "smime.h"
|
|
|
|
#if defined(XP_UNIX)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
extern void SEC_Init(void); /* XXX */
|
|
|
|
static SECStatus
|
|
DigestFile(PLArenaPool *poolp, SECItem ***digests, FILE *inFile, SECAlgorithmID **algids)
|
|
{
|
|
NSSCMSDigestContext *digcx;
|
|
int nb;
|
|
char ibuf[4096];
|
|
SECStatus rv;
|
|
|
|
digcx = NSS_CMSDigestContext_StartMultiple(algids);
|
|
if (digcx == NULL)
|
|
return SECFailure;
|
|
|
|
for (;;) {
|
|
if (feof(inFile)) break;
|
|
nb = fread(ibuf, 1, sizeof(ibuf), inFile);
|
|
if (nb == 0) {
|
|
if (ferror(inFile)) {
|
|
PORT_SetError(SEC_ERROR_IO);
|
|
NSS_CMSDigestContext_Cancel(digcx);
|
|
return SECFailure;
|
|
}
|
|
/* eof */
|
|
break;
|
|
}
|
|
NSS_CMSDigestContext_Update(digcx, (const unsigned char *)ibuf, nb);
|
|
}
|
|
|
|
rv = NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests);
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
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, "\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);
|
|
}
|
|
|
|
static CERTCertDBHandle certHandleStatic; /* avoid having to allocate */
|
|
|
|
static CERTCertDBHandle *
|
|
OpenCertDB(char *progName)
|
|
{
|
|
CERTCertDBHandle *certHandle;
|
|
SECStatus rv;
|
|
|
|
certHandle = &certHandleStatic;
|
|
rv = CERT_OpenCertDB(certHandle, PR_FALSE, SECU_CertDBNameCallback, NULL);
|
|
if (rv != SECSuccess) {
|
|
SECU_PrintError(progName, "could not open cert database");
|
|
return NULL;
|
|
}
|
|
|
|
return certHandle;
|
|
}
|
|
|
|
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 {
|
|
FILE *contentFile;
|
|
int headerLevel;
|
|
PRBool suppressContent;
|
|
};
|
|
|
|
struct signOptionsStr {
|
|
char *nickname;
|
|
char *encryptionKeyPreferenceNick;
|
|
PRBool signingTime;
|
|
PRBool smimeProfile;
|
|
PRBool detached;
|
|
};
|
|
|
|
struct envelopeOptionsStr {
|
|
char **recipients;
|
|
};
|
|
|
|
static int
|
|
decode(FILE *out, FILE *infile, char *progName, struct optionsStr options, struct decodeOptionsStr decodeOptions)
|
|
{
|
|
NSSCMSDecoderContext *dcx;
|
|
NSSCMSMessage *cmsg;
|
|
NSSCMSContentInfo *cinfo;
|
|
NSSCMSSignedData *sigd = NULL;
|
|
NSSCMSEnvelopedData *envd;
|
|
SECAlgorithmID **digestalgs;
|
|
unsigned char buffer[32];
|
|
int nlevels, i, nsigners, j;
|
|
char *signercn;
|
|
NSSCMSSignerInfo *si;
|
|
SECOidTag typetag;
|
|
SECItem **digests;
|
|
PLArenaPool *poolp;
|
|
int nb;
|
|
char ibuf[4096];
|
|
PK11PasswordFunc pwcb;
|
|
void *pwcb_arg;
|
|
SECItem *item;
|
|
|
|
pwcb = (options.password != NULL) ? ownpw : NULL;
|
|
pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
|
|
|
|
dcx = NSS_CMSDecoder_Start(NULL,
|
|
NULL, NULL, /* content callback */
|
|
pwcb, pwcb_arg, /* password callback */
|
|
NULL, NULL); /* decrypt key callback */
|
|
|
|
for (;;) {
|
|
if (feof(infile)) break;
|
|
nb = fread(ibuf, 1, sizeof(ibuf), infile);
|
|
if (nb == 0) {
|
|
if (ferror(infile)) {
|
|
fprintf(stderr, "ERROR: file i/o error.\n");
|
|
NSS_CMSDecoder_Cancel(dcx);
|
|
return SECFailure;
|
|
}
|
|
/* eof */
|
|
break;
|
|
}
|
|
(void)NSS_CMSDecoder_Update(dcx, (const char *)ibuf, nb);
|
|
}
|
|
cmsg = NSS_CMSDecoder_Finish(dcx);
|
|
if (cmsg == NULL)
|
|
return -1;
|
|
|
|
if (decodeOptions.headerLevel >= 0) {
|
|
fprintf(out, "SMIME: ", decodeOptions.headerLevel, i);
|
|
}
|
|
|
|
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");
|
|
return -1;
|
|
}
|
|
|
|
/* 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, "Out of memory.\n");
|
|
return -1;
|
|
}
|
|
digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
|
|
if (DigestFile (poolp, &digests, decodeOptions.contentFile, digestalgs) != SECSuccess) {
|
|
SECU_PrintError(progName, "problem computing message digest");
|
|
return -1;
|
|
}
|
|
if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) != SECSuccess) {
|
|
|
|
SECU_PrintError(progName, "problem setting message digests");
|
|
return -1;
|
|
}
|
|
PORT_FreeArena(poolp, PR_FALSE);
|
|
}
|
|
|
|
/* still no digests? */
|
|
if (!NSS_CMSSignedData_HasDigests(sigd)) {
|
|
SECU_PrintError(progName, "no message digests");
|
|
return -1;
|
|
}
|
|
|
|
/* 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 */
|
|
} else {
|
|
/* import the certificates */
|
|
if (NSS_CMSSignedData_ImportCerts(sigd, options.certHandle, options.certUsage, PR_FALSE) != SECSuccess) {
|
|
SECU_PrintError(progName, "cert import failed");
|
|
return -1;
|
|
}
|
|
|
|
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, options.certHandle, 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_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) {
|
|
char buffer[4096];
|
|
size_t nbytes;
|
|
/* detached content: print content file */
|
|
fseek(decodeOptions.contentFile, 0, SEEK_SET);
|
|
while ((nbytes = fread(buffer, 1, sizeof(buffer), decodeOptions.contentFile)) != 0) {
|
|
fwrite(buffer, nbytes, 1, out);
|
|
}
|
|
} else {
|
|
if ((item = NSS_CMSMessage_GetContent(cmsg)) != NULL) {
|
|
fwrite(item->data, item->len, 1, out);
|
|
}
|
|
}
|
|
}
|
|
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return 0;
|
|
}
|
|
|
|
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 int
|
|
sign(FILE *out, FILE *infile, char *progName, struct optionsStr options, struct signOptionsStr signOptions)
|
|
{
|
|
NSSCMSEncoderContext *ecx;
|
|
NSSCMSMessage *cmsg;
|
|
NSSCMSContentInfo *cinfo;
|
|
NSSCMSSignedData *sigd;
|
|
NSSCMSSignerInfo *signerinfo;
|
|
int nb;
|
|
char ibuf[4096];
|
|
PK11PasswordFunc pwcb;
|
|
void *pwcb_arg;
|
|
CERTCertificate *cert, *ekpcert;
|
|
|
|
if (signOptions.nickname == NULL) {
|
|
fprintf(stderr, "ERROR: please indicate the nickname of a certificate to sign with.\n");
|
|
return SECFailure;
|
|
}
|
|
|
|
if ((cert = CERT_FindCertByNickname(options.certHandle, signOptions.nickname)) == NULL) {
|
|
SECU_PrintError(progName, "the corresponding cert for key \"%s\" does not exist",
|
|
signOptions.nickname);
|
|
return SECFailure;
|
|
}
|
|
|
|
/*
|
|
* 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 SECFailure;
|
|
}
|
|
/*
|
|
* build chain of objects: message->signedData->data
|
|
*/
|
|
if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) {
|
|
fprintf(stderr, "ERROR: cannot create CMS signedData object.\n");
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
|
|
if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) != SECSuccess) {
|
|
fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n");
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
|
|
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");
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
|
|
/*
|
|
* create & attach signer information
|
|
*/
|
|
if ((signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, SEC_OID_SHA1)) == NULL) {
|
|
fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n");
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
|
|
/* we want the cert chain included for this one */
|
|
if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, options.certUsage) != SECSuccess) {
|
|
fprintf(stderr, "ERROR: cannot find cert chain.\n");
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (signOptions.signingTime) {
|
|
if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) {
|
|
fprintf(stderr, "ERROR: cannot add signingTime attribute.\n");
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
}
|
|
if (signOptions.smimeProfile) {
|
|
if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) {
|
|
fprintf(stderr, "ERROR: cannot add SMIMECaps attribute.\n");
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
}
|
|
if (signOptions.encryptionKeyPreferenceNick) {
|
|
/* get the cert, add it to the message */
|
|
if ((ekpcert = CERT_FindCertByNickname(options.certHandle, signOptions.encryptionKeyPreferenceNick)) == NULL) {
|
|
SECU_PrintError(progName, "the corresponding cert for key \"%s\" does not exist",
|
|
signOptions.encryptionKeyPreferenceNick);
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert, options.certHandle) != SECSuccess) {
|
|
fprintf(stderr, "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n");
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) {
|
|
fprintf(stderr, "ERROR: cannot add encryption certificate.\n");
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
} else {
|
|
/* check signing cert for fitness as encryption cert */
|
|
/* if yes, add signing cert as EncryptionKeyPreference */
|
|
if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, cert, options.certHandle) != SECSuccess) {
|
|
fprintf(stderr, "ERROR: cannot add default SMIMEEncKeyPrefs attribute.\n");
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
}
|
|
|
|
if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
|
|
fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n");
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECFailure;
|
|
}
|
|
|
|
/*
|
|
* do not add signer independent certificates
|
|
*/
|
|
|
|
pwcb = (options.password != NULL) ? ownpw : NULL;
|
|
pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
|
|
|
|
ecx = NSS_CMSEncoder_Start(cmsg,
|
|
writeout, out, /* DER output callback */
|
|
NULL, NULL, /* destination storage */
|
|
pwcb, pwcb_arg, /* password callback */
|
|
NULL, NULL, /* decrypt key callback */
|
|
NULL, NULL); /* detached digests (not used, we feed) */
|
|
|
|
for (;;) {
|
|
if (feof(infile)) break;
|
|
nb = fread(ibuf, 1, sizeof(ibuf), infile);
|
|
if (nb == 0) {
|
|
if (ferror(infile)) {
|
|
fprintf(stderr, "ERROR: file i/o error.\n");
|
|
NSS_CMSEncoder_Cancel(ecx);
|
|
return SECFailure;
|
|
}
|
|
/* eof */
|
|
break;
|
|
}
|
|
(void)NSS_CMSEncoder_Update(ecx, (const char *)ibuf, nb);
|
|
}
|
|
if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
|
|
fprintf(stderr, "ERROR: DER encoding problem.\n");
|
|
return SECFailure;
|
|
}
|
|
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
return SECSuccess;
|
|
}
|
|
|
|
static int
|
|
envelope(FILE *out, FILE *infile, char *progName, struct optionsStr options, struct envelopeOptionsStr envelopeOptions)
|
|
{
|
|
SECStatus retval = SECFailure;
|
|
NSSCMSEncoderContext *ecx;
|
|
NSSCMSMessage *cmsg = NULL;
|
|
NSSCMSContentInfo *cinfo;
|
|
NSSCMSEnvelopedData *envd;
|
|
NSSCMSRecipientInfo *recipientinfo;
|
|
int cnt;
|
|
int nb;
|
|
char ibuf[4096];
|
|
PK11PasswordFunc pwcb;
|
|
void *pwcb_arg;
|
|
CERTCertificate **recipientcerts;
|
|
PLArenaPool *tmppoolp = NULL;
|
|
SECOidTag bulkalgtag;
|
|
int keysize, i;
|
|
|
|
if ((cnt = NSS_CMSArray_Count(envelopeOptions.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;
|
|
}
|
|
|
|
/* XXX find the recipient's certs by email address or nickname */
|
|
if ((recipientcerts = (CERTCertificate **)NSS_CMSArray_Alloc(tmppoolp, cnt)) == NULL) {
|
|
fprintf(stderr, "ERROR: out of memory.\n");
|
|
goto loser;
|
|
}
|
|
|
|
for (i=0; envelopeOptions.recipients[i] != NULL; i++) {
|
|
if ((recipientcerts[i] = CERT_FindCertByNicknameOrEmailAddr(options.certHandle, envelopeOptions.recipients[i])) == NULL) {
|
|
SECU_PrintError(progName, "cannot find certificate for \"%s\"", envelopeOptions.recipients[i]);
|
|
goto loser;
|
|
}
|
|
}
|
|
recipientcerts[i] = NULL;
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
/* we might need a password for diffie hellman key agreement, so set it up... */
|
|
pwcb = (options.password != NULL) ? ownpw : NULL;
|
|
pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
|
|
|
|
ecx = NSS_CMSEncoder_Start(cmsg,
|
|
writeout, out, /* DER output callback */
|
|
NULL, NULL, /* destination storage */
|
|
pwcb, pwcb_arg, /* password callback */
|
|
NULL, NULL, /* decrypt key callback (not used) */
|
|
NULL, NULL); /* detached digests (not used) */
|
|
|
|
for (;;) {
|
|
if (feof(infile)) break;
|
|
nb = fread(ibuf, 1, sizeof(ibuf), infile);
|
|
if (nb == 0) {
|
|
if (ferror(infile)) {
|
|
fprintf(stderr, "ERROR: file i/o error.\n");
|
|
NSS_CMSEncoder_Cancel(ecx);
|
|
goto loser;
|
|
}
|
|
/* eof */
|
|
break;
|
|
}
|
|
(void)NSS_CMSEncoder_Update(ecx, (const char *)ibuf, nb);
|
|
}
|
|
if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
|
|
fprintf(stderr, "ERROR: DER encoding problem.\n");
|
|
goto loser;
|
|
}
|
|
|
|
retval = SECSuccess;
|
|
|
|
loser:
|
|
if (cmsg)
|
|
NSS_CMSMessage_Destroy(cmsg);
|
|
if (tmppoolp)
|
|
PORT_FreeArena(tmppoolp, PR_FALSE);
|
|
return retval;
|
|
}
|
|
|
|
typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT } Mode;
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
char *progName;
|
|
FILE *outFile, *inFile;
|
|
PLOptState *optstate;
|
|
PLOptStatus status;
|
|
Mode mode = UNKNOWN;
|
|
struct decodeOptionsStr decodeOptions = { 0 };
|
|
struct signOptionsStr signOptions = { 0 };
|
|
struct envelopeOptionsStr envelopeOptions = { 0 };
|
|
struct optionsStr options = { 0 };
|
|
int exitstatus;
|
|
static char *ptrarray[128] = { 0 };
|
|
int nrecipients = 0;
|
|
|
|
progName = strrchr(argv[0], '/');
|
|
progName = progName ? progName+1 : argv[0];
|
|
|
|
inFile = stdin;
|
|
outFile = stdout;
|
|
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;
|
|
|
|
/*
|
|
* Parse command line arguments
|
|
*/
|
|
optstate = PL_CreateOptState(argc, argv, "DSEnN:TGPY:h:p:i:c:d:o:s:u:r:");
|
|
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
|
|
switch (optstate->option) {
|
|
case '?':
|
|
Usage(progName);
|
|
break;
|
|
|
|
case 'D':
|
|
mode = DECODE;
|
|
break;
|
|
case 'S':
|
|
mode = SIGN;
|
|
break;
|
|
case 'E':
|
|
mode = ENCRYPT;
|
|
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':
|
|
if ((inFile = fopen(optstate->value, "r")) == 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 = fopen(optstate->value, "r")) == NULL) {
|
|
fprintf(stderr, "%s: unable to open \"%s\" for reading.\n",
|
|
progName, optstate->value);
|
|
exit(1);
|
|
}
|
|
break;
|
|
|
|
case 'o':
|
|
if ((outFile = fopen(optstate->value, "w")) == 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;
|
|
envelopeOptions.recipients[nrecipients++] = strdup(optstate->value);
|
|
envelopeOptions.recipients[nrecipients] = NULL;
|
|
break;
|
|
|
|
case 'd':
|
|
SECU_ConfigDirectory(optstate->value);
|
|
break;
|
|
|
|
case 'u': {
|
|
int usageType;
|
|
|
|
usageType = atoi (strdup(optstate->value));
|
|
if (usageType < certUsageSSLClient || usageType > certUsageAnyCA)
|
|
return -1;
|
|
options.certUsage = (SECCertUsage)usageType;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* Call the libsec initialization routines */
|
|
PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
|
|
SECU_PKCS11Init(PR_FALSE);
|
|
SEC_Init();
|
|
|
|
/* open cert database */
|
|
options.certHandle = OpenCertDB(progName);
|
|
if (options.certHandle == NULL) {
|
|
return -1;
|
|
}
|
|
CERT_SetDefaultCertDB(options.certHandle);
|
|
|
|
exitstatus = 0;
|
|
switch (mode) {
|
|
case DECODE:
|
|
if (decode(outFile, inFile, progName, options, decodeOptions)) {
|
|
SECU_PrintError(progName, "problem decoding");
|
|
exitstatus = 1;
|
|
}
|
|
break;
|
|
case SIGN:
|
|
if (sign(outFile, inFile, progName, options, signOptions)) {
|
|
SECU_PrintError(progName, "problem signing");
|
|
exitstatus = 1;
|
|
}
|
|
break;
|
|
case ENCRYPT:
|
|
if (envelope(outFile, inFile, progName, options, envelopeOptions)) {
|
|
SECU_PrintError(progName, "problem encrypting");
|
|
exitstatus = 1;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "One of options -D, -S or -E must be set.\n");
|
|
Usage(progName);
|
|
exitstatus = 1;
|
|
}
|
|
if (outFile != stdout)
|
|
fclose(outFile);
|
|
|
|
exit(exitstatus);
|
|
}
|