| RSA BSAFE Cert-C |
Certificate Components for C |
| Crypto-C 6.2.1 Developer's Guide | ||
| Search |
/* $Id: keywrap.c,v 1.4 2004/03/02 05:18:41 gsingh Exp $ */
/* keywrap.c
** Copyright (c) 1999-2003, RSA Security Inc.
**
** This file is used to demonstrate how to interface to an RSA Security
** licensed development product. You have a royalty-free right to use,
** modify, reproduce and distribute this demonstration file (including
** any modified version), provided that you agree that RSA Security has
** no warranty, implied or otherwise, or liability for this demonstration
** file or any modified version.
**
** This program takes a PKCS #8 PrivateKeyInfo and encrypts it to produce an
** EncryptedPrivateKeyInfo and vice versa. This is meant as a demonstration
** of the ASN.1 APIs.
**
** The following is from section 7 of PKCS #8:
**
** EncryptedPrivateKeyInfo ::= SEQUENCE {
** encryptionAlgorithm EncryptionAlgorithmIdentifier,
** encryptedData EncryptedData }
**
** EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
**
** EncryptedData ::= OCTET STRING
**
** This program will encode the encrypted private key in an octet string and
** create a sequence of the algorithm identifier and octet string to produce
** the BER-encoded EncryptedPrivateKeyInfo. To unwrap a key, this program
** will extract the contents of the sequence and use the algorithm identifier
** to initialize the algorithm object that will be used to decrypt the data
** contained in the octet string.
**
** For simplicity, this example uses the PKCS #5 (v1.5) password-based
** encryption scheme with MD5 and RC2. This is AI_MD5WithRC2_CBCPad in the
** Crypto-C toolkit. The RC2 effective key bits is set to 80, and the PBE
** iteration count is set to 1000. This program can be modified to
** be more flexible or use other options.
**
** When compiling, define the macro RSA_REQUIRE_FILE_LOG (-D compile
** option, or equivelent) to force the program to return an error code
** if file logging cannot be initialized. For example, if the file
** containing the log message format strings cannot be located (certc.msg
** or equivalent).
*/
#include "certc.h"
#include "filelog.h"
#include "rsacsp.h"
#include "demoutil.h" /* see samples/common/include */
#include "keyutil.h" /* see samples/common/include */
#include "dummyrand.h" /* see samples/common/include */
#ifdef _MSC_VER
# pragma warning (disable: 171) /* invalid type conversion (often of very similar ptrs) */
#endif
/* algorithm parameters used for encryption */
#define RC2_EKB 80 /* RC2 effective key bits */
#define PBE_IC 1000 /* Iteration count */
#define BLOCK_SIZE 8 /* RC2 block size */
/* name to reference the dummy random provider instance */
#define DUMMY_RANDOM_NAME "Dummy Random Provider"
/* name to reference the default random provider instance */
#define DEFAULT_CRYPTO_NAME "BSAFE Crypto-C"
static int WrapKey (CERTC_CTX ctx);
/* Note that the calling procedure must free algId.data and encryptedKey.data */
static int EncryptKey (CERTC_CTX ctx, ITEM *privateKey, ITEM *algId,
ITEM *encryptedKey);
static int UnwrapKey (CERTC_CTX ctx);
/* Note that the calling procedure must free decryptedKey.data */
static int DecryptKey (CERTC_CTX ctx, ITEM *encryptedKey, ITEM *algId,
ITEM *decryptedKey);
int main (int argc, char *argv[])
{
int status = 0;
char command[RSA_DEMO_MAX_LINE_LEN];
CERTC_CTX ctx = NULL;
FILE_LOG_PARAMS logParams = {NULL, NULL};
SERVICE_HANDLER logHandler = {
SPT_LOG, "Default File Log", S_InitializeFileLog
};
status = RSA_SetOptions (&logParams, argc, argv);
if (status != 0)
goto CLEANUP;
status = C_InitializeCertC (NULL, NULL, 0, &ctx);
if (status != 0)
goto CLEANUP;
/* Attempt to initialize file logging, but unless RSA_REQUIRE_FILE_LOG is
* defined, treat it as a non-fatal condition.
*/
status = C_RegisterService (ctx, &logHandler, (POINTER)&logParams,
SERVICE_ORDER_FIRST);
#ifdef RSA_REQUIRE_FILE_LOG
if (status != 0)
goto CLEANUP;
#endif
RSA_PrintMessage ("Keywrap Demo\n");
RSA_PrintMessage ("============\n");
for (;;) {
RSA_PrintMessage (" A - Encrypt a private key\n");
RSA_PrintMessage (" B - Decrypt an encrypted private key\n");
status = RSA_GetCommand (command, sizeof (command),
"Enter choice (blank to quit)");
if (status != 0)
goto CLEANUP;
switch (command[0]) {
case 'a':
case 'A':
status = WrapKey (ctx);
break;
case 'b':
status = UnwrapKey (ctx);
break;
case '\0':
case 'q':
case 'Q':
goto CLEANUP;
default:
RSA_PrintMessage ("Unrecognized Option: %c\n", command[0]);
status = RSA_DEMO_E_INVALID_PARAMETER;
}
if (status != 0)
RSA_PrintMessage ("Operation not completed.\n");
else
RSA_PrintMessage ("Operation successful!\n");
}
CLEANUP:
if (status != 0)
RSA_PrintError ("keywrap.c", status);
C_FinalizeCertC (&ctx);
return status;
} /* end main */
static int WrapKey (CERTC_CTX ctx)
{
int status = 0;
B_KEY_OBJ tmpPrivate = NULL;
LIST_OBJ items = NULL;
ITEM privateKey = {NULL, 0}, encryptedKey = {NULL, 0}, algId = {NULL, 0};
ITEM octetString = {NULL, 0}, encryptedKeyBer = {NULL, 0};
status = RSA_GetFileToAllocBuffer
(&privateKey.data, &privateKey.len,
"Enter name of PrivateKeyInfo binary to encrypt");
if (status != 0)
goto CLEANUP;
/* use tmpPrivate to see if the data given is a valid PrivateKeyInfo */
status = B_CreateKeyObject (&tmpPrivate);
if (status != 0)
goto CLEANUP;
status = RSA_SetKeyBer (RSA_DEMO_PRIVATE_KEY, tmpPrivate, privateKey);
if (status != 0)
goto CLEANUP;
B_DestroyKeyObject (&tmpPrivate);
status = EncryptKey (ctx, &privateKey, &algId, &encryptedKey);
if (status != 0)
goto CLEANUP;
/* wrap encryptedKey in an octet string */
status = C_DEREncodeString (ctx, VT_OCTET_STRING, VTC_UNIVERSAL,
encryptedKey.data, encryptedKey.len,
&octetString.data, &octetString.len);
if (status != 0)
goto CLEANUP;
/* Create a sequence containing the PBE algorithm identifier and the
* octet string.
*/
status = C_CreateListObject (&items);
if (status != 0)
goto CLEANUP;
status = C_AddItemToList (items, &algId, NULL);
if (status != 0)
goto CLEANUP;
status = C_AddItemToList (items, &octetString, NULL);
if (status != 0)
goto CLEANUP;
status = C_DEREncodeList (ctx, VT_SEQUENCE, VTC_UNIVERSAL, items,
&encryptedKeyBer.data, &encryptedKeyBer.len);
if (status != 0)
goto CLEANUP;
status = RSA_WriteDataToFile
(encryptedKeyBer.data, encryptedKeyBer.len,
"Enter file name to store EncryptedPrivateKeyInfo");
CLEANUP:
if (status != 0)
RSA_PrintError ("WrapKey", status);
T_memset (privateKey.data, 0, privateKey.len);
T_memset (encryptedKey.data, 0, encryptedKey.len);
T_free (privateKey.data);
T_free (encryptedKey.data);
T_free (algId.data);
T_free (octetString.data);
T_free (encryptedKeyBer.data);
B_DestroyKeyObject (&tmpPrivate);
C_DestroyListObject (&items);
return status;
} /* end WrapKey */
static int EncryptKey (CERTC_CTX ctx, ITEM *privateKey, ITEM *algId,
ITEM *encryptedKey)
{
int status = 0;
unsigned char password[RSA_DEMO_MAX_LINE_LEN], salt[BLOCK_SIZE];
B_ALGORITHM_OBJ encryptionObj = NULL, randomObj = NULL;
B_KEY_OBJ pbeKey = NULL;
B_RC2_PBE_PARAMS pbeParams;
ITEM passwordItem = {NULL, 0}, *bsafeAlgId = NULL;
B_ALGORITHM_CHOOSER chooser;
SERVICE_HANDLER defaultCrypto = {
SPT_CRYPTO, DEFAULT_CRYPTO_NAME, S_InitializeDefaultCSP
};
unsigned int bufSize = 0, outputLenUpdate = 0, outputLenFinal = 0;
/* for graceful error handling */
if ((privateKey == NULL) || (algId == NULL) || (encryptedKey == NULL))
return RSA_DEMO_E_INVALID_PARAMETER;
algId->data = NULL;
algId->len = 0;
encryptedKey->data = NULL;
encryptedKey->len = 0;
/* We will get E_DUPLICATE_SERVICE if we already have a default crypto
provider registered. */
status = C_RegisterService (ctx, &defaultCrypto, NULL, SERVICE_ORDER_FIRST);
if (status != 0 && status != E_DUPLICATE_SERVICE)
goto CLEANUP;
/* first, we gather the B_RC2_PBE_PARAMS info */
status = C_GetRandomObject (ctx, &randomObj);
if (status != 0)
goto CLEANUP;
status = B_GenerateRandomBytes (randomObj, salt, sizeof (salt), NULL);
if (status != 0)
goto CLEANUP;
pbeParams.effectiveKeyBits = RC2_EKB;
pbeParams.iterationCount = PBE_IC;
pbeParams.salt = salt;
/* set up key object */
status = RSA_GetCommand ((char *)password, sizeof (password),
"Enter password for encryption");
if (status != 0)
goto CLEANUP;
passwordItem.data = password;
passwordItem.len = T_strlen ((char *)password);
status = B_CreateKeyObject (&pbeKey);
if (status != 0)
goto CLEANUP;
status = B_SetKeyInfo (pbeKey, KI_Item, (POINTER)&passwordItem);
if (status != 0)
goto CLEANUP;
/* encrypt the privateKey */
status = B_CreateAlgorithmObject (&encryptionObj);
if (status != 0)
goto CLEANUP;
status = B_SetAlgorithmInfo (encryptionObj, AI_MD5WithRC2_CBCPad,
(POINTER)&pbeParams);
if (status != 0)
goto CLEANUP;
/* the RSA CSP chooser has the Crypto-C AMs for MD5 and RC2 */
status = C_GetChooser (ctx, &chooser);
if (status != 0)
goto CLEANUP;
status = B_EncryptInit (encryptionObj, pbeKey, chooser, NULL);
if (status != 0)
goto CLEANUP;
/* The encrypted data can be as much as BLOCK_SIZE bytes longer than the
* data to encrypt.
*/
bufSize = privateKey->len + BLOCK_SIZE;
encryptedKey->data = T_malloc (bufSize);
if (encryptedKey->data == NULL) {
status = RSA_DEMO_E_ALLOC;
goto CLEANUP;
}
status = B_EncryptUpdate (encryptionObj, encryptedKey->data,
&outputLenUpdate, bufSize, privateKey->data,
privateKey->len, NULL, NULL);
if (status != 0)
goto CLEANUP;
status = B_EncryptFinal (encryptionObj, encryptedKey->data + outputLenUpdate,
&outputLenFinal, bufSize - outputLenUpdate, NULL,
NULL);
if (status != 0)
goto CLEANUP;
/* total length of ciphertext */
encryptedKey->len = outputLenUpdate + outputLenFinal;
/* get the algorithm identifier */
status = B_GetAlgorithmInfo ((POINTER *)&bsafeAlgId, encryptionObj,
AI_MD5WithRC2_CBCPadBER);
if (status != 0)
goto CLEANUP;
/* Since bsafeAlgId points to memory belonging to Crypto-C, effectively a
* read-only pointer, we must make our own copy.
*/
algId->len = bsafeAlgId->len;
algId->data = T_malloc (algId->len);
if (algId->data == NULL) {
status = RSA_DEMO_E_ALLOC;
goto CLEANUP;
}
T_memcpy (algId->data, bsafeAlgId->data, algId->len);
CLEANUP:
if (status != 0) {
T_free (algId->data);
T_free (encryptedKey->data);
algId->data = NULL;
algId->len = 0;
encryptedKey->data = NULL;
encryptedKey->len = 0;
RSA_PrintError ("EncryptKey", status);
}
T_memset (password, 0, sizeof (password));
B_DestroyKeyObject (&pbeKey);
B_DestroyAlgorithmObject (&encryptionObj);
return status;
} /* end EncryptKey */
static int UnwrapKey (CERTC_CTX ctx)
{
int status = 0, tmpTag = 0;
unsigned int tmpTagClass = 0;
ITEM encryptedKeyBer = {NULL, 0}, encryptedKey = {NULL, 0};
ITEM decryptedKey = {NULL, 0};
ITEM *algId = NULL, *octetString = NULL;
LIST_OBJ items = NULL;
status = RSA_GetFileToAllocBuffer
(&encryptedKeyBer.data, &encryptedKeyBer.len,
"Enter name of file containing EncryptedPrivateKeyInfo binary");
if (status != 0)
goto CLEANUP;
/* Extract the PBE algorithm ID and the octet string containing the
* octet string which has the data to decrypt.
*/
status = C_CreateListObject (&items);
if (status != 0)
goto CLEANUP;
status = C_BERDecodeList (ctx, encryptedKeyBer.data, encryptedKeyBer.len,
&tmpTag, &tmpTagClass, items);
if (status != 0)
goto CLEANUP;
status = C_GetListObjectEntry (items, 0, (POINTER *)&algId);
if (status != 0)
goto CLEANUP;
status = C_GetListObjectEntry (items, 1, (POINTER *)&octetString);
if (status != 0)
goto CLEANUP;
/* get data to decrypt from octet string */
status = C_BERDecodeString (ctx, octetString->data, octetString->len,
&tmpTag, &tmpTagClass, &encryptedKey.data,
&encryptedKey.len);
if (status != 0)
goto CLEANUP;
status = DecryptKey (ctx, &encryptedKey, algId, &decryptedKey);
if (status != 0)
goto CLEANUP;
status = RSA_WriteDataToFile
(decryptedKey.data, decryptedKey.len,
"Enter name of file to store decrypted PrivateKeyInfo");
CLEANUP:
if (status != 0)
RSA_PrintError ("UnwrapKey", status);
T_memset (decryptedKey.data, 0, decryptedKey.len);
T_free (decryptedKey.data);
T_free (encryptedKeyBer.data);
T_free (encryptedKey.data);
C_DestroyListObject (&items);
return status;
} /* end UnwrapKey */
static int DecryptKey (CERTC_CTX ctx, ITEM *encryptedKey, ITEM *algId,
ITEM *decryptedKey)
{
int status = 0;
unsigned char password[RSA_DEMO_MAX_LINE_LEN];
B_ALGORITHM_OBJ decryptionObj = NULL;
B_KEY_OBJ pbeKey = NULL;
ITEM passwordItem = {NULL, 0};
B_ALGORITHM_CHOOSER chooser;
B_INFO_TYPE pbeAlgs[] = {
AI_SHA1WithDES_CBCPadBER,
AI_MD5WithDES_CBCPadBER,
AI_MD5WithRC2_CBCPadBER,
AI_MD2WithDES_CBCPadBER,
AI_MD2WithRC2_CBCPadBER
};
SERVICE_HANDLER dummyRandom = {
SPT_CRYPTO, DUMMY_RANDOM_NAME, S_InitializeDummyRandomCSP
};
unsigned int i, numPbeAlgs = sizeof (pbeAlgs) / sizeof (pbeAlgs[0]);
unsigned int bufSize = 0, outputLenUpdate = 0, outputLenFinal = 0;
/* for graceful error handling */
if ((encryptedKey == NULL) || (algId == NULL) || (decryptedKey == NULL))
return RSA_DEMO_E_INVALID_PARAMETER;
decryptedKey->data = NULL;
decryptedKey->len = 0;
/* If we register the dummy random provider, we will unregister it before
leaving this routine. If we already have a real crypto provider (which
was registered when wrapping a key), there is no need for a dummy random
service. Therefore, if we get E_DUPLICATE_SERVICE, that is our indicator
that a real crypto provider is already present. */
status = C_RegisterService (ctx, &dummyRandom, NULL, SERVICE_ORDER_FIRST);
if (status != 0 && status != E_DUPLICATE_SERVICE)
goto CLEANUP;
status = B_CreateAlgorithmObject (&decryptionObj);
if (status != 0)
goto CLEANUP;
for (i = 0; i < numPbeAlgs; i++) {
status = B_SetAlgorithmInfo (decryptionObj, pbeAlgs[i], (POINTER)algId);
if (status == 0)
break;
}
/* the CSP chooser has the Crypto-C AMs for MD5 and RC2 */
status = C_GetChooser (ctx, &chooser);
if (status != 0)
goto CLEANUP;
/* set up key object */
status = RSA_GetCommand ((char *)password, sizeof (password),
"Enter password for encryption");
if (status != 0)
goto CLEANUP;
passwordItem.data = password;
passwordItem.len = T_strlen ((char *)password);
status = B_CreateKeyObject (&pbeKey);
if (status != 0)
goto CLEANUP;
status = B_SetKeyInfo (pbeKey, KI_Item, (POINTER)&passwordItem);
if (status != 0)
goto CLEANUP;
status = B_DecryptInit (decryptionObj, pbeKey, chooser, NULL);
if (status != 0)
goto CLEANUP;
/* The decrypted key is at least one byte shorter than the ciphertext
*/
bufSize = encryptedKey->len - 1;
decryptedKey->data = T_malloc (bufSize);
if (decryptedKey->data == NULL) {
status = RSA_DEMO_E_ALLOC;
goto CLEANUP;
}
status = B_DecryptUpdate (decryptionObj, decryptedKey->data,
&outputLenUpdate, bufSize, encryptedKey->data,
encryptedKey->len, NULL, NULL);
if (status != 0)
goto CLEANUP;
status = B_DecryptFinal (decryptionObj, decryptedKey->data + outputLenUpdate,
&outputLenFinal, bufSize - outputLenUpdate, NULL,
NULL);
if (status != 0)
goto CLEANUP;
/* total length of ciphertext */
decryptedKey->len = outputLenUpdate + outputLenFinal;
CLEANUP:
if (status != 0) {
T_memset (decryptedKey->data, 0, decryptedKey->len);
T_free (decryptedKey->data);
decryptedKey->data = NULL;
decryptedKey->len = 0;
RSA_PrintError ("DecryptKey", status);
}
T_memset (password, 0, sizeof (password));
B_DestroyKeyObject (&pbeKey);
B_DestroyAlgorithmObject (&decryptionObj);
C_UnregisterService (ctx, SPT_CRYPTO, DUMMY_RANDOM_NAME);
return status;
} /* end DecryptKey */