Node.js crypto: too short encryption key

Viewed 1640

I want to encrypt a user's data with AES-256 to store it securely in my database. However, I have the problem that the key must be 32 characters long. But the passwords of my users are usually much shorter. Is there a way how I can "extend" the length of the passwords?

I also thought about the fact that human-made passwords are usually weak. So I would need some kind of function that "links" the password to the encryption key?

Here is my code, which I use to encrypt and decrypt:

const crypto = require('crypto');
const algorithm = 'aes-256-cbc';
const key; //Here I would get the password of the user

function encrypt(text) {
   const iv = crypto.randomBytes(16);
   let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
   let encrypted = cipher.update(text);
   encrypted = Buffer.concat([encrypted, cipher.final()]);
   return { iv: iv.toString('hex'), encryptedData: encrypted.toString('hex') };
}

function decrypt(text) {
   let iv = Buffer.from(text.iv, 'hex');
   let encryptedText = Buffer.from(text.encryptedData, 'hex');
   let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key), iv);
   let decrypted = decipher.update(encryptedText);
   decrypted = Buffer.concat([decrypted, decipher.final()]);
   return decrypted.toString();
}

Many thanks for answers in advance.

Update 1.0:

After some research I have found the following code: (Source)

const crypto = require('crypto');

// Uses the PBKDF2 algorithm to stretch the string 's' to an arbitrary size,
// in a way that is completely deterministic yet impossible to guess without
// knowing the original string
function stretchString(s, outputLength) {
  var salt = crypto.randomBytes(16);
  return crypto.pbkdf2Sync(s, salt, 100000, outputLength, 'sha512');
}

// Stretches the password in order to generate a key (for encrypting)
// and a large salt (for hashing)
function keyFromPassword(password) {
  // We need 32 bytes for the key
  const keyPlusHashingSalt = stretchString(password, 32 + 16);
  return {
    cipherKey: keyPlusHashingSalt.slice(0, 32),
    hashingSalt: keyPlusHashingSalt.slice(16)
  };
}

If I got everything right, this should solve my problem: From any password I can generate a secure encryption key with a given length using the above function. The same password always generates the same encryption key with the function keyFromPassword(password), right?

Update 2.0:

Thanks to @President James K. Polk, who gave me some important tips, I have now updated my code. I hope that everything is fine now.

2 Answers

You should not be using your user's passwords directly as keys. Instead, you can use them as input to a key-derivation algorithm like pkbdf2.

As this paper on PKBDF2 explains:

Unfortunately, user-chosen passwords are generally short and lack enough entropy [11], [21], [18]. For these reasons, they cannot be directly used as a key to implement secure cryptographic systems. A possible solution to this issue is to adopt a key derivation function (KDF), that is a function which takes a source of initial keying material and derives from it one or more pseudorandom keys.

A library for computing pkbdf2 for Node.js, for example is found here.

Always add a salt to the text, and append it to the end of the text. The salt can be a randomly generated string of arbitrary length and can easily satisfy the 32-char length limit. In addition, adding salts before encryption strengthens the encryption.

Related