/* Example code to illustrate encryption/decryption using NSS. * The example skips the details of obtaining the Key & IV to use, and * just uses a hardcoded Key & IV. * Note: IV is only needed if Cipher Blocking Chaining (CBC) mode of encryption * is used * * The recommended approach is to store and transport WRAPPED (encrypted) * symmteric keys (IVs can be in the clear). However, it is a common, and * dangerous, practice to use raw DES Keys. This example shows the use of * a RAW key. */ #include <nss.h> #include <pk11pub.h> #include <nspr.h> #include <prprf.h> #include <prlog.h> #include <plstr.h> /* example Key & IV */ unsigned char gKey[] = {0xe8, 0xa7, 0x7c, 0xe2, 0x05, 0x63, 0x6a, 0x31}; unsigned char gIV[] = {0xe4, 0xbb, 0x3b, 0xd3, 0xc3, 0x71, 0x2e, 0x58}; int main(int argc, char **argv) { CK_MECHANISM_TYPE cipherMech; PK11SlotInfo* slot = NULL; PK11SymKey* symKey = NULL; SECItem* secParam = NULL; PK11Context* ctx = NULL; SECItem keyItem, ivItem; SECStatus rv, rv1, rv2; unsigned char data[1024], encrypted[1024], decrypted[1024]; int i, outLen1; unsigned int resultLen, outLen2; PRBool nssInitialized = PR_FALSE; do { /* Initialize NSS * If your application code has already initialized NSS, you can skip it * here. * This code uses the simplest of the Init functions, which does not * require a NSS database to exist */ rv = NSS_NoDB_Init("."); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "NSS initialization failed (err %d)\n", rv); break; } nssInitialized = PR_TRUE; /* choose mechanism: CKM_DES_CBC_PAD, CKM_DES3_ECB, CKM_DES3_CBC..... * Note that some mechanisms (*_PAD) imply the padding is handled for you * by NSS. If you choose something else, then data padding is the * application's responsibility */ cipherMech = CKM_DES_CBC_PAD; slot = PK11_GetBestSlot(cipherMech, NULL); /* slot = PK11_GetInternalKeySlot(); is a simpler alternative but in * theory, it *may not* return the optimal slot for the operation. For * DES ops, Internal slot is typically the best slot */ if (!slot) { rv = PR_GetError(); PR_fprintf(PR_STDERR, "Unable to find security device (err %d)\n", rv); break; } /* NSS passes blobs around as SECItems. These contain a pointer to * data and a length. Turn the raw key into a SECItem. */ keyItem.type = siBuffer; keyItem.data = gKey; keyItem.len = sizeof(gKey); /* Turn the raw key into a key object. We use PK11_OriginUnwrap * to indicate the key was unwrapped - which is what should be done * normally anyway - using raw keys isn't a good idea */ symKey = PK11_ImportSymKey(slot, cipherMech, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, NULL); if (!symKey) { rv = PR_GetError(); PR_fprintf(PR_STDERR, "Failure to import key into NSS (err %d)\n", rv); break; } /* set up the PKCS11 encryption parameters. * when not using CBC mode, ivItem.data and ivItem.len can be 0, or you * can simply pass NULL for the iv parameter in PK11_ParamFromIV function */ ivItem.type = siBuffer; ivItem.data = gIV; ivItem.len = sizeof(gIV); secParam = PK11_ParamFromIV(cipherMech, &ivItem); if (!secParam) { rv = PR_GetError(); PR_fprintf(PR_STDERR, "Failure to set up PKCS11 param (err %d)\n", rv); break; } /* sample data we'll encrypt and decrypt */ PL_strcpy((char *)data, "Encrypt me!"); PR_fprintf(PR_STDERR, "Clear Data: %s\n", data); /* ========================= START SECTION ============================= */ /* If using the the same key and iv over and over, stuff before this */ /* section and after this section needs to be done only ONCE */ /* ENCRYPT data into cipherText. * cipherText length must be at least (data len + 8) */ outLen1 = outLen2 = 0; /* Create cipher context */ ctx = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, symKey, secParam); rv1 = PK11_CipherOp(ctx, encrypted, &outLen1, sizeof(encrypted), data, (int)PL_strlen((const char *)data)+1); rv2 = PK11_DigestFinal(ctx, encrypted+outLen1, &outLen2, sizeof(encrypted) - outLen1); PK11_DestroyContext(ctx, PR_TRUE); resultLen = outLen1 + outLen2; if (rv1 != SECSuccess || rv2 != SECSuccess) break; PR_fprintf(PR_STDOUT, "Encrypted Data: "); for (i = 0; i < resultLen; i++) PR_fprintf(PR_STDOUT,"%02x ", encrypted[i]); PR_fprintf(PR_STDOUT,"\n"); /* Release it before reusing ctx for a new one */ PK11_DestroyContext(ctx, PR_TRUE); /* DECRYPT encrypted into decrypted. decrypted * length must be at least encrypted length */ outLen1 = outLen2 = 0; /* Create cipher context */ ctx = PK11_CreateContextBySymKey(cipherMech, CKA_DECRYPT, symKey, secParam); rv1 = PK11_CipherOp(ctx, decrypted, &outLen1, sizeof(decrypted), encrypted, resultLen); rv2 = PK11_DigestFinal(ctx, decrypted+outLen1, &outLen2, resultLen-outLen1); resultLen = outLen1 + outLen2; if (rv1 != SECSuccess || rv2 != SECSuccess) break; PR_ASSERT(resultLen == PLstrlen(data)); PR_ASSERT(PL_strncmp(decrypted, data, resultLen)==0); PR_fprintf(PR_STDERR,"Decrypted Data: %s\n", decrypted); /* =========================== END SECTION ============================= */ } while (0); /* Cleanup after ourselves */ if (symKey) PK11_FreeSymKey(symKey); if (secParam) SECITEM_FreeItem(secParam, PR_TRUE); if (ctx) PK11_DestroyContext(ctx, PR_TRUE); if (nssInitialized) (void) NSS_Shutdown(); return rv; }