4

I'm having an issue with either the commandline tool openssl or I'm having a problem with my C++ code. I don't know which is incorrect but when I generate a key and IV from a passphase and a salt using both methods I don't get the same key/IV values. Are there any typo or issues with either the code or commandline that you can see? Is the version of openssl 0.9.8i broken? I'm using the nround value of 1 since the commandline doesn't have the ability to pass the iteration count value. It should be matching but it isn't and I can't discover where my mistake is.

-----------------The following code encrypts the string XYZ correctly as 2OG7CNt/SjFEZ4RM3ZS4ZA== with Key: eaa4d33f9f6a9a8e543c0ae80eef651b675ef50682e5f144f1c140269531ddb2 IV: e94c989252a82fcb6b934752c0f3702b ----

EVP_CIPHER_CTX ctx;
unsigned char acOutbuf[cMaxEncryptLen + EVP_MAX_BLOCK_LENGTH + 1];
int nOutLen;
std::string strDecrypt;

// Key and IV values.
const unsigned char pcCode[] = "f7bUtborBWdBIUkZuLr9oVGzGsc7Y6AMMe7U3z+AQo8";
const unsigned char pcSalt[] = {0x78,0x01,0x3E,0x00,0xD8,0x04,0x3E,0x00};
unsigned char acKey[EVP_MAX_KEY_LENGTH + 1];
unsigned char acIV[EVP_MAX_IV_LENGTH + 1];

// Load all the encryption ciphers and lookup the one we want to use.
OpenSSL_add_all_algorithms();
const EVP_CIPHER *cipher = EVP_get_cipherbyname("aes-256-cbc");

// Generate HashKey for the password.
int nrounds = 1;
int nCnt = EVP_BytesToKey(cipher, EVP_sha1(), pcSalt, pcCode, sizeof(pcCode), nrounds, acKey, acIV);

char *pcPassword = const_cast<char *> (strPassword.c_str());
nBase64DecodeCnt = strPassword.length();

EVP_CIPHER_CTX_init(&ctx);

EVP_CipherInit_ex(&ctx, cipher, NULL, acKey, acIV, 1);
if(!EVP_CipherUpdate(&ctx, acOutbuf, &nOutLen, (const unsigned char *) pcPassword, nBase64DecodeCnt))
{
    EVP_CIPHER_CTX_cleanup(&ctx);
    return strDecrypt;
}
if(!EVP_CipherFinal_ex(&ctx, acOutbuf, &nOutLen))
{
    EVP_CIPHER_CTX_cleanup(&ctx);
    return strDecrypt;
}

strDecrypt.assign((const char *) acOutbuf, nOutLen);

EVP_CIPHER_CTX_cleanup(&ctx);
return strDecrypt;

-----------------The following code decrypts 2OG7CNt/SjFEZ4RM3ZS4ZA== as XYZ -----------------------

EVP_CIPHER_CTX ctx;
unsigned char acOutbuf[cMaxEncryptLen + EVP_MAX_BLOCK_LENGTH + 1];
int nOutLen;
std::string strDecrypt;

// Key and IV values.
const unsigned char pcCode[] = "f7bUtborBWdBIUkZuLr9oVGzGsc7Y6AMMe7U3z+AQo8";
const unsigned char pcSalt[] = {0x78,0x01,0x3E,0x00,0xD8,0x04,0x3E,0x00};
unsigned char acKey[EVP_MAX_KEY_LENGTH + 1];
unsigned char acIV[EVP_MAX_IV_LENGTH + 1];

// Load all the encryption ciphers and lookup the one we want to use.
OpenSSL_add_all_algorithms();
const EVP_CIPHER *cipher = EVP_get_cipherbyname("aes-256-cbc");

// Generate HashKey for the password.
int nrounds = 1;
int nCnt = EVP_BytesToKey(cipher, EVP_sha1(), pcSalt, pcCode, sizeof(pcCode), nrounds, acKey, acIV);

// Convert the base64 password string back into a real password.
int nBase64DecodeCnt;
char *pcPassword = Base64ToByteStream ((char *) strPassword.c_str(), (int) strPassword.length(), &nBase64DecodeCnt);

EVP_CIPHER_CTX_init(&ctx);

