I have an encrypted file where I know the used password and the salt. The file was encrypted with the following credentials:
aes-128-cbc
sha1
5
8P0puxB5OVUFI6uX
MarkRobs
--> hex: 4d61726b526f6273
All the following commands and code was tested on Linux with openssl 3.1.
When I use the command line with:
openssl enc -aes-128-cbc -k "8P0puxB5OVUFI6uX" -S 4d61726b526f6273 -d -p -iter 5 -md sha1 -bufsize 1024 -in file.enc -out prj.xma -nosalt
I get the following key and iv:
key=5864BC3B9AAD200975546C1B0FF6F4CF
iv =FB14855AC59E4C541C48D78891B14495
With my small program I get the following key and IV:
Using passw.: 8P0puxB5OVUFI6uX (38 50 30 70 75 78 42 35 4f 56 55 46 49 36 75 58)
Using salt: MarkRobs (4d 61 72 6b 52 6f 62 73)
Using key: 14 14 1d 8a 68 ba c6 2b 76 5e ff 05 86 b8 e8 55
Using IV: c0 be c4 96 c9 1b 77 ed 3a 1c 53 f7 18 8f d5 98
Here is the code I implemented:
#include <iostream>
#include <fstream>
#include <cstring>
#include <openssl/err.h>
#include <openssl/evp.h>
#include "scramble.h"
#include "utils.h"
using std::string;
using std::ifstream;
using std::ios_base;
using std::ios;
using std::exception;
using std::min;
using std::cout;
using std::cerr;
using std::endl;
#define CHUNK_SIZE 1024
#define OSSL_SUCCESS 1
#define OSSL_ERROR 0
#define AES128_KEY_SIZE 16
#define AES128_SALT_SIZE 8
Scramble::Scramble()
{
mCtx = EVP_CIPHER_CTX_new();
if (!mCtx)
cerr << "Error getting new context!" << endl;
}
bool Scramble::aesInit(const string& key, const string& salt, bool encrypt)
{
if (mAesInitialized)
return true;
if (!mCtx)
{
cerr << "No context available! Initialisation failed!" << endl;
return false;
}
int keySize;
int count = 5; // Number iterations
const EVP_MD *md = EVP_sha1();
const EVP_CIPHER *pCipher = EVP_aes_128_cbc();
if (!md)
{
cerr << "Error getting SHA1 hash function!" << endl;
return false;
}
if (!pCipher)
{
cerr << "Error getting the AES128-CBC cipher algorithm!" << endl;
return false;
}
memset(mAesSalt, 0, AES128_SALT_SIZE);
memcpy(mAesSalt, salt.c_str(), min((size_t)AES128_SALT_SIZE, salt.length()));
// Initialize the key and IV with 0
memset(mAesKey, 0, AES128_KEY_SIZE);
memset(mAesIV, 0, AES128_KEY_SIZE);
keySize = EVP_BytesToKey(pCipher, md, (unsigned char *)salt.c_str(), (unsigned char *)key.c_str(), key.length(), count, mAesKey, mAesIV);
if (keySize == AES128_KEY_SIZE)
{
EVP_CIPHER_CTX_init(mCtx);
if (encrypt)
{
if (EVP_EncryptInit_ex(mCtx, pCipher, nullptr, mAesKey, mAesIV) != OSSL_SUCCESS)
{
cerr << "Error initializing decrypting!" << endl;
return false;
}
}
else
{
if (EVP_DecryptInit_ex(mCtx, pCipher, nullptr, mAesKey, mAesIV) != OSSL_SUCCESS)
{
cerr << "Error initializing decrypting!" << endl;
return false;
}
}
}
else
{
cerr << "Key size is " << (keySize * 8) << " bits - should be 128 bits" << endl;
return false;
}
mAesInitialized = true;
return mAesInitialized;
}
Why is this difference and what must I change to get identical key and IV?
BTW: The original file was encrypted on a Windows by using the library LIBEAY32.DLL
and the key and IV is different then on Linux although the same C++ code as shown above was used. This I don't understand. What I'm missing?
I could solve my problem. It turned out that it was my fault because I had a typo in the password. But I want to clarify a few things.
The function EVP_BytesToKey()
hasn't changed in openssl. It doesn't matter which version of openssl is used. If you call this function with the parameters as in my code example, the result is always the same.
In my example you see that the digest is fixed to sha1
and that there is a salt given. This means that the default options, who may have changed over the versions, are overwritten. The way the function is called in the example guaranties that the result remains the same.
If the digest, the password and the salt is correct, then the file, provided the file was encrypted with the same credentials, can be decrypted successfully. And so it was for my problem. Once the password was correct, it worked.