5

I am trying to encrypt/decrypt. Encryption works fine and it writes encrypted data to file. While decrypting I am getting an error of length issue. I have used "utf-8" format but error continues.

/ A decrypt function 
function decrypt(file) {

  let data = JSON.parse(fs.readFileSync(file));

  let iv = Buffer.from(data.iv, 'hex');
  let encryptedText =
    Buffer.from(data.encryptedData, 'hex');


  //  Creating Decipher 
  let decipher = crypto.createDecipheriv(
    algorithm, Buffer.from(key), iv);

  // Updating encrypted text 
  let decrypted = decipher.update(encryptedText);
  let decrypted = Buffer.concat([decrypted, decipher.final()]);

  //  // returns data after decryption 
  return decrypted.toString();
}
//run 
// Decrypts output 
console.log(decrypt('./file.json.enc'));
Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
    at Decipheriv.final (internal/crypto/cipher.js:170:29)
    at decrypt (/Users/chandrasekarareddy/Documents/projects/encrypt/final.js:48:22)
    at Object.<anonymous> (/Users/chandrasekarareddy/Documents/projects/encrypt/final.js:64:13)
    at Module._compile (internal/modules/cjs/loader.js:959:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)
    at Module.load (internal/modules/cjs/loader.js:815:32)
    at Function.Module._load (internal/modules/cjs/loader.js:727:14)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)
    at internal/main/run_main_module.js:17:11 {
  library: 'digital envelope routines',
  function: 'EVP_DecryptFinal_ex',
  reason: 'wrong final block length',
  code: 'ERR_OSSL_EVP_WRONG_FINAL_BLOCK_LENGTH'
}

Its throwing error at decipher.final(). Instead of file as input param if I pass text it's wrong fine. Thanks in advance

Chandu
  • 962
  • 2
  • 16
  • 33

3 Answers3

4

If you're getting this error, it's likely that you're passing in the wrong key. This can happen if the key is encoded incorrectly to the file in question.

I'd suggest the best approach is to encode in hex format (since we're using JSON here).

Here's a complete example of encoding to a .json.enc file, then decoding again. Note I'm using aes-256-cbc, so if you change the mode of encryption, the key and iv length may have to change.

const crypto = require("crypto");
const fs = require("fs");

function encrypt(buffer, algorithm, key, iv) {
    const cipher = crypto.createCipheriv(algorithm, key, iv);
    return Buffer.concat([cipher.update(buffer, null), cipher.final()]);
}

function decrypt(buffer, algorithm, key, iv) {
    const decipher = crypto.createDecipheriv(algorithm, key, iv);
    return Buffer.concat([decipher.update(buffer), decipher.final()]);
}

function encryptToJsonFile(buffer, filePath, algorithm, key, iv) {
    let encryptedData = encrypt(buffer, algorithm, key, iv);
    let fileData = { encryptedData: encryptedData.toString("hex"), iv: iv.toString("hex") };
    fs.writeFileSync(filePath, JSON.stringify(fileData), "utf8");
    return fileData;
}

function decryptJsonFile(filePath, algorithm, key) {
    let fileData = JSON.parse(fs.readFileSync(filePath, "utf8"));
    let encryptedData = Buffer.from(fileData.encryptedData, "hex");
    let iv = Buffer.from(fileData.iv, "hex");
    return decrypt(encryptedData, algorithm, key, iv);
}

const filePath = "./test.json.enc";
const EncryptionAlgorithm = "aes-256-cbc";

const key = Buffer.from("70ac30ae736068d90467beec0aedd75f3714cfe1e83b030c67911bb649316be0", "hex");
const iv = Buffer.from("3d4be42df33cc6a030aa54df2e144920", "hex");

const textToEncrypt = "My secrets are here";
const bufferToEncrypt = Buffer.from(textToEncrypt, "utf8");

console.log("Encrypted:", encryptToJsonFile(bufferToEncrypt, filePath, EncryptionAlgorithm, key, iv));
console.log("Decrypted:", decryptJsonFile(filePath, EncryptionAlgorithm, key).toString("utf8"));
Terry Lennox
  • 29,471
  • 5
  • 28
  • 40
0

I got the same error, so I'm explaining my solution as it might help others.

