Search code examples
node.jsopensslcryptojs

How do you decrypt a file that has been encrypted with openssl using nodejs javascript


I have a file that has been encrypted using openssl using the following command:

openssl enc -in data -out encrypted -e -aes256 -k myverystrongpassword

Where data is the original file and encrypted is the encrypted file.

I tried various ways using crypto library but nothing seems to work. I understand that the password needs to be converted into a key so maybe I am doing something wrong there. Looked all over for a solution but nothing seems to work.


Solution

  • The posted OpenSSL statement uses a key derivation function EVP_BytesToKey() to derive a 32 bytes key and a 16 bytes IV from the password in combination with a random 8 bytes salt.
    The ciphertext corresponds to the concatenation of the ASCII encoding of Salted__, followed by the salt and finally by the actual ciphertext.
    As you already know according to your comment, EVP_BytesToKey() uses a digest for which OpenSSL applied MD5 by default in earlier versions and SHA-256 as of version v1.1.0 (the default value can be overridden in the OpenSSL statement with the -md option).

    Decryption is possible e.g. with CryptoJS: Due to its OpenSSL compatibility (s. sec. Interoperability) CryptoJS has a built-in implementation of an accessible EVP_BytesToKey() function and additionally allows to explicitly set the digest in the internal EVP_BytesToKey() call during key derivation. This makes it possible to decrypt encryptions that used SHA-256 or MD5 in key derivation.

    The following data is the Base64 encoding of a ciphertext generated with the posted OpenSSL statement. The plaintext used was The quick brown fox jumps over the lazy dog. The OpenSSL version applied is v1.1.1i (i.e. SHA-256 is implicitly used in the key derivation):

    U2FsdGVkX19W4wmC9dD35X4J66cSvaRaIQpvjDKHrLF9+qYg5VTo5urvExHLXhwf/bE8FXJTQZmKN8ITMJVdqQ==
    

    This ciphertext can be successfully decrypted using the following CryptoJS implementation:

    const password = 'myverystrongpassword';
    const saltCiphertextB64 = 'U2FsdGVkX19W4wmC9dD35X4J66cSvaRaIQpvjDKHrLF9+qYg5VTo5urvExHLXhwf/bE8FXJTQZmKN8ITMJVdqQ==';
    
    CryptoJS.algo.EvpKDF.cfg.hasher = CryptoJS.algo.SHA256.create(); // default: MD5        
    const decryptedData = CryptoJS.AES.decrypt(saltCiphertextB64, password);
    console.log(decryptedData.toString(CryptoJS.enc.Utf8)); // The quick brown fox jumps over the lazy dog
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>

    Note that the digest in the code must be explicitly specified as SHA-256 since OpenSSL v1.1.1i was used for encryption.
    If the encryption was done with an OpenSSL version that uses MD5, the digest in the code must be modified accordingly.

    Edit: As noted in the comment, the crypto functions createCipher()/createDecipher() also use EVP_BytesToKey() as key derivation. However, the following should be noted:

    • Unlike CryptoJS, it is not possible to specify the digest, i.e. MD5 is used unchangeably. Thus, encryptions that applied SHA-256 for key derivation cannot be decrypted (what applies to the encryptions here).
    • In contrast to CryptoJS, no salt is used by default. Therefore, salt creation and concatenation (Salted__|<salt>|<cipherext>) during encryption and separation during decryption would have to be implemented additionally. createCipher()/createDecipher() then has to be passed the concatenation of passphrase and salt.
    • Both functions are deprecated since version 10.0.0 and should actually not be used.

    A more robust approach to decrypt encryptions (with arbitrary digests in key derivation) using the crypto module is to apply createCipheriv()/createDecipheriv() and a port of the required functionality of EVP_BytesToKey() to derive key and IV (various implementations can be found on the net).

    Security: EVP_BytesToKey() is deemed to be a vulnerability these days. This is worsened by a low iteration count (like 1, which is used by OpenSSL), a broken digest (like MD5) or a missing salt (as is the default for crypto). Ultimately, this is why createCipher()/createDecipher() are deprecated. Instead of EVP_BytesToKey(), a more reliable key derivation function such as PBKDF2 or the more modern scrypt or Argon2 should be used.