.. _mozilla_projects_nss_nss_sample_code_sample2:

sample2
=======

.. container::

   .. code:: c

      /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ /* NSPR Headers */ #include <prthread.h> #include <plgetopt.h> #include <prerror.h> #include <prinit.h> #include <prlog.h> #include <prtypes.h> #include <plstr.h> /* NSS headers */ #include <cryptohi.h> #include <keyhi.h> #include <pk11priv.h> #include <cert.h> #include <base64.h> #include <secerr.h> #include <secport.h> #include <secoid.h> #include <secmodt.h> #include <secoidt.h> #include <sechash.h> /* our samples utilities */ #include "util.h" /* Constants */ #define BLOCKSIZE 32 #define MODBLOCKSIZE 128 #define DEFAULT_KEY_BITS 1024 /* Header file Constants */ #define ENCKEY_HEADER "-----BEGIN WRAPPED ENCKEY-----" #define ENCKEY_TRAILER "-----END WRAPPED ENCKEY-----" #define MACKEY_HEADER "-----BEGIN WRAPPED MACKEY-----" #define MACKEY_TRAILER "-----END WRAPPED MACKEY-----" #define IV_HEADER "-----BEGIN IV-----" #define IV_TRAILER "-----END IV-----" #define MAC_HEADER "-----BEGIN MAC-----" #define MAC_TRAILER "-----END MAC-----" #define PAD_HEADER "-----BEGIN PAD-----" #define PAD_TRAILER "-----END PAD-----" #define LAB_HEADER "-----BEGIN KEY LABEL-----" #define LAB_TRAILER "-----END KEY LABEL-----" #define PUBKEY_HEADER "-----BEGIN PUB KEY -----" #define PUBKEY_TRAILER "-----END PUB KEY -----" #define NS_CERTREQ_HEADER "-----BEGIN NEW CERTIFICATE REQUEST-----" #define NS_CERTREQ_TRAILER "-----END NEW CERTIFICATE REQUEST-----" #define NS_CERT_ENC_HEADER "-----BEGIN CERTIFICATE FOR ENCRYPTION-----" #define NS_CERT_ENC_TRAILER "-----END CERTIFICATE FOR ENCRYPTION-----" #define NS_CERT_VFY_HEADER "-----BEGIN CERTIFICATE FOR SIGNATURE VERIFICATION-----" #define NS_CERT_VFY_TRAILER "-----END CERTIFICATE FOR SIGNATURE VERIFICATION-----" #define NS_SIG_HEADER "-----BEGIN SIGNATURE-----" #define NS_SIG_TRAILER "-----END SIGNATURE-----" #define NS_CERT_HEADER "-----BEGIN CERTIFICATE-----" #define NS_CERT_TRAILER "-----END CERTIFICATE-----" /* Missing publically from nss versions earlier than 3.13 */ #ifndef SEC_ERROR_BASE #define SEC_ERROR_BASE (-0x2000) typedef enum { SEC_ERROR_IO = SEC_ERROR_BASE + 0, SEC_ERROR_TOKEN_NOT_LOGGED_IN = (SEC_ERROR_BASE + 155), SEC_ERROR_END_OF_LIST } SECErrorCodes; #endif /* PORT_ErrorToString introduced in nss 3.13. On earlier versions of nss that * don't support error tables, PR_ErrorToString will return "Unknown code". */ #ifndef PORT_ErrorToString #define PORT_ErrorToString(err) PR_ErrorToString((err), PR_LANGUAGE_I_DEFAULT) #endif /* sample 6 commands */ typedef enum { GENERATE_CSR, ADD_CERT_TO_DB, SAVE_CERT_TO_HEADER, ENCRYPT, DECRYPT, SIGN, VERIFY, UNKNOWN } CommandType; typedef enum { SYMKEY = 0, MACKEY = 1, IV = 2, MAC = 3, PAD = 4, PUBKEY = 5, LAB = 6, CERTENC= 7, CERTVFY= 8, SIG = 9 } HeaderType; /* * Print usage message and exit */ static void Usage(const char *progName) { fprintf(stderr, "\nUsage: %s %s %s %s %s %s %s %s %s %s\n\n", progName, " -<g|a|h|e|ds|v> -d <dbdirpath> ", "[-p <dbpwd> | -f <dbpwdfile>] [-z <noisefilename>] [-a <\"\">]", "-s <subject> -r <csr> | ", "-n <nickname> -t <trust> -c <cert> [ -r <csr> -u <issuernickname> [-x <\"\">] -m <serialnumber> ] | ", "-n <nickname> -b <headerfilename> | ", "-b <headerfilename> -i <ipfilename> -e <encryptfilename> | ", "-b <headerfilename> -i <ipfilename> | ", "-b <headerfilename> -i <ipfilename> | ", "-b <headerfilename> -e <encryptfilename> -o <opfilename> \n"); fprintf(stderr, "commands:\n\n"); fprintf(stderr, "%s %s\n --for generating cert request (for CA also)\n\n", progName, "-G -s <subject> -r <csr>"); fprintf(stderr, "%s %s\n --to input and store cert (for CA also)\n\n", progName, "-A -n <nickname> -t <trust> -c <cert> [ -r <csr> -u <issuernickname> [-x <\"\">] -m <serialnumber> ]"); fprintf(stderr, "%s %s\n --to put cert in header\n\n", progName, "-H -n <nickname> -b <headerfilename> [-v <\"\">]"); fprintf(stderr, "%s %s\n --to find public key from cert in header and encrypt\n\n", progName, "-E -b <headerfilename> -i <ipfilename> -e <encryptfilename> "); fprintf(stderr, "%s %s\n --decrypt using corresponding private key \n\n", progName, "-D -b <headerfilename> -e <encryptfilename> -o <opfilename>"); fprintf(stderr, "%s %s\n --Sign using private key \n\n", progName, "-S -b <headerfilename> -i <infilename> "); fprintf(stderr, "%s %s\n --Verify using public key \n\n", progName, "-V -b <headerfilename> -i <ipfilename> "); fprintf(stderr, "options:\n\n"); fprintf(stderr, "%-30s - db directory path\n\n", "-d <dbdirpath>"); fprintf(stderr, "%-30s - db password [optional]\n\n", "-p <dbpwd>"); fprintf(stderr, "%-30s - db password file [optional]\n\n", "-f <dbpwdfile>"); fprintf(stderr, "%-30s - noise file name [optional]\n\n", "-z <noisefilename>"); fprintf(stderr, "%-30s - input file name\n\n", "-i <ipfilename>"); fprintf(stderr, "%-30s - header file name\n\n", "-b <headerfilename>"); fprintf(stderr, "%-30s - encrypt file name\n\n", "-e <encryptfilename>"); fprintf(stderr, "%-30s - output file name\n\n", "-o <opfilename>"); fprintf(stderr, "%-30s - certificate serial number\n\n", "-m <serialnumber>"); fprintf(stderr, "%-30s - certificate nickname\n\n", "-n <nickname>"); fprintf(stderr, "%-30s - certificate trust\n\n", "-t <trustargs>"); fprintf(stderr, "%-30s - certificate issuer nickname\n\n", "-u <issuernickname>"); fprintf(stderr, "%-30s - certificate signing request \n\n", "-r <csr>"); fprintf(stderr, "%-30s - generate a self-signed cert [optional]\n\n", "-x"); fprintf(stderr, "%-30s - to enable ascii [optional]\n\n", "-a"); fprintf(stderr, "%-30s - to save certificate to header file as sig verification [optional]\n\n", "-v"); exit(-1); } /* * Validate the options used for Generate CSR command */ static void ValidateGenerateCSRCommand(const char *progName, const char *dbdir, CERTName *subject, const char *subjectStr, const char *certReqFileName) { PRBool validationFailed = PR_FALSE; if (!subject) { PR_fprintf(PR_STDERR, "%s -G -d %s -s: improperly formatted name: \"%s\"\n", progName, dbdir, subjectStr); validationFailed = PR_TRUE; } if (!certReqFileName) { PR_fprintf(PR_STDERR, "%s -G -d %s -s %s -r: certificate request file name not found\n", progName, dbdir, subjectStr); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-G -d <dbdirpath> -s <subject> -r <csr> \n"); exit(-1); } } /* * Validate the options used for Add Cert to DB command */ static void ValidateAddCertToDBCommand(const char *progName, const char *dbdir, const char *nickNameStr, const char *trustStr, const char *certFileName, const char *certReqFileName, const char *issuerNameStr, const char *serialNumberStr, PRBool selfsign) { PRBool validationFailed = PR_FALSE; if (!nickNameStr) { PR_fprintf(PR_STDERR, "%s -A -d %s -n : nick name is missing\n", progName, dbdir); validationFailed = PR_TRUE; } if (!trustStr) { PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t: trust flag is missing\n", progName, dbdir, nickNameStr); validationFailed = PR_TRUE; } if (!certFileName) { PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c: certificate file name not found\n", progName, dbdir, nickNameStr, trustStr, serialNumberStr, certReqFileName); validationFailed = PR_TRUE; } if (PR_Access(certFileName, PR_ACCESS_EXISTS) == PR_FAILURE) { if (!certReqFileName) { PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r: certificate file or certificate request file is not found\n", progName, dbdir, nickNameStr, trustStr, certFileName); validationFailed = PR_TRUE; } if (!selfsign && !issuerNameStr) { PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r %s -u : issuer name is missing\n", progName, dbdir, nickNameStr, trustStr, certFileName, certReqFileName); validationFailed = PR_TRUE; } if (!serialNumberStr) { PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r %s -u %s -m : serial number is missing\n", progName, dbdir, nickNameStr, trustStr, certFileName, certReqFileName, issuerNameStr); validationFailed = PR_TRUE; } } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, " -A -d <dbdirpath> -n <nickname> -t <trust> -c <cert> \n"); fprintf(stderr, " OR\n"); fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-A -d <dbdirpath> -n <nickname> -t <trust> -c <cert> -r <csr> -u <issuernickname> -m <serialnumber> [-x <\"\">] \n"); exit(-1); } } /* * Validate the options used for Save Cert To Header command */ static void ValidateSaveCertToHeaderCommand(const char *progName, const char *dbdir, const char *nickNameStr, const char *headerFileName) { PRBool validationFailed = PR_FALSE; if (!nickNameStr) { PR_fprintf(PR_STDERR, "%s -S -d %s -n : nick name is missing\n", progName, dbdir); validationFailed = PR_TRUE; } if (!headerFileName) { PR_fprintf(PR_STDERR, "%s -S -d %s -n %s -b : header file name is not found\n", progName, dbdir, nickNameStr); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-S -d <dbdirpath> -n <nickname> -b <headerfilename> [-v <\"\">]\n"); exit(-1); } } /* * Validate the options used for Encrypt command */ static void ValidateEncryptCommand(const char *progName, const char *dbdir, const char *nickNameStr, const char *headerFileName, const char *inFileName, const char *encryptedFileName) { PRBool validationFailed = PR_FALSE; if (!nickNameStr) { PR_fprintf(PR_STDERR, "%s -E -d %s -n : nick name is missing\n", progName, dbdir); validationFailed = PR_TRUE; } if (!headerFileName) { PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b : header file name is not found\n", progName, dbdir, nickNameStr); validationFailed = PR_TRUE; } if (!inFileName) { PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b %s -i : input file name is not found\n", progName, dbdir, nickNameStr, headerFileName); validationFailed = PR_TRUE; } if (!encryptedFileName) { PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b %s -i %s -e : encrypt file name is not found\n", progName, dbdir, nickNameStr, headerFileName, inFileName); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-E -d <dbdirpath> -b <headerfilename> -i <ipfilename> -e <encryptfilename> -n <nickname> \n"); exit(-1); } } /* * Validate the options used for Sign command */ static void ValidateSignCommand(const char *progName, const char *dbdir, const char *nickNameStr, const char *headerFileName, const char *inFileName) { PRBool validationFailed = PR_FALSE; if (!nickNameStr) { PR_fprintf(PR_STDERR, "%s -I -d %s -n : nick name is missing\n", progName, dbdir); validationFailed = PR_TRUE; } if (!headerFileName) { PR_fprintf(PR_STDERR, "%s -I -d %s -n %s -b : header file name is not found\n", progName, dbdir, nickNameStr); validationFailed = PR_TRUE; } if (!inFileName) { PR_fprintf(PR_STDERR, "%s -I -d %s -n %s -b %s -i : input file name is not found\n", progName, dbdir, nickNameStr, headerFileName); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-I -d <dbdirpath> -b <headerfilename> -i <ipfilename> -n <nickname> \n"); exit(-1); } } /* * Validate the options used for verify command */ static void ValidateVerifyCommand(const char *progName, const char *dbdir, const char *headerFileName, const char *inFileName) { PRBool validationFailed = PR_FALSE; if (!headerFileName) { PR_fprintf(PR_STDERR, "%s -V -d %s -b : header file name is not found\n", progName, dbdir); validationFailed = PR_TRUE; } if (!inFileName) { PR_fprintf(PR_STDERR, "%s -I -d %s -b %s -i : input file name is not found\n", progName, dbdir, headerFileName); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-I -d <dbdirpath> -b <headerfilename> -i <ipfilename> \n"); exit(-1); } } /* * Validate the options used for Decrypt command */ static void ValidateDecryptCommand(const char *progName, const char *dbdir, const char *headerFileName, const char *encryptedFileName, const char *outFileName) { PRBool validationFailed = PR_FALSE; if (!headerFileName) { PR_fprintf(PR_STDERR, "%s -D -d %s -b : header file name is not found\n", progName, dbdir); validationFailed = PR_TRUE; } if (!encryptedFileName) { PR_fprintf(PR_STDERR, "%s -D -d %s -b %s -e : encrypt file name is not found\n", progName, dbdir, headerFileName); validationFailed = PR_TRUE; } if (!outFileName) { PR_fprintf(PR_STDERR, "%s -D -d %s -b %s -e %s -o : output file name is not found\n", progName, dbdir, headerFileName, encryptedFileName); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-D -d <dbdirpath> -b <headerfilename> -e <encryptfilename> -o <opfilename>\n"); exit(-1); } } /* * Sign the contents of input file using private key and * return result as SECItem */ SECStatus SignData(const char *inFileName, SECKEYPrivateKey *pk, SECItem *res) { SECStatus rv = SECFailure; unsigned int nb; unsigned char ibuf[4096]; PRFileDesc *inFile = NULL; SGNContext *sgn = NULL; /* Open the input file for reading */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); rv = SECFailure; goto cleanup; } /* Sign using private key */ sgn = SGN_NewContext(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, pk); if (!sgn) { PR_fprintf(PR_STDERR, "unable to create context for signing\n"); rv = SECFailure; goto cleanup; } rv = SGN_Begin(sgn); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while SGN_Begin\n"); goto cleanup; } while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) { rv = SGN_Update(sgn, ibuf, nb); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while SGN_Update\n"); goto cleanup; } } rv = SGN_End(sgn, res); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while SGN_End\n"); goto cleanup; } cleanup: if (inFile) { PR_Close(inFile); } if (sgn) { SGN_DestroyContext(sgn, PR_TRUE); } return rv; } /* * Verify the signature using public key */ SECStatus VerifyData(const char *inFileName, SECKEYPublicKey *pk, SECItem *sigItem, secuPWData *pwdata) { unsigned int nb; unsigned char ibuf[4096]; SECStatus rv = SECFailure; VFYContext *vfy = NULL; PRFileDesc *inFile = NULL; /* Open the input file for reading */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); rv = SECFailure; goto cleanup; } vfy = VFY_CreateContext(pk, sigItem, SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, pwdata); if (!vfy) { PR_fprintf(PR_STDERR, "unable to create context for verifying signature\n"); rv = SECFailure; goto cleanup; } rv = VFY_Begin(vfy); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while VFY_Begin\n"); goto cleanup; } while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) { rv = VFY_Update(vfy, ibuf, nb); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while VFY_Update\n"); goto cleanup; } } rv = VFY_End(vfy); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while VFY_End\n"); goto cleanup; } cleanup: if (inFile) { PR_Close(inFile); } if (vfy) { VFY_DestroyContext(vfy, PR_TRUE); } return rv; } /* * Write Cryptographic parameters to header file */ SECStatus WriteToHeaderFile(const char *buf, unsigned int len, HeaderType type, PRFileDesc *outFile) { SECStatus rv; const char *header; const char *trailer; switch (type) { case SYMKEY: header = ENCKEY_HEADER; trailer = ENCKEY_TRAILER; break; case MACKEY: header = MACKEY_HEADER; trailer = MACKEY_TRAILER; break; case IV: header = IV_HEADER; trailer = IV_TRAILER; break; case MAC: header = MAC_HEADER; trailer = MAC_TRAILER; break; case PAD: header = PAD_HEADER; trailer = PAD_TRAILER; break; case PUBKEY: header = PUBKEY_HEADER; trailer = PUBKEY_TRAILER; break; case CERTENC: header = NS_CERT_ENC_HEADER; trailer = NS_CERT_ENC_TRAILER; break; case CERTVFY: header = NS_CERT_VFY_HEADER; trailer = NS_CERT_VFY_TRAILER; break; case SIG: header = NS_SIG_HEADER; trailer = NS_SIG_TRAILER; break; case LAB: header = LAB_HEADER; trailer = LAB_TRAILER; PR_fprintf(outFile, "%s\n", header); PR_fprintf(outFile, "%s\n", buf); PR_fprintf(outFile, "%s\n\n", trailer); return SECSuccess; break; default: return SECFailure; } PR_fprintf(outFile, "%s\n", header); PrintAsHex(outFile, buf, len); PR_fprintf(outFile, "%s\n\n", trailer); return SECSuccess; } /* * Read cryptographic parameters from the header file */ SECStatus ReadFromHeaderFile(const char *fileName, HeaderType type, SECItem *item, PRBool isHexData) { SECStatus rv = SECSuccess; PRFileDesc* file = NULL; SECItem filedata; SECItem outbuf; unsigned char *nonbody; unsigned char *body; char *header; char *trailer; outbuf.type = siBuffer; file = PR_Open(fileName, PR_RDONLY, 0); if (!file) { PR_fprintf(PR_STDERR, "Failed to open %s\n", fileName); rv = SECFailure; goto cleanup; } switch (type) { case PUBKEY: header = PUBKEY_HEADER; trailer = PUBKEY_TRAILER; break; case SYMKEY: header = ENCKEY_HEADER; trailer = ENCKEY_TRAILER; break; case MACKEY: header = MACKEY_HEADER; trailer = MACKEY_TRAILER; break; case IV: header = IV_HEADER; trailer = IV_TRAILER; break; case MAC: header = MAC_HEADER; trailer = MAC_TRAILER; break; case PAD: header = PAD_HEADER; trailer = PAD_TRAILER; break; case LAB: header = LAB_HEADER; trailer = LAB_TRAILER; break; case CERTENC: header = NS_CERT_ENC_HEADER; trailer = NS_CERT_ENC_TRAILER; break; case CERTVFY: header = NS_CERT_VFY_HEADER; trailer = NS_CERT_VFY_TRAILER; break; case SIG: header = NS_SIG_HEADER; trailer = NS_SIG_TRAILER; break; default: rv = SECFailure; goto cleanup; } rv = FileToItem(&filedata, file); nonbody = (char *)filedata.data; if (!nonbody) { PR_fprintf(PR_STDERR, "unable to read data from input file\n"); rv = SECFailure; goto cleanup; } /* check for headers and trailers and remove them */ if ((body = strstr(nonbody, header)) != NULL) { char *trail = NULL; nonbody = body; body = PORT_Strchr(body, '\n'); if (!body) body = PORT_Strchr(nonbody, '\r'); /* maybe this is a MAC file */ if (body) trail = strstr(++body, trailer); if (trail != NULL) { *trail = '\0'; } else { PR_fprintf(PR_STDERR, "input has header but no trailer\n"); PORT_Free(filedata.data); rv = SECFailure; goto cleanup; } } else { /* headers didn't exist */ char *trail = NULL; body = nonbody; if (body) { trail = strstr(++body, trailer); if (trail != NULL) { PR_fprintf(PR_STDERR, "input has no header but has trailer\n"); PORT_Free(filedata.data); rv = SECFailure; goto cleanup; } } } HexToBuf(body, item, isHexData); cleanup: if (file) { PR_Close(file); } return rv; } /* * Generate the private key */ SECKEYPrivateKey * GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size, int publicExponent, const char *noise, SECKEYPublicKey **pubkeyp, const char *pqgFile, secuPWData *pwdata) { CK_MECHANISM_TYPE mechanism; SECOidTag algtag; PK11RSAGenParams rsaparams; void *params; SECKEYPrivateKey *privKey = NULL; SECStatus rv; unsigned char randbuf[BLOCKSIZE + 1]; rv = GenerateRandom(randbuf, BLOCKSIZE); if (rv != SECSuccess) { fprintf(stderr, "Error while generating the random numbers : %s\n", PORT_ErrorToString(rv)); goto cleanup; } PK11_RandomUpdate(randbuf, BLOCKSIZE); switch (keytype) { case rsaKey: rsaparams.keySizeInBits = size; rsaparams.pe = publicExponent; mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; algtag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; params = &rsaparams; break; default: goto cleanup; } fprintf(stderr, "\n\n"); fprintf(stderr, "Generating key. This may take a few moments...\n\n"); privKey = PK11_GenerateKeyPair(slot, mechanism, params, pubkeyp, PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/, pwdata); cleanup: return privKey; } /* * Get the certificate request from CSR */ static CERTCertificateRequest * GetCertRequest(char *inFileName, PRBool ascii) { CERTSignedData signedData; SECItem reqDER; CERTCertificateRequest *certReq = NULL; SECStatus rv = SECSuccess; PRArenaPool *arena = NULL; reqDER.data = NULL; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { rv = SECFailure; goto cleanup; } rv = ReadDERFromFile(&reqDER, inFileName, ascii); if (rv) { rv = SECFailure; goto cleanup; } certReq = (CERTCertificateRequest*) PORT_ArenaZAlloc (arena, sizeof(CERTCertificateRequest)); if (!certReq) { rv = SECFailure; goto cleanup; } certReq->arena = arena; /* Since cert request is a signed data, must decode to get the inner data */ PORT_Memset(&signedData, 0, sizeof(signedData)); rv = SEC_ASN1DecodeItem(arena, &signedData, SEC_ASN1_GET(CERT_SignedDataTemplate), &reqDER); if (rv) { rv = SECFailure; goto cleanup; } rv = SEC_ASN1DecodeItem(arena, certReq, SEC_ASN1_GET(CERT_CertificateRequestTemplate), &signedData.data); if (rv) { rv = SECFailure; goto cleanup; } rv = CERT_VerifySignedDataWithPublicKeyInfo(&signedData, &certReq->subjectPublicKeyInfo, NULL /* wincx */); if (reqDER.data) { SECITEM_FreeItem(&reqDER, PR_FALSE); } cleanup: if (rv) { PR_fprintf(PR_STDERR, "bad certificate request\n"); if (arena) { PORT_FreeArena(arena, PR_FALSE); } certReq = NULL; } return certReq; } /* * Sign Cert */ static SECItem * SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign, SECOidTag hashAlgTag, SECKEYPrivateKey *privKey, char *issuerNickName, void *pwarg) { SECItem der; SECStatus rv; SECOidTag algID; void *dummy; PRArenaPool *arena = NULL; SECItem *result = NULL; SECKEYPrivateKey *caPrivateKey = NULL; if (!selfsign) { CERTCertificate *issuer = PK11_FindCertFromNickname(issuerNickName, pwarg); if ((CERTCertificate *)NULL == issuer) { PR_fprintf(PR_STDERR, "unable to find issuer with nickname %s\n", issuerNickName); goto cleanup; } privKey = caPrivateKey = PK11_FindKeyByAnyCert(issuer, pwarg); CERT_DestroyCertificate(issuer); if (caPrivateKey == NULL) { PR_fprintf(PR_STDERR, "unable to retrieve key %s\n", issuerNickName); goto cleanup; } } arena = cert->arena; algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, hashAlgTag); if (algID == SEC_OID_UNKNOWN) { PR_fprintf(PR_STDERR, "Unknown key or hash type for issuer.\n"); goto cleanup; } rv = SECOID_SetAlgorithmID(arena, &cert->signature, algID, 0); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not set signature algorithm id.\n%s\n", PORT_ErrorToString(rv)); goto cleanup; } /* we only deal with cert v3 here */ *(cert->version.data) = 2; cert->version.len = 1; der.len = 0; der.data = NULL; dummy = SEC_ASN1EncodeItem (arena, &der, cert, SEC_ASN1_GET(CERT_CertificateTemplate)); if (!dummy) { PR_fprintf(PR_STDERR, "Could not encode certificate.\n"); goto cleanup; } result = (SECItem *) PORT_ArenaZAlloc (arena, sizeof (SECItem)); if (result == NULL) { PR_fprintf(PR_STDERR, "Could not allocate item for certificate data.\n"); goto cleanup; } rv = SEC_DerSignData(arena, result, der.data, der.len, privKey, algID); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not sign encoded certificate data : %s\n", PORT_ErrorToString(rv)); /* result allocated out of the arena, it will be freed * when the arena is freed */ result = NULL; goto cleanup; } cert->derCert = *result; cleanup: if (caPrivateKey) { SECKEY_DestroyPrivateKey(caPrivateKey); } return result; } /* * MakeV1Cert */ static CERTCertificate * MakeV1Cert(CERTCertDBHandle *handle, CERTCertificateRequest *req, char * issuerNickName, PRBool selfsign, unsigned int serialNumber, int warpmonths, int validityMonths) { PRExplodedTime printableTime; PRTime now; PRTime after; CERTValidity *validity = NULL; CERTCertificate *issuerCert = NULL; CERTCertificate *cert = NULL; if ( !selfsign ) { issuerCert = CERT_FindCertByNicknameOrEmailAddr(handle, issuerNickName); if (!issuerCert) { PR_fprintf(PR_STDERR, "could not find certificate named %s\n", issuerNickName); goto cleanup; } } now = PR_Now(); PR_ExplodeTime (now, PR_GMTParameters, &printableTime); if ( warpmonths ) { printableTime.tm_month += warpmonths; now = PR_ImplodeTime (&printableTime); PR_ExplodeTime (now, PR_GMTParameters, &printableTime); } printableTime.tm_month += validityMonths; after = PR_ImplodeTime (&printableTime); /* note that the time is now in micro-second unit */ validity = CERT_CreateValidity (now, after); if (validity) { cert = CERT_CreateCertificate(serialNumber, (selfsign ? &req->subject : &issuerCert->subject), validity, req); CERT_DestroyValidity(validity); } cleanup: if ( issuerCert ) { CERT_DestroyCertificate (issuerCert); } return cert; } /* * Add a certificate to the nss database */ SECStatus AddCert(PK11SlotInfo *slot, CERTCertDBHandle *handle, const char *name, char *trusts, char *inFileName, PRBool ascii, PRBool emailcert, void *pwdata) { SECItem certDER; SECStatus rv; CERTCertTrust *trust = NULL; CERTCertificate *cert = NULL; certDER.data = NULL; /* Read in the entire file specified with the -i argument */ rv = ReadDERFromFile(&certDER, inFileName, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "unable to read input file %s : %s\n", inFileName, PORT_ErrorToString(rv)); goto cleanup; } /* Read in an ASCII cert and return a CERTCertificate */ cert = CERT_DecodeCertFromPackage((char *)certDER.data, certDER.len); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); rv = SECFailure; goto cleanup; } /* Create a cert trust */ trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust)); if (!trust) { PR_fprintf(PR_STDERR, "unable to allocate cert trust\n"); rv = SECFailure; goto cleanup; } rv = CERT_DecodeTrustString(trust, trusts); if (rv) { PR_fprintf(PR_STDERR, "unable to decode trust string\n"); rv = SECFailure; goto cleanup; } rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, name, PR_FALSE); if (rv != SECSuccess) { /* sigh, PK11_Import Cert and CERT_ChangeCertTrust should have * been coded to take a password arg. */ if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { rv = PK11_Authenticate(slot, PR_TRUE, pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "could not authenticate to token %s : %s\n", PK11_GetTokenName(slot), PORT_ErrorToString(rv)); rv = SECFailure; goto cleanup; } rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, name, PR_FALSE); } if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "could not add certificate to token or database : %s\n", PORT_ErrorToString(rv)); rv = SECFailure; goto cleanup; } } rv = CERT_ChangeCertTrust(handle, cert, trust); if (rv != SECSuccess) { if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { rv = PK11_Authenticate(slot, PR_TRUE, pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "could not authenticate to token %s : %s\n", PK11_GetTokenName(slot), PORT_ErrorToString(rv)); rv = SECFailure; goto cleanup; } rv = CERT_ChangeCertTrust(handle, cert, trust); } if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "could not change trust on certificate : %s\n", PORT_ErrorToString(rv)); rv = SECFailure; goto cleanup; } } if (emailcert) { CERT_SaveSMimeProfile(cert, NULL, pwdata); } cleanup: if (cert) { CERT_DestroyCertificate (cert); } if (trust) { PORT_Free(trust); } if (certDER.data) { PORT_Free(certDER.data); } return rv; } /* * Create a certificate */ static SECStatus CreateCert( CERTCertDBHandle *handle, PK11SlotInfo *slot, char * issuerNickName, char *inFileName, char *outFileName, SECKEYPrivateKey **selfsignprivkey, void *pwarg, SECOidTag hashAlgTag, unsigned int serialNumber, int warpmonths, int validityMonths, const char *dnsNames, PRBool ascii, PRBool selfsign) { void *extHandle; SECItem reqDER; CERTCertExtension **CRexts; SECStatus rv = SECSuccess; CERTCertificate *subjectCert = NULL; CERTCertificateRequest *certReq = NULL; PRFileDesc *outFile = NULL; SECItem *certDER = NULL; reqDER.data = NULL; outFile = PR_Open(outFileName, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 00660); /* Create a cert request object from the input cert request der */ certReq = GetCertRequest(inFileName, ascii); if (certReq == NULL) { rv = SECFailure; goto cleanup; } subjectCert = MakeV1Cert(handle, certReq, issuerNickName, selfsign, serialNumber, warpmonths, validityMonths); if (subjectCert == NULL) { rv = SECFailure; goto cleanup; } extHandle = CERT_StartCertExtensions (subjectCert); if (extHandle == NULL) { rv = SECFailure; goto cleanup; } if (certReq->attributes != NULL && certReq->attributes[0] != NULL && certReq->attributes[0]->attrType.data != NULL && certReq->attributes[0]->attrType.len > 0 && SECOID_FindOIDTag(&certReq->attributes[0]->attrType) == SEC_OID_PKCS9_EXTENSION_REQUEST) { rv = CERT_GetCertificateRequestExtensions(certReq, &CRexts); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "%s\n", PORT_ErrorToString(rv)); goto cleanup; } rv = CERT_MergeExtensions(extHandle, CRexts); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "%s\n", PORT_ErrorToString(rv)); goto cleanup; } } CERT_FinishExtensions(extHandle); /* self-signing a cert request, find the private key */ if (*selfsignprivkey == NULL) { *selfsignprivkey = PK11_FindKeyByDERCert(slot, subjectCert, pwarg); if (!*selfsignprivkey) { PR_fprintf(PR_STDERR, "Failed to locate private key.\n"); rv = SECFailure; goto cleanup; } } certDER = SignCert(handle, subjectCert, selfsign, hashAlgTag, *selfsignprivkey, issuerNickName,pwarg); if (certDER) { if (ascii) { PR_fprintf(outFile, "%s\n%s\n%s\n", NS_CERT_HEADER, BTOA_DataToAscii(certDER->data, certDER->len), NS_CERT_TRAILER); } else { PR_Write(outFile, certDER->data, certDER->len); } } if (rv != SECSuccess) { PRErrorCode perr = PR_GetError(); PR_fprintf(PR_STDERR, "unable to create cert %s\n", perr); } cleanup: if (outFile) { PR_Close(outFile); } if (*selfsignprivkey) { SECKEY_DestroyPrivateKey(*selfsignprivkey); } if (certReq) { CERT_DestroyCertificateRequest(certReq); } if (subjectCert) { CERT_DestroyCertificate(subjectCert); } return rv; } /* * Generate the certificate request with subject */ static SECStatus CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, SECOidTag hashAlgTag, CERTName *subject, PRBool ascii, const char *certReqFileName) { SECOidTag signAlgTag; SECItem result; PRInt32 numBytes; SECStatus rv = SECSuccess; PRArenaPool *arena = NULL; void *extHandle = NULL; PRFileDesc *outFile = NULL; CERTSubjectPublicKeyInfo *spki = NULL; CERTCertificateRequest *cr = NULL; SECItem *encoding = NULL; /* If the certificate request file already exists, delete it */ if (PR_Access(certReqFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { PR_Delete(certReqFileName); } /* Open the certificate request file to write */ outFile = PR_Open(certReqFileName, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); if (!outFile) { PR_fprintf(PR_STDERR, "unable to open \"%s\" for writing (%ld, %ld).\n", certReqFileName, PR_GetError(), PR_GetOSError()); goto cleanup; } /* Create info about public key */ spki = SECKEY_CreateSubjectPublicKeyInfo(pubk); if (!spki) { PR_fprintf(PR_STDERR, "unable to create subject public key\n"); rv = SECFailure; goto cleanup; } /* Generate certificate request */ cr = CERT_CreateCertificateRequest(subject, spki, NULL); if (!cr) { PR_fprintf(PR_STDERR, "unable to make certificate request\n"); rv = SECFailure; goto cleanup; } arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { fprintf(stderr, "out of memory"); rv = SECFailure; goto cleanup; } extHandle = CERT_StartCertificateRequestAttributes(cr); if (extHandle == NULL) { PORT_FreeArena (arena, PR_FALSE); rv = SECFailure; goto cleanup; } CERT_FinishExtensions(extHandle); CERT_FinishCertificateRequestAttributes(cr); /* Der encode the request */ encoding = SEC_ASN1EncodeItem(arena, NULL, cr, SEC_ASN1_GET(CERT_CertificateRequestTemplate)); if (encoding == NULL) { PR_fprintf(PR_STDERR, "der encoding of request failed\n"); rv = SECFailure; goto cleanup; } /* Sign the request */ signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag); if (signAlgTag == SEC_OID_UNKNOWN) { PR_fprintf(PR_STDERR, "unknown Key or Hash type\n"); rv = SECFailure; goto cleanup; } rv = SEC_DerSignData(arena, &result, encoding->data, encoding->len, privk, signAlgTag); if (rv) { PR_fprintf(PR_STDERR, "signing of data failed\n"); rv = SECFailure; goto cleanup; } /* Encode request in specified format */ if (ascii) { char *obuf; char *name, *email, *org, *state, *country; SECItem *it; int total; it = &result; obuf = BTOA_ConvertItemToAscii(it); total = PL_strlen(obuf); name = CERT_GetCommonName(subject); if (!name) { name = strdup("(not specified)"); } email = CERT_GetCertEmailAddress(subject); if (!email) email = strdup("(not specified)"); org = CERT_GetOrgName(subject); if (!org) org = strdup("(not specified)"); state = CERT_GetStateName(subject); if (!state) state = strdup("(not specified)"); country = CERT_GetCountryName(subject); if (!country) country = strdup("(not specified)"); PR_fprintf(outFile, "\nCertificate request generated by Netscape certutil\n"); PR_fprintf(outFile, "Common Name: %s\n", name); PR_fprintf(outFile, "Email: %s\n", email); PR_fprintf(outFile, "Organization: %s\n", org); PR_fprintf(outFile, "State: %s\n", state); PR_fprintf(outFile, "Country: %s\n\n", country); PR_fprintf(outFile, "%s\n", NS_CERTREQ_HEADER); numBytes = PR_Write(outFile, obuf, total); if (numBytes != total) { PR_fprintf(PR_STDERR, "write error\n"); return SECFailure; } PR_fprintf(outFile, "\n%s\n", NS_CERTREQ_TRAILER); } else { numBytes = PR_Write(outFile, result.data, result.len); if (numBytes != (int)result.len) { PR_fprintf(PR_STDERR, "write error\n"); rv = SECFailure; goto cleanup; } } cleanup: if (outFile) { PR_Close(outFile); } if (privk) { SECKEY_DestroyPrivateKey(privk); } if (pubk) { SECKEY_DestroyPublicKey(pubk); } return rv; } /* * Create certificate request with subject */ SECStatus CreateCertRequest(PK11SlotInfo *slot, secuPWData *pwdata, CERTName *subject, char *certReqFileName, PRBool ascii) { SECStatus rv; SECKEYPrivateKey *privkey = NULL; SECKEYPublicKey *pubkey = NULL; KeyType keytype = rsaKey; int keysize = DEFAULT_KEY_BITS; int publicExponent = 0x010001; SECOidTag hashAlgTag = SEC_OID_UNKNOWN; privkey = GeneratePrivateKey(keytype, slot, keysize, publicExponent, NULL, &pubkey, NULL, pwdata); if (privkey == NULL) { PR_fprintf(PR_STDERR, "unable to generate key(s)\n"); rv = SECFailure; goto cleanup; } privkey->wincx = pwdata; PORT_Assert(pubkey != NULL); rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject, ascii, certReqFileName); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Failed to create Certificate Request\n"); } cleanup: return rv; } /* * Creates the certificate using CSR and adds the certificate to DB */ SECStatus AddCertificateToDB(PK11SlotInfo *slot, secuPWData *pwdata, char *certReqFileName, char *certFileName, char *issuerNameStr, CERTCertDBHandle *certHandle, const char *nickNameStr, char *trustStr, unsigned int serialNumber, PRBool selfsign, PRBool ascii) { SECStatus rv; SECKEYPrivateKey *privkey = NULL; SECKEYPublicKey *pubkey = NULL; SECOidTag hashAlgTag = SEC_OID_UNKNOWN; if (PR_Access(certFileName, PR_ACCESS_EXISTS) == PR_FAILURE) { rv = CreateCert(certHandle, slot, issuerNameStr, certReqFileName, certFileName, &privkey, &pwdata, hashAlgTag, serialNumber, 0, 3, NULL, ascii, selfsign); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Failed to create Certificate\n"); goto cleanup; } } rv = AddCert(slot, certHandle, nickNameStr, trustStr, certFileName, ascii, 0, &pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Failed to add Certificate\n"); } cleanup: return rv; } /* * Finds the certificate using nickname and saves it to the header file */ SECStatus AddCertificateToHeader(PK11SlotInfo *slot, secuPWData *pwdata, const char *headerFileName, CERTCertDBHandle *certHandle, const char *nickNameStr, PRBool sigVerify) { SECStatus rv = SECSuccess; PRFileDesc *headerFile = NULL; CERTCertificate *cert = NULL; HeaderType hType = CERTENC; /* If the intermediate header file already exists, delete it */ if (PR_Access(headerFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { PR_Delete(headerFileName); } headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); if (!headerFile) { PR_fprintf(PR_STDERR, "unable to open \"%s\" for writing (%ld, %ld).\n", headerFileName, PR_GetError(), PR_GetOSError()); rv = SECFailure; goto cleanup; } cert = CERT_FindCertByNicknameOrEmailAddr(certHandle, nickNameStr); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); rv = SECFailure; goto cleanup; } if (sigVerify) { hType = CERTVFY; } WriteToHeaderFile(cert->derCert.data, cert->derCert.len, hType, headerFile); cleanup: if (headerFile) { PR_Close(headerFile); } if (cert) { CERT_DestroyCertificate(cert); } return rv; } /* * Finds the public key from the certificate saved in the header file * and encrypts with it the contents of inFileName to encryptedFileName. */ SECStatus FindKeyAndEncrypt(PK11SlotInfo *slot, secuPWData *pwdata, const char *headerFileName, const char *encryptedFileName, const char *inFileName) { SECStatus rv; PRFileDesc *headerFile = NULL; PRFileDesc *encFile = NULL; PRFileDesc *inFile = NULL; CERTCertificate *cert = NULL; SECItem data; unsigned char ptext[MODBLOCKSIZE]; unsigned char encBuf[MODBLOCKSIZE]; unsigned int ptextLen; int index; unsigned int nWritten; unsigned int pad[1]; SECItem padItem; unsigned int paddingLength = 0; SECKEYPublicKey *pubkey = NULL; /* If the intermediate encrypted file already exists, delete it*/ if (PR_Access(encryptedFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { PR_Delete(encryptedFileName); } /* Read certificate from header file */ rv = ReadFromHeaderFile(headerFileName, CERTENC, &data, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not read certificate from header file\n"); goto cleanup; } /* Read in an ASCII cert and return a CERTCertificate */ cert = CERT_DecodeCertFromPackage((char *)data.data, data.len); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); rv = SECFailure; goto cleanup; } /* Extract the public key from certificate */ pubkey = CERT_ExtractPublicKey(cert); if (!pubkey) { PR_fprintf(PR_STDERR, "could not get key from certificate\n"); rv = SECFailure; goto cleanup; } /* Open the encrypted file for writing */ encFile = PR_Open(encryptedFileName, PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); if (!encFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", encryptedFileName); rv = SECFailure; goto cleanup; } /* Open the input file for reading */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); rv = SECFailure; goto cleanup; } /* Open the header file to write padding */ headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_APPEND, 00660); if (!headerFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", headerFileName); rv = SECFailure; goto cleanup; } /* Read input file */ while ((ptextLen = PR_Read(inFile, ptext, sizeof(ptext))) > 0) { if (ptextLen != MODBLOCKSIZE) { paddingLength = MODBLOCKSIZE - ptextLen; for ( index=0; index < paddingLength; index++) { ptext[ptextLen+index] = (unsigned char)paddingLength; } ptextLen = MODBLOCKSIZE; } rv = PK11_PubEncryptRaw(pubkey, encBuf, ptext, ptextLen, NULL); nWritten = PR_Write(encFile, encBuf, ptextLen); } /* Write the padding to header file */ pad[0] = paddingLength; padItem.type = siBuffer; padItem.data = (unsigned char *)pad; padItem.len = sizeof(pad[0]); WriteToHeaderFile(padItem.data, padItem.len, PAD, headerFile); cleanup: if (headerFile) { PR_Close(headerFile); } if (encFile) { PR_Close(encFile); } if (inFile) { PR_Close(inFile); } if (pubkey) { SECKEY_DestroyPublicKey(pubkey); } if (cert) { CERT_DestroyCertificate(cert); } return rv; } /* * Finds the private key from db and signs the contents * of inFileName and writes to signatureFileName */ SECStatus FindKeyAndSign(PK11SlotInfo *slot, CERTCertDBHandle* certHandle, secuPWData *pwdata, const char *nickNameStr, const char *headerFileName, const char *inFileName) { SECStatus rv; PRFileDesc *headerFile = NULL; PRFileDesc *inFile = NULL; CERTCertificate *cert = NULL; unsigned int signatureLen = 0; SECKEYPrivateKey *privkey = NULL; SECItem sigItem; SECOidTag hashOIDTag; /* Open the header file to write padding */ headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_APPEND, 00660); if (!headerFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", headerFileName); rv = SECFailure; goto cleanup; } /* Get the certificate by nick name and write to header file */ cert = CERT_FindCertByNicknameOrEmailAddr(certHandle, nickNameStr); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate by name - %s\n", nickNameStr); rv = SECFailure; goto cleanup; } WriteToHeaderFile(cert->derCert.data, cert->derCert.len, CERTVFY, headerFile); /* Find private key from certificate */ privkey = PK11_FindKeyByAnyCert(cert, NULL); if (privkey == NULL) { fprintf(stderr, "Couldn't find private key for cert\n"); rv = SECFailure; goto cleanup; } /* Sign the contents of the input file */ rv = SignData(inFileName, privkey, &sigItem); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "could not sign the contents from file - %s \n", inFileName); goto cleanup; } /* write signature to header file */ WriteToHeaderFile(sigItem.data, sigItem.len, SIG, headerFile); cleanup: if (headerFile) { PR_Close(headerFile); } if (privkey) { SECKEY_DestroyPrivateKey(privkey); } if (cert) { CERT_DestroyCertificate(cert); } return rv; } /* * Finds the public key from certificate and verifies signature */ SECStatus FindKeyAndVerify(PK11SlotInfo *slot, CERTCertDBHandle* certHandle, secuPWData *pwdata, const char *headerFileName, const char *inFileName) { SECStatus rv = SECFailure; PRFileDesc *headerFile = NULL; PRFileDesc *inFile = NULL; CERTCertificate *cert = NULL; SECKEYPublicKey *pubkey = NULL; SECItem sigItem; SECItem certData; /* Open the input file */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); rv = SECFailure; goto cleanup; } /* Open the header file to read the certificate and signature */ headerFile = PR_Open(headerFileName, PR_RDONLY, 0); if (!headerFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", headerFileName); rv = SECFailure; goto cleanup; } /* Read certificate from header file */ rv = ReadFromHeaderFile(headerFileName, CERTVFY, &certData, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not read certificate from header file\n"); goto cleanup; } /* Read in an ASCII cert and return a CERTCertificate */ cert = CERT_DecodeCertFromPackage((char *)certData.data, certData.len); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); rv = SECFailure; goto cleanup; } /* Extract the public key from certificate */ pubkey = CERT_ExtractPublicKey(cert); if (!pubkey) { PR_fprintf(PR_STDERR, "Could not get key from certificate\n"); rv = SECFailure; goto cleanup; } /* Read signature from header file */ rv = ReadFromHeaderFile(headerFileName, SIG, &sigItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not read signature from header file\n"); goto cleanup; } /* Verify with the public key */ rv = VerifyData(inFileName, pubkey, &sigItem, pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Couldn't verify the signature for file - %s\n", inFileName); goto cleanup; } cleanup: if (headerFile) { PR_Close(headerFile); } if (pubkey) { SECKEY_DestroyPublicKey(pubkey); } if (cert) { CERT_DestroyCertificate(cert); } return rv; } /* * Finds the private key corresponding to the certificate saved in the header file * and decrypts with it the contents of encryptedFileName to outFileName. */ SECStatus FindKeyAndDecrypt(PK11SlotInfo *slot, secuPWData *pwdata, const char *headerFileName, const char *encryptedFileName, const char *outFileName) { SECStatus rv; PRFileDesc *encFile = NULL; PRFileDesc *outFile = NULL; SECKEYPrivateKey *pvtkey = NULL; unsigned int inFileLength = 0; unsigned int paddingLength = 0; unsigned int count = 0; unsigned int temp = 0; unsigned char ctext[MODBLOCKSIZE]; unsigned char decBuf[MODBLOCKSIZE]; unsigned int ctextLen; unsigned int decBufLen; SECItem padItem; SECItem data; SECItem signature; CERTCertificate *cert = NULL; /* Read certificate from header file */ rv = ReadFromHeaderFile(headerFileName, CERTENC, &data, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not read certificate from header file\n"); goto cleanup; } /* Read padding from header file */ rv = ReadFromHeaderFile(headerFileName, PAD, &padItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve PAD detail from header file\n"); goto cleanup; } paddingLength = (unsigned int)padItem.data[0]; inFileLength = FileSize(encryptedFileName); /* Read in an ASCII cert and return a CERTCertificate */ cert = CERT_DecodeCertFromPackage((char *)data.data, data.len); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); rv = SECFailure; goto cleanup; } /* Find private key from certificate */ pvtkey = PK11_FindKeyByAnyCert(cert, NULL); if (pvtkey == NULL) { fprintf(stderr, "Couldn't find private key for cert\n"); rv = SECFailure; goto cleanup; } /* Open the out file to write */ outFile = PR_Open(outFileName, PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); if (!outFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", outFileName); rv = SECFailure; goto cleanup; } /* Open the encrypted file for reading */ encFile = PR_Open(encryptedFileName, PR_RDONLY, 0); if (!encFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", encryptedFileName); rv = SECFailure; goto cleanup; } /* Read the encrypt file, decrypt and write to out file */ while ((ctextLen = PR_Read(encFile, ctext, sizeof(ctext))) > 0) { count += ctextLen; rv = PK11_PubDecryptRaw(pvtkey, decBuf, &decBufLen, sizeof(decBuf), ctext, ctextLen); if (rv != SECSuccess) { fprintf(stderr, "Couldn't decrypt\n"); goto cleanup; } if (decBufLen == 0) { break; } if (count == inFileLength) { decBufLen = decBufLen - paddingLength; } /* write the plain text to out file */ temp = PR_Write(outFile, decBuf, decBufLen); if (temp != decBufLen) { PR_fprintf(PR_STDERR, "write error\n"); rv = SECFailure; break; } } cleanup: if (encFile) { PR_Close(encFile); } if (outFile) { PR_Close(outFile); } if (pvtkey) { SECKEY_DestroyPrivateKey(pvtkey); } if (cert) { CERT_DestroyCertificate(cert); } return rv; } /* Map option letter to command */ static CommandType option2Command(char c) { switch (c) { case 'G': return GENERATE_CSR; case 'A': return ADD_CERT_TO_DB; case 'H': return SAVE_CERT_TO_HEADER; case 'E': return ENCRYPT; case 'D': return DECRYPT; case 'S': return SIGN; case 'V': return VERIFY; default: return UNKNOWN; } } /* * This example illustrates basic encryption/decryption and MACing * Generates the RSA key pair as token object and outputs public key as cert request. * Reads cert request file and stores certificate in DB. * Input, store and trust CA certificate. * Write certificate to intermediate header file * Extract public key from certificate, encrypts the input file and write to external file. * Finds the matching private key, decrypts and write to external file * * How this sample is different from sample 5 ? * * 1. As in sample 5, output is a PKCS#10 CSR * 2. Input and store a cert in cert DB and also used to input, store and trust CA cert. * 3. Like sample 5, but puts cert in header * 4. Like sample 5, but finds key matching cert in header */ int main(int argc, char **argv) { SECStatus rv; PLOptState *optstate; PLOptStatus status; PRBool initialized = PR_FALSE; CommandType cmd = UNKNOWN; const char *dbdir = NULL; secuPWData pwdata = { PW_NONE, 0 }; char *subjectStr = NULL; CERTName *subject = 0; unsigned int serialNumber = 0; char *serialNumberStr = NULL; char *trustStr = NULL; CERTCertDBHandle *certHandle; const char *nickNameStr = NULL; char *issuerNameStr = NULL; PRBool selfsign = PR_FALSE; PRBool ascii = PR_FALSE; PRBool sigVerify = PR_FALSE; const char *headerFileName = NULL; const char *encryptedFileName = NULL; const char *inFileName = NULL; const char *outFileName = NULL; char *certReqFileName = NULL; char *certFileName = NULL; const char *noiseFileName = NULL; PK11SlotInfo *slot = NULL; char * progName = strrchr(argv[0], '/'); progName = progName ? progName + 1 : argv[0]; /* Parse command line arguments */ optstate = PL_CreateOptState(argc, argv, "GAHEDSVad:i:o:f:p:z:s:r:n:x:m:t:c:u:e:b:v:"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case 'a': ascii = PR_TRUE; break; case 'G': /* Generate a CSR */ case 'A': /* Add cert to database */ case 'H': /* Save cert to the header file */ case 'E': /* Encrypt with public key from cert in header file */ case 'S': /* Sign with private key */ case 'D': /* Decrypt with the matching private key */ case 'V': /* Verify with the matching public key */ cmd = option2Command(optstate->option); break; case 'd': dbdir = strdup(optstate->value); break; case 'f': pwdata.source = PW_FROMFILE; pwdata.data = strdup(optstate->value); break; case 'p': pwdata.source = PW_PLAINTEXT; pwdata.data = strdup(optstate->value); break; case 'i': inFileName = strdup(optstate->value); break; case 'b': headerFileName = strdup(optstate->value); break; case 'e': encryptedFileName = strdup(optstate->value); break; case 'o': outFileName = strdup(optstate->value); break; case 'z': noiseFileName = strdup(optstate->value); break; case 's': subjectStr = strdup(optstate->value); subject = CERT_AsciiToName(subjectStr); break; case 'r': certReqFileName = strdup(optstate->value); break; case 'c': certFileName = strdup(optstate->value); break; case 'u': issuerNameStr = strdup(optstate->value); break; case 'n': nickNameStr = strdup(optstate->value); break; case 'x': selfsign = PR_TRUE; break; case 'm': serialNumberStr = strdup(optstate->value); serialNumber = atoi(serialNumberStr); break; case 't': trustStr = strdup(optstate->value); break; case 'v': sigVerify = PR_TRUE; break; default: Usage(progName); break; } } PL_DestroyOptState(optstate); if (cmd == UNKNOWN || !dbdir) Usage(progName); /* Open DB for read/write and authenticate to it */ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); initialized = PR_TRUE; rv = NSS_InitReadWrite(dbdir); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "NSS_InitReadWrite Failed\n"); goto cleanup; } PK11_SetPasswordFunc(GetModulePassword); slot = PK11_GetInternalKeySlot(); if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", PK11_GetTokenName(slot)); goto cleanup; } } switch (cmd) { case GENERATE_CSR: ValidateGenerateCSRCommand(progName, dbdir, subject, subjectStr, certReqFileName); /* Generate a CSR */ rv = CreateCertRequest(slot, &pwdata, subject, certReqFileName, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Create Certificate Request: Failed\n"); goto cleanup; } break; case ADD_CERT_TO_DB: ValidateAddCertToDBCommand(progName, dbdir, nickNameStr, trustStr, certFileName, certReqFileName, issuerNameStr, serialNumberStr, selfsign); /* Add cert to database */ rv = AddCertificateToDB(slot, &pwdata, certReqFileName, certFileName, issuerNameStr, certHandle, nickNameStr, trustStr, serialNumber, selfsign, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Add Certificate to DB: Failed\n"); goto cleanup; } break; case SAVE_CERT_TO_HEADER: ValidateSaveCertToHeaderCommand(progName, dbdir, nickNameStr, headerFileName); /* Save cert to the header file */ rv = AddCertificateToHeader(slot, &pwdata, headerFileName, certHandle, nickNameStr, sigVerify); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Saving Certificate to header: Failed\n"); goto cleanup; } break; case ENCRYPT: ValidateEncryptCommand(progName, dbdir, nickNameStr, headerFileName, inFileName, encryptedFileName); /* Encrypt with public key from cert in header file */ rv = FindKeyAndEncrypt(slot, &pwdata, headerFileName, encryptedFileName, inFileName); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Find public key and Encrypt : Failed\n"); goto cleanup; } break; case SIGN: ValidateSignCommand(progName, dbdir, nickNameStr, headerFileName, inFileName); /* Sign with private key */ rv = FindKeyAndSign(slot, certHandle, &pwdata, nickNameStr, headerFileName, inFileName); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Find private key and sign : Failed\n"); goto cleanup; } break; case DECRYPT: ValidateDecryptCommand(progName, dbdir, headerFileName, encryptedFileName, outFileName); /* Decrypt with the matching private key */ rv = FindKeyAndDecrypt(slot, &pwdata, headerFileName, encryptedFileName, outFileName); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Find private key and Decrypt : Failed\n"); } break; case VERIFY: ValidateVerifyCommand(progName, dbdir, headerFileName, inFileName); /* Verify with the matching public key */ rv = FindKeyAndVerify(slot, certHandle, &pwdata, headerFileName, inFileName); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Find public key and verify signature : Failed\n"); goto cleanup; } } cleanup: if (slot) { PK11_FreeSlot(slot); } if (initialized) { SECStatus rvShutdown = NSS_Shutdown(); if (rvShutdown != SECSuccess) { PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown() - %s", PORT_ErrorToString(rvShutdown)); rv = SECFailure; } PR_Cleanup(); } return rv; }</opfilename></encryptfilename></headerfilename></dbdirpath></ipfilename></headerfilename></dbdirpath></nickname></ipfilename></headerfilename></dbdirpath></nickname></encryptfilename></ipfilename></headerfilename></dbdirpath></headerfilename></nickname></dbdirpath></serialnumber></issuernickname></csr></cert></trust></nickname></dbdirpath></cert></trust></nickname></dbdirpath></csr></subject></dbdirpath></csr></issuernickname></trustargs></nickname></serialnumber></opfilename></encryptfilename></headerfilename></ipfilename></noisefilename></dbpwdfile></dbpwd></dbdirpath></ipfilename></headerfilename></infilename></headerfilename></opfilename></encryptfilename></headerfilename></encryptfilename></ipfilename></headerfilename></headerfilename></nickname></serialnumber></issuernickname></csr></cert></trust></nickname></csr></subject></opfilename></encryptfilename></headerfilename></ipfilename></headerfilename></ipfilename></headerfilename></encryptfilename></ipfilename></headerfilename></headerfilename></nickname></serialnumber></issuernickname></csr></cert></trust></nickname></csr></subject></noisefilename></dbpwdfile></dbpwd></dbdirpath></g|a|h|e|ds|v></sechash.h></secoidt.h></secmodt.h></secoid.h></secport.h></secerr.h></base64.h></cert.h></pk11priv.h></keyhi.h></cryptohi.h></plstr.h></prtypes.h></prlog.h></prinit.h></prerror.h></plgetopt.h></prthread.h>
