Back to index

lightning-sunbird  0.9+nobinonly
p7sign.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * p7sign -- A command to create a *detached* pkcs7 signature (over a given
00039  * input file).
00040  *
00041  * $Id: p7sign.c,v 1.10 2004/10/07 04:10:52 julien.pierre.bugs%sun.com Exp $
00042  */
00043 
00044 #include "nspr.h"
00045 #include "plgetopt.h"
00046 #include "secutil.h"
00047 #include "secpkcs7.h"
00048 #include "cert.h"
00049 #include "certdb.h"
00050 #include "sechash.h" /* for HASH_GetHashObject() */
00051 #include "nss.h"
00052 #include "pk11func.h"
00053 
00054 #if defined(XP_UNIX)
00055 #include <unistd.h>
00056 #endif
00057 
00058 #include <stdio.h>
00059 #include <string.h>
00060 
00061 #if (defined(XP_WIN) && !defined(WIN32)) || (defined(__sun) && !defined(SVR4))
00062 extern int fread(char *, size_t, size_t, FILE*);
00063 extern int fwrite(char *, size_t, size_t, FILE*);
00064 extern int fprintf(FILE *, char *, ...);
00065 #endif
00066 
00067 char* KeyDbPassword = 0;
00068 
00069 
00070 char* MyPK11PasswordFunc (PK11SlotInfo *slot, PRBool retry, void* arg)
00071 {
00072     char *ret=0;
00073 
00074     if (retry == PR_TRUE)
00075         return NULL;
00076     ret = PL_strdup (KeyDbPassword);
00077     return ret;
00078 }
00079 
00080 
00081 static void
00082 Usage(char *progName)
00083 {
00084     fprintf(stderr,
00085            "Usage:  %s -k keyname [-d keydir] [-i input] [-o output]\n",
00086            progName);
00087     fprintf(stderr, "%-20s Nickname of key to use for signature\n",
00088            "-k keyname");
00089     fprintf(stderr, "%-20s Key database directory (default is ~/.netscape)\n",
00090            "-d keydir");
00091     fprintf(stderr, "%-20s Define an input file to use (default is stdin)\n",
00092            "-i input");
00093     fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n",
00094            "-o output");
00095     fprintf(stderr, "%-20s Encapsulate content in signature message\n",
00096            "-e");
00097     fprintf(stderr, "%-20s Password to the key databse\n", "-p");
00098     exit(-1);
00099 }
00100 
00101 static void
00102 SignOut(void *arg, const char *buf, unsigned long len)
00103 {
00104    FILE *out;
00105 
00106    out = (FILE*) arg; 
00107    fwrite (buf, len, 1, out);
00108 }
00109 
00110 static int
00111 CreateDigest(SECItem *data, char *digestdata, unsigned int *len, unsigned int maxlen)
00112 {
00113     const SECHashObject *hashObj;
00114     void *hashcx;
00115 
00116     /* XXX probably want to extend interface to allow other hash algorithms */
00117     hashObj = HASH_GetHashObject(HASH_AlgSHA1);
00118 
00119     hashcx = (* hashObj->create)();
00120     if (hashcx == NULL)
00121        return -1;
00122 
00123     (* hashObj->begin)(hashcx);
00124     (* hashObj->update)(hashcx, data->data, data->len);
00125     (* hashObj->end)(hashcx, (unsigned char *)digestdata, len, maxlen);
00126     (* hashObj->destroy)(hashcx, PR_TRUE);
00127     return 0;
00128 }
00129 
00130 static int
00131 SignFile(FILE *outFile, PRFileDesc *inFile, CERTCertificate *cert, 
00132          PRBool encapsulated)
00133 {
00134     char digestdata[32];
00135     unsigned int len;
00136     SECItem digest, data2sign;
00137     SEC_PKCS7ContentInfo *cinfo;
00138     SECStatus rv;
00139 
00140     if (outFile == NULL || inFile == NULL || cert == NULL)
00141        return -1;
00142 
00143     /* suck the file in */
00144        if (SECU_ReadDERFromFile(&data2sign, inFile, PR_FALSE) != SECSuccess)
00145        return -1;
00146 
00147     if (!encapsulated) {
00148        /* unfortunately, we must create the digest ourselves */
00149        /* SEC_PKCS7CreateSignedData should have a flag to not include */
00150        /* the content for non-encapsulated content at encode time, but */
00151        /* should always compute the hash itself */
00152        if (CreateDigest(&data2sign, digestdata, &len, 32) < 0)
00153            return -1;
00154        digest.data = (unsigned char *)digestdata;
00155        digest.len = len;
00156     }
00157 
00158     /* XXX Need a better way to handle that usage stuff! */
00159     cinfo = SEC_PKCS7CreateSignedData (cert, certUsageEmailSigner, NULL,
00160                                    SEC_OID_SHA1,
00161                                    encapsulated ? NULL : &digest,
00162                                    NULL, NULL);
00163     if (cinfo == NULL)
00164        return -1;
00165 
00166     if (encapsulated) {
00167        SEC_PKCS7SetContent(cinfo, (char *)data2sign.data, data2sign.len);
00168     }
00169 
00170     rv = SEC_PKCS7IncludeCertChain (cinfo, NULL);
00171     if (rv != SECSuccess) {
00172        SEC_PKCS7DestroyContentInfo (cinfo);
00173        return -1;
00174     }
00175 
00176     rv = SEC_PKCS7Encode (cinfo, SignOut, outFile, NULL,
00177                        NULL, NULL);
00178 
00179     SEC_PKCS7DestroyContentInfo (cinfo);
00180 
00181     if (rv != SECSuccess)
00182        return -1;
00183 
00184     return 0;
00185 }
00186 
00187 int
00188 main(int argc, char **argv)
00189 {
00190     char *progName;
00191     FILE *outFile;
00192     PRFileDesc *inFile;
00193     char *keyName;
00194     CERTCertDBHandle *certHandle;
00195     CERTCertificate *cert;
00196     PRBool encapsulated = PR_FALSE;
00197     PLOptState *optstate;
00198     PLOptStatus status;
00199     SECStatus rv;
00200 
00201     progName = strrchr(argv[0], '/');
00202     progName = progName ? progName+1 : argv[0];
00203 
00204     inFile = NULL;
00205     outFile = NULL;
00206     keyName = NULL;
00207 
00208     /*
00209      * Parse command line arguments
00210      */
00211     optstate = PL_CreateOptState(argc, argv, "ed:k:i:o:p:");
00212     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
00213        switch (optstate->option) {
00214          case '?':
00215            Usage(progName);
00216            break;
00217 
00218          case 'e':
00219            /* create a message with the signed content encapsulated */
00220            encapsulated = PR_TRUE;
00221            break;
00222 
00223          case 'd':
00224            SECU_ConfigDirectory(optstate->value);
00225            break;
00226 
00227          case 'i':
00228            inFile = PR_Open(optstate->value, PR_RDONLY, 0);
00229            if (!inFile) {
00230               fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
00231                      progName, optstate->value);
00232               return -1;
00233            }
00234            break;
00235 
00236          case 'k':
00237            keyName = strdup(optstate->value);
00238            break;
00239 
00240          case 'o':
00241            outFile = fopen(optstate->value, "w");
00242            if (!outFile) {
00243               fprintf(stderr, "%s: unable to open \"%s\" for writing\n",
00244                      progName, optstate->value);
00245               return -1;
00246            }
00247            break;
00248          case 'p':
00249             KeyDbPassword = strdup (optstate->value);
00250             break;
00251        }
00252     }
00253 
00254     if (!keyName) Usage(progName);
00255 
00256     if (!inFile) inFile = PR_STDIN;
00257     if (!outFile) outFile = stdout;
00258 
00259     /* Call the initialization routines */
00260     PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
00261     rv = NSS_Init(SECU_ConfigDirectory(NULL));
00262     if (rv != SECSuccess) {
00263        SECU_PrintPRandOSError(progName);
00264        return -1;
00265     }
00266 
00267     PK11_SetPasswordFunc (MyPK11PasswordFunc);
00268 
00269     /* open cert database */
00270     certHandle = CERT_GetDefaultCertDB();
00271     if (certHandle == NULL) {
00272        return -1;
00273     }
00274 
00275     /* find cert */
00276     cert = CERT_FindCertByNickname(certHandle, keyName);
00277     if (cert == NULL) {
00278        SECU_PrintError(progName,
00279                       "the corresponding cert for key \"%s\" does not exist",
00280                      keyName);
00281        return -1;
00282     }
00283 
00284     if (SignFile(outFile, inFile, cert, encapsulated)) {
00285        SECU_PrintError(progName, "problem signing data");
00286        return -1;
00287     }
00288 
00289     if (NSS_Shutdown() != SECSuccess) {
00290         exit(1);
00291     }
00292 
00293     return 0;
00294 }