Looking for a way to encrypt data (mainly strings) in node and decrypt in an android app (java).
Have successfully done so in each one (encrypt/decrypt in node, and
You need to make sure you are using
on both sides of the connection.
For the key, on the Java side you are using quite some work to derive a key from a string - no such thing is done on the node.js side. Use a standard key derivation algorithm here (and the same one on both sides).
Looking again, the line
var cipher = crypto.createCipher('aes-128-cbc','somepass')
does indeed some key derivation, just the documentation is silent about what it does exactly:
crypto.createCipher(algorithm, password)
Creates and returns a cipher object, with the given algorithm and password.
algorithm
is dependent on OpenSSL, examples are'aes192'
, etc. On recent releases,openssl list-cipher-algorithms
will display the available cipher algorithms.password
is used to derive key and IV, which must be'binary'
encoded string (See the Buffers for more information).
Okay, this at least says how to encode it, but not what is done here. So, we either can use the other initialization method crypto.createCipheriv (which takes key and initialization vector directly, and uses them without any modification), or look at the source.
createCipher
will somehow invoke the C++ function CipherInit in node_crypto.cc. This uses in essence the EVP_BytesToKey
function to derive the key from the provided string (with MD5, empty salt and count 1), and then do the same as CipherInitiv
(which is called by createCipheriv
, and uses IV and key directly.)
As AES uses 128 bits of key and initialization vector and MD5 has 128 bits of output, this in effect means
key = MD5(password)
iv = MD5(key + password)
(where + denotes concatenation, not addition). You can re-implement this key-derivation in Java using the MessageDigest class, if needed.
A better idea would be to use some slow key derivation algorithm, specially if your password is something what a human can memorize. Then use the pbkdf2 function to generate this key on the node.js side, and PBEKeySpec together with a SecretKeyFactory (with algorithm PBKDF2WithHmacSHA1
) on the Java side. (Choose an iteration count which just does not make your customers complain about slowness on the most common devices.)
For your cipher algorithm, on the Java side you are saying "use the AES algorithm with whatever is the default mode of operation and the default padding mode here". Don't do this, as it might change from provider to provider.
Instead, use explicit indications of the mode of operation (CBC
, in your case), and explicit indication of the padding mode. One example might be:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Have a look at the node.js documentation to see how to indicate a padding mode there (or which one is the default, to select the same on the Java side). (From the OpenSSL EVP documentation, it looks like the default is PKCS5Padding here, too.)
Also, instead of implementing the encryption yourself, consider using TLS for transport encryption. (Of course, this only works if you have a real-time connection between both sides.)