In my case, I was encrypting files and storing Buffer (returned by encrypt()) to directly in s3. Which converts it to JSON. I solved it by storing buffer in base64 encoding STRING (NOT Buffer).

Cool thing with createDecipheriv.update() is that it receives a base64 encoded STRING as input and returns decrypted buffer which you can convert to viewable ascii format.

Let's see my functions:

// this returns buffer, but a buffer can’t be saved in s3 or db (details lost). So always convert 
// it to base64 encoding (which reduces space) and simply give its base64 encoded string 
// output as input to decrypt function as it accepts base64 encoded STRING.
function encrypt(key, buffer) {
  try {
    const cipher = crypto.createCipheriv('aes256', key, resizedIV);
    return Buffer.concat([cipher.update(buffer), cipher.final()]).toString('base64');
  } catch (error) {
    console.log('crypto encrypt err - ', error);
  }
}

and here's my decrypt function:

// here cipher text was saved in s3 in base64 format 
// and decipher.update expects an base64 encoded STRING (not buffer)
async function decrypt(key, ciphertext) {
  try {
    const decipher = crypto.createDecipheriv('aes256', key, resizedIV);

    return Buffer.concat([
      decipher.update(ciphertext, 'base64'), // Expect `text` to be a base64 string
      decipher.final(),
    ]).toString();
  } catch (err) {
    console.log('decrypt err - ', err);
  }
}

Bonus points: if you're saving a dataKey in db or returning datakey from lambda functions using envelope encryption of KMS, then: since it's a buffer,

  • When sending/saving Buffer, always convert it into a base64 STRING e.g: Buffer.from(dataKey).toString('base64')
  • When accessing/retrieving this dataKey, convert it back to ascii format e.g: Buffer.from(base64EncodedData, 'base64').toString('ascii')

If the error still persists, follow this link: https://mlink.in/qa/?qa=755282/

please let me know if it helped anyone :)

Waleed Ahmad
  • 446
  • 3
  • 15
0

My code for Encryption decryption works fine

const crypto = require('crypto');
const SALT_SIZE = 256;
class AESEncryptor {
  encrypt(plainText, key) {
    if (!plainText) {
      throw new Error('plainText is required');
    }
    if (!key) {
      throw new Error('key is required');
    }

    const saltBytes = crypto.randomBytes(SALT_SIZE);
    const keyBytes = crypto.pbkdf2Sync(key, saltBytes, 10000, 32, 'sha1');
    const ivBytes = crypto.pbkdf2Sync(key, saltBytes, 10000, 16, 'sha1');

    const cipher = crypto.createCipheriv('aes-256-cbc', keyBytes, ivBytes);
    let cipherText = cipher.update(plainText, 'utf8', 'base64');
    cipherText += cipher.final('base64');

    const saltAndCipherText = Buffer.concat([saltBytes, Buffer.from(cipherText, 'base64')]);
    return saltAndCipherText.toString('base64');
  }


  decrypt(cipherText, key) {
    if (!cipherText || cipherText.length == 0) {
      throw new Error('cipherText is required');
    }
    if (!key) {
      throw new Error('key is required');
    }
    const allTheBytes = Buffer.from(cipherText, 'base64');
    const saltBytes = allTheBytes.slice(0, SALT_SIZE);
    const cipherTextBytes = allTheBytes.slice(SALT_SIZE);
    const keyBytes = crypto.pbkdf2Sync(key, saltBytes, 10000, 32, 'sha1');
    const ivBytes = crypto.pbkdf2Sync(key, saltBytes, 10000, 16, 'sha1');

    const decipher = crypto.createDecipheriv('aes-256-cbc', keyBytes, ivBytes);
    let decryptedText = decipher.update(cipherTextBytes, 'base64', 'utf8');
    decryptedText += decipher.final('utf8');

    return decryptedText;
  }
}

const plainText = 'hamza khalil';
const key = 'password1!';

try {
  let obj = new AESEncryptor();
  const encryptedText = obj.encrypt(plainText, key);
  console.log('Encrypted:', encryptedText);
  const decryptedText = obj.decrypt(encryptedData, key);
  console.log('Decrypted:', decryptedText);
} catch (error) {
  console.error('Encryption/Decryption error:', error.message);
}