EVP_CipherInit_ex(&ctx, cipher, NULL, acKey, acIV, 0);
if(!EVP_CipherUpdate(&ctx, acOutbuf, &nOutLen, (const unsigned char *) pcPassword, nBase64DecodeCnt))
{
    EVP_CIPHER_CTX_cleanup(&ctx);
    return strDecrypt;
}
if(!EVP_CipherFinal_ex(&ctx, acOutbuf, &nOutLen))
{
    EVP_CIPHER_CTX_cleanup(&ctx);
    return strDecrypt;
}

strDecrypt.assign((const char *) acOutbuf, nOutLen);

EVP_CIPHER_CTX_cleanup(&ctx);
return strDecrypt;

Note that although I can decrypt and encrypt using the openssl commandline tool I can't seem to get the key and iv generated correctly using the commandline.

---- Encrypts correct -----
echo -n XYZ | openssl enc -e -a -aes-256-cbc -K eaa4d33f9f6a9a8e543c0ae80eef651b675ef50682e5f144f1c140269531ddb2 -iv e94c989252a82fcb6b934752c0f3702b

Produces : 2OG7CNt/SjFEZ4RM3ZS4ZA==

---- Decrypts correct to XYZ ----

echo 2OG7CNt/SjFEZ4RM3ZS4ZA== | openssl.exe enc -d -a -aes-256-cbc -K eaa4d33f9f6a9a8e543c0ae80eef651b675ef50682e5f144f1c140269531ddb2 -iv e94c989252a82fcb6b934752c0f3702b

Produces : XYZ

-----  Does not produce the correct key and IV --------
openssl enc -aes-256-cbc -md sha1 -S 78013E00D8043E00 -P -pass pass:f7bUtborBWdBIUkZuLr9oVGzGsc7Y6AMMe7U3z+AQo8

salt=78013E00D8043E00
key=718D65A221F0B2F12B6C92B7E54501DD63A8E156E03CA0E98E73653B1D0F58E5
iv =A5FDF910766E727E2100FA09B7578685
jww
  • 97,681
  • 90
  • 411
  • 885
Andrew Stern
  • 688
  • 9
  • 18
  • You are validating the key derivation in both the code and cmd-line usage against the fixed key `eaa4d33f9f6...`. Where did that key/IV come from? – B-Con Feb 21 '13 at 16:56
  • I have a section in my code (not shown here) that dumps the values of key/IV after I call EVP_BytesToKey. It is these values that I copied to the commandline along with the Base64 encoded version of my password to decrypt it back to my original password. – Andrew Stern Feb 21 '13 at 18:43
  • [OpenSSL 1.1.0c changed the digest algorithm](http://stackoverflow.com/q/39637388/608639) used in some internal components. Formerly, MD5 was used, and 1.1.0 switched to SHA256. Be careful the change is not affecting you in both `EVP_BytesToKey` and commands like `openssl enc`. – jww Jan 26 '17 at 16:13

1 Answers1

3

It seems that the openssl commandline tool used a character pointer and the string length. I was using an array of characters and the size of the character array. This left off the last '\0' character from my calculation causing me to generate different key and IV values. The corrected code is:

{
...
    // Key and IV values.
    const char *pcCode = "f7bUtborBWdBIUkZuLr9oVGzGsc7Y6AMMe7U3z+AQo8";
    unsigned char acKey[EVP_MAX_KEY_LENGTH + 1];
    unsigned char acIV[EVP_MAX_IV_LENGTH + 1];

    // Load all the encryption ciphers and lookup the one we want to use.
    OpenSSL_add_all_algorithms();
    const EVP_CIPHER *cipher = EVP_get_cipherbyname("aes-256-cbc");
    const EVP_MD *digest = EVP_get_digestbyname("sha1");

    // Generate HashKey for the password.
    int nrounds = 1;
    int nCnt = EVP_BytesToKey(cipher, digest, Global::acDecryptSalt, (const unsigned char *) pcCode, strlen(pcCode), nrounds, acKey, acIV);

...
}
Daniel Roethlisberger
  • 6,958
  • 2
  • 41
  • 59
Andrew Stern
  • 688
  • 9
  • 18