I was recently given the responsibility of upgrading a very old piece of crypto code that would no longer run under Windows 10. I upgraded to Visual Studio 2013, and modified the code a bit to use better error reporting. And the code works fine. It uses a private key to decrypt encrypted data. All well and good.
Except for when I noticed a comment in the code that indicates some PUBLIC specifiers had to be used as inputs to the BSAFE v6.0 library routines, instead of their PRIVATE counterparts. In fact, when the private specifiers are used, I get the following error message:
RSA Error: invalid key info format
Note: my private key is not encrypted.
I'm confused as to why the private specifiers won't work, since I'm doing private key decryption.
In the following code, BSAFE v6.0 library functionis B_SetKeyInfo, B_GetKeyInfo, and B_SetAlgorithmInfo use public key specifiers.
int Decrypt(unsigned char* cypherData, const string privKeyFilePathAndName, unsigned char*& plainData) {
enum {
IN_BUF_LEN = 1000, // input buffer length
NUM_DIGITAL_SIGNATURE_BYTES = 128 // number of digital signature bytes
};
static unsigned char decryptedDigest[NUM_DIGITAL_SIGNATURE_BYTES]; // decrypted digest (returned)
const string delimiters = " \n"; // strtok delimeters
A_RSA_KEY privKey; // private key
B_ALGORITHM_OBJ rsaAlgorithmObj; // rsa algorithm object
B_KEY_OBJ privKeyObject; // private key object
char inBuf[IN_BUF_LEN]; // input buffer
char* token; // strtok token
FILE* ifp; // input (private key) file pointer
int i; // index
int stat; // status
unsigned char privModulusData[NUM_MODULUS_BYTES]; // modulus data
unsigned char privExponentData[NUM_MODULUS_BYTES]; // exponent data
unsigned int digestLen; // digest length
unsigned int partOutLen; // part out length
// open private key file
if ((ifp = fopen(privKeyFilePathAndName.c_str(), "r")) == NULL) {
DisplayErrorMsg("Can't open private key file");
return FAIL;
}
// get modulus length and exponent length
if ((fgets(inBuf, IN_BUF_LEN, ifp)) == NULL) {
DisplayErrorMsg("Private key file error - can't read number of modulus bytes (modulus length)");
return FAIL;
}
privKey.modulus.len = privKey.exponent.len = stoi(inBuf);
// get modulus data
if ((fgets(inBuf, IN_BUF_LEN, ifp)) == NULL) {
DisplayErrorMsg("Private key file error - can't read modulus data");
return FAIL;
}
token = strtok(inBuf, delimiters.c_str());
for (i = 0; token; i++) {
privModulusData[i] = (unsigned char) stoi(token);
token = strtok(NULL, delimiters.c_str());
}
if (i != sizeof(privModulusData)) {
DisplayErrorMsg("Private key file error - wrong amount of modulus data");
return FAIL;
}
// get exponent data
if ((fgets(inBuf, IN_BUF_LEN, ifp)) == NULL) {
DisplayErrorMsg("Private key file error - can't read exponent data");
return FAIL;
}
token = strtok(inBuf, delimiters.c_str());
for (i = 0; token; i++) {
privExponentData[i] = (unsigned char) stoi(token);
token = strtok(NULL, delimiters.c_str());
}
if (i != sizeof(privExponentData)) {
DisplayErrorMsg("Private key file error - wrong amount of exponent data");
return FAIL;
}
// close private key file and finish creating private key variable
fclose(ifp);
privKey.modulus.data = privModulusData;
privKey.exponent.data = privExponentData;
// create private key object and set to key read in from file
if ((stat = B_CreateKeyObject(&privKeyObject)) != 0) {
DisplayRsaErrorMsg(stat);
return FAIL;
}
// the private key is set to an Infotype of KI_RSAPublic because an error is
// generated during decryption if we use the Infotype KI_RSAPrivate; not sure
// why it works this way
if ((stat = B_SetKeyInfo(privKeyObject, KI_RSAPublic, (POINTER)&privKey)) != 0) {
DisplayRsaErrorMsg(stat);
return FAIL;
}
if ((stat = B_GetKeyInfo((POINTER *)&privKey, privKeyObject, KI_RSAPublic)) != 0) {
DisplayRsaErrorMsg(stat);
return FAIL;
}
// create an rsa algorithm object and set algorithm infoType to RSAPublic
if ((stat = B_CreateAlgorithmObject(&rsaAlgorithmObj)) != 0) {
DisplayRsaErrorMsg(stat);
return FAIL;
}
if ((stat = B_SetAlgorithmInfo(rsaAlgorithmObj, AI_RSAPublic, NULL_PTR)) != 0) {
DisplayRsaErrorMsg(stat);
return FAIL;
}
// perform the decryption, in stages (initial, update, and final)
if ((stat = B_DecryptInit(rsaAlgorithmObj, privKeyObject, DEMO_ALGORITHM_CHOOSER, ((A_SURRENDER_CTX*)NULL_PTR))) != 0) {
DisplayRsaErrorMsg(stat);
return FAIL;
}
if ((stat = B_DecryptUpdate(rsaAlgorithmObj, decryptedDigest, &digestLen, sizeof(decryptedDigest), cypherData, NUM_DIGITAL_SIGNATURE_BYTES, (B_ALGORITHM_OBJ)NULL_PTR, ((A_SURRENDER_CTX*)NULL_PTR))) != 0) {
DisplayRsaErrorMsg(stat);
return FAIL;
}
if ((stat = B_DecryptFinal(rsaAlgorithmObj, decryptedDigest, &partOutLen, sizeof(decryptedDigest), (B_ALGORITHM_OBJ)NULL_PTR, ((A_SURRENDER_CTX*)NULL_PTR))) != 0) {
DisplayRsaErrorMsg(stat);
return FAIL;
}
// set returned pointer and return pass code
plainData = decryptedDigest;
return PASS;
}