I have been successfully using openssl to encrypt a large file. I'm trying to develop the equivalent nodejs crypto API code for the following openssl commands:

    openssl rand -hex 64 -out {keyfile}
    openssl enc -aes-256-cbc -salt -in {infile} -out {outfile} -pass file:{keyfile}

So far, I think this is what I need to do:

    const crypto=require('crypto');

    var iv = crypto.randomBytes(16);  // ????

    const encryptionkey = crypto.randomBytes( 32 );
    crypto.createCipheriv( 'aes-256-cbc', encryptionkey, iv );

createCipheriv requires an initialization vector, but my original openssl command doesn't use one.

What value for iv should I use to achieve the same result as my openssl command?


OpenSSL commandline enc does password-based encryption (PBE) by default. This means the key, and IV if applicable which it is for CBC, is derived from the 'password' input by a Password-Based Key Derivation Function (PBKDF); the key is NOT the same as the input. And it uses a particularly poor PBKDF; for details see my attempt at a canonical. Further, the default hash in the PBKDF has changed, and you don't specify which OpenSSL release you use(d) -- but the rand command you posted is an invalid form that was accidentally accepted by the old ad-hoc command parser and is rejected by the standardized parser in 1.1.0 up, so your release must be one that used MD5 for the enc PBKDF.

Incidentally this means most of the entropy in your rand 64 was wasted; the MD5-based PBKDF cuts the entropy to 128 bits (or very slightly less, for technical reasons), and the extra cost of using AES-256 instead of AES-128 also is wasted and doesn't add anything to security. Using 1.1.0 up, or specifying -md sha256, would avoid this specific problem, although there is really no point in using PBKDF/PBE at all for a fully random 'password'.

It happens that nodejs' older (and deprecated) crypto.createCipher (NOT createCipheriv) uses almost the same poor PBKDF as older OpenSSL enc: EVP_BytesToKey with MD5 and 1 iteration, but with no salt, which differs from the OpenSSL default and makes the PBKDF even weaker -- but that doesn't matter for a random 'password'.

  1. If you want or need to be compatible with exactly that openssl enc command, you'll have to code the equivalent of EVP_BytesToKey (for 1 iteration, which is fairly easy), and use createCipheriv with the resulting (derived) key and IV, plus write OpenSSL's Salted__ + salt header. See https://stackoverflow.com/questions/50181685/nodejs-createcipheriv-like-openssl for one example.

  2. If it's enough to be compatible with enc -nosalt on older releases (and enc -nosalt -md md5 on all releases), you can just use createCipher with the password, which must be printable chars e.g. hex as you did in your first example.

  3. enc can encrypt and decrypt with 'raw' key and IV, not password-based. Since your key is fully random that's secure. If that is sufficient, just use createCipheriv with a suitable key and IV. If you reuse a key it is vital the IV be unique (for CTR or OFB) and unpredictable (for CBC and CFB) for each use of a key, but if you use a different key every time it doesn't really matter what the IV is and you can even use a constant (such as all-zeros, which is convenient on the enc side). Pass the key and IV in hex to the enc options -K (uppercase!) and -iv. Note OpenSSL can't itself read these from files, like -pass file: or -kfile for a password, but in most Unix shells you can use file contents in an option like

    openssl enc -aes-256-cbc -d -K $(cat key_hex_file) -iv $(cat iv_hex_file)
    # or 
    openssl enc -aes-256-cbc -d -K $(xxd -p key_bin_file) -iv $(xxd -p iv_bin_file) 
    # (in most cases it is necessary or prudent to doublequote "$(cmd)"
    #  but here the output is _only_ hex chars so not needed if sane IFS)

Meta PS: we have lots of other Qs about nodejs crypto, and about OpenSSL enc, but I couldn't find a good dupe for the combination.

| improve this answer | |
  • Thanks for that detailed and informative response. I guess my real question is 'how can I encrypt/decrypt a large file with nodejs crytpo and still retain the ability to decrypt with the openssl utility.' I think you've given me enough information to figure that out, but if not, I'll post another question. – Mark Roy Aug 2 '19 at 12:44
  • Given you can choose/specify how to run openssl enc, all three options work and I recommend #3 if possible (although on Windows without WSL it may be a hassle to pass the arguments correctly). – dave_thompson_085 Aug 3 '19 at 7:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.