skip to Main Content

Question:

Hello, I’m trying to decrypt my encoded base64 with the following command:

echo "base64key" | (openssl enc -AES-256-cbc -d -a -pass "pass:test" -pbkdf2 -iter 100000 -md sha256 -p; echo) | tee >(hexdump -C)

The base64 key originates from the following JavaScript code:

const crypto = require('crypto');
const passphrase = 'test';
const iterations = 100000; 
const keyLength = 32;
const saltLength = 8; 
const ivLength = 16;

let salt = crypto.randomBytes(saltLength);
let key = crypto.pbkdf2Sync(passphrase, salt, iterations, keyLength, 'sha256');
let iv = crypto.randomBytes(ivLength);
console.log("Salt size: ",salt.length + " bytes");
console.log("Key size: ",key.length + " bytes");
console.log("IV size: ",iv.length +" bytes");
const dataToEncrypt = 'its a secret!';
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encryptedData = cipher.update(dataToEncrypt, 'utf8', 'base64');
encryptedData += cipher.final('base64');
const saltedPrefix = Buffer.from('Salted__');
const prefixedData = Buffer.concat([saltedPrefix, salt, iv, Buffer.from(encryptedData, 'base64')]);
console.log("Result HEX: ", prefixedData.toString('hex'));
const encrypted = prefixedData.toString('base64');
/* FINAL SIZE */
console.log("Result size: ", encrypted.length +" bytes");

/* RESULT LOG */ 
console.log('Base64 encrypted: ' + encrypted);

output of the javascript:

Salt size:  8 bytes
Key size:  32 bytes
IV size:  16 bytes
Result HEX:  53616c7465645f5f2d920c9fc07a1131ff26f94a8e2b8110691e022c824f9251256f28e1533a4dfcb7eda2ae5f665809
Result size:  64 bytes
Base64 encrypted: U2FsdGVkX18tkgyfwHoRMf8m+UqOK4EQaR4CLIJPklElbyjhUzpN/Lftoq5fZlgJ

On my linux server now,
The output of the command is the following:

command:

echo "U2FsdGVkX18tkgyfwHoRMf8m+UqOK4EQaR4CLIJPklElbyjhUzpN/Lftoq5fZlgJ" | (openssl enc -AES-256-cbc -d -a -pass "pass:test" -pbkdf2 -iter 100000 -md sha256 -p; echo) | tee >(hexdump -C)

output:

salt=2D920C9FC07A1131
key=C4B6F8A9A1DA258846E066D8B3D4A7028922376D87DC898255C8DE64DD994E09
iv =F7369730119C2461AB0A6CA1B8D3015D
▒▒f/B>▒=O▒▒琰its a secret!
00000000  73 61 6c 74 3d 32 44 39  32 30 43 39 46 43 30 37  |salt=2D920C9FC07|
00000010  41 31 31 33 31 0a 6b 65  79 3d 43 34 42 36 46 38  |A1131.key=C4B6F8|
00000020  41 39 41 31 44 41 32 35  38 38 34 36 45 30 36 36  |A9A1DA258846E066|
00000030  44 38 42 33 44 34 41 37  30 32 38 39 32 32 33 37  |D8B3D4A702892237|
00000040  36 44 38 37 44 43 38 39  38 32 35 35 43 38 44 45  |6D87DC898255C8DE|
00000050  36 34 44 44 39 39 34 45  30 39 0a 69 76 20 3d 46  |64DD994E09.iv =F|
00000060  37 33 36 39 37 33 30 31  31 39 43 32 34 36 31 41  |7369730119C2461A|
00000070  42 30 41 36 43 41 31 42  38 44 33 30 31 35 44 0a  |B0A6CA1B8D3015D.|
00000080  f6 db 66 2f 01 42 3e 80  3d 4f 9d c5 04 e7 90 b0  |..f/.B>.=O......|
00000090  69 74 73 20 61 20 73 65  63 72 65 74 21 0a        |its a secret!.|
0000009e

As you can see, the secret is there, but there’s a jumbled words behind it, how can I make this work without those characters?
I’m using prefix "Salted__" because it seems its needed for the OpenSSL decryption in this case.

I’m really struggling on this, anyone got any ideas?

2

Answers


  1. Chosen as BEST ANSWER

    I have found the solution.

    In In AES-256-CBC encryption, you need both a key and an IV, and by concatenating these two lengths (keyLength + ivLength), you ensure that the crypto.pbkdf2Sync function generates a single buffer that contains both the key and the IV, with the key occupying the first keyLength bytes of the buffer and the IV occupying the next ivLength bytes.

    This can then be read by the openssl command without the need to specify the -iv

    const crypto = require('crypto');
     
    // Configuration
    const algorithm = 'aes-256-cbc';
    const passphrase = 'test'; 
    const saltLength = 8;
    const ivLength = 16;
    const keyLength = 32
    const iterations = 100000;
    const digest = 'sha256';
    const blockSize = 16;
     
     
    const salt = crypto.randomBytes(saltLength);
    const buf = crypto.pbkdf2Sync(passphrase, salt, iterations, keyLength + ivLength, digest);
    const key = buf.subarray(0, keyLength);
    const iv = buf.subarray(keyLength, keyLength + ivLength);
     
    const cipher = crypto.createCipheriv(algorithm, key, iv);
    cipher.setAutoPadding(true);
     
    const input = Buffer.from('some clear text data', 'utf8');
    const encrypted = Buffer.concat([cipher.update(input), cipher.final()]);
     
    const saltedPrefix = Buffer.from('Salted__', 'utf8');
    const combined = Buffer.concat([saltedPrefix, salt, encrypted]);
     
    console.log('Salt:', salt.toString('hex'));
    console.log('IV:', iv.toString('hex'));
    console.log('Encrypted:', encrypted.toString('hex'));
    console.log('Combined Base64:', combined.toString('base64'));
     
    

  2. In the JavaScript code, the IV is not determined from the key derivation function (PBKDF2), instead a random IV is generated.
    The encrypted data consists of the concatenation of the ASCII encoding of Salted__, followed by the 8 bytes salt, followed by the 16 bytes IV and finally the actual ciphertext:

    Salted__-prefix  8-bytes-salt     16-bytes-IV                      actual ciphertext
    53616c7465645f5f 2d920c9fc07a1131 ff26f94a8e2b8110691e022c824f9251 256f28e1533a4dfcb7eda2ae5f665809
    

    In contrast, openssl enc -d requires the encrypted data in the following order: ASCII encoding of Salted__, followed by the 8 bytes salt and finally the actual ciphertext, i.e. the IV is not included:

    Salted__-prefix  8-bytes-salt     actual ciphertext
    53616c7465645f5f 2d920c9fc07a1131 256f28e1533a4dfcb7eda2ae5f665809      
    

    which corresponds Base64 encoded to:

    U2FsdGVkX18tkgyfwHoRMSVvKOFTOk38t+2irl9mWAk=                            
    

    In addition, the IV is derived together with the key using the key derivation function. Alternatively, the IV can be specified with the -iv option, which must be followed by the hex encoded IV. The latter is required here:

    -iv ff26f94a8e2b8110691e022c824f9251
    

    Overall the fixed OpenSSL statement is:

    echo U2FsdGVkX18tkgyfwHoRMSVvKOFTOk38t+2irl9mWAk= | openssl enc -AES-256-cbc -d -a -pass pass:test -pbkdf2 -iter 100000 -md sha256 -p -iv ff26f94a8e2b8110691e022c824f9251
    

    This then returns the original plaintext its a secret!.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search