7

I have a use case where the text has to be encoded and sent using the AES 256 algorithm. The client-side code is in C# which would be decrypting the code.

Encryption code in JS:

const crypto = require('crypto');
  algorithm = 'aes-256-cbc',
  secret = '1234567890123456',
  keystring = crypto.createHash('sha256').update(String(secret)).digest('base64').substr(0, 16);
  iv = crypto.createHash('sha256').update(String(secret)).digest('base64').substr(0, 16);
  inputEncoding = 'utf8',
  outputEncoding = 'base64';


function encrypt(text) {
  let cipher = crypto.createCipheriv('aes-256-cbc', keystring, iv);
  let encrypted = cipher.update(text, inputEncoding, outputEncoding)
  encrypted += cipher.final(outputEncoding);
  return encrypted;
}

Updated code used in the client side:

var keybytes = Encoding.UTF8.GetBytes(passwordKey);
var iv = Encoding.UTF8.GetBytes(passwordKey);

private byte[] EncryptStringToBytes(string plainText, byte[] key, byte[] iv)
        {
            try
            {
                // Check arguments.  
                if (plainText == null || plainText.Length <= 0)
                {
                    throw new ArgumentNullException("plainText");
                }
                if (key == null || key.Length <= 0)
                {
                    throw new ArgumentNullException("key");
                }
                if (iv == null || iv.Length <= 0)
                {
                    throw new ArgumentNullException("key");
                }
                byte[] encrypted;
                // Create a RijndaelManaged object  
                // with the specified key and IV.  
                using (var rijAlg = new RijndaelManaged())
                {
                    rijAlg.Mode = CipherMode.CBC;
                    rijAlg.Padding = PaddingMode.PKCS7;
                    rijAlg.FeedbackSize = 128;

                    rijAlg.Key = key;
                    rijAlg.IV = iv;

                    // Create a decrytor to perform the stream transform.  
                    var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

                    // Create the streams used for encryption.  
                    using (var msEncrypt = new MemoryStream())
                    {
                        using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                        {
                            using (var swEncrypt = new StreamWriter(csEncrypt))
                            {
                                //Write all data to the stream.  
                                swEncrypt.Write(plainText);
                            }
                            encrypted = msEncrypt.ToArray();
                        }
                    }
                }
                // Return the encrypted bytes from the memory stream.  
                return encrypted;
            }
            catch (Exception ex)
            {
                throw ex;
                //LoggerCS.logError("Utility", "EncryptStringToBytes", JsonConvert.SerializeObject(null), ex.ToString(), ex.StackTrace);
            }
            return null;
        }

The keyString and IV value used are same in C# and is encrypted using Utf8. Looking for the equivalent operation in Node JS.

Avinash
  • 2,003
  • 2
  • 17
  • 15
Suchetha B
  • 105
  • 1
  • 7
  • Upvote for the fiddle, nice work, good luck – TheGeneral Jun 18 '20 at 09:53
  • 1
    Is there a reason why you're ignoring `algorithm` and have hard-coded it to `aes-128-cbc`? – ProgrammingLlama Jun 18 '20 at 10:00
  • @John - There is no reason, I was trying to look at the other suggestions over the past questions of the users regarding the same and tried aes-128 as well. – Suchetha B Jun 18 '20 at 10:35
  • 1
    I was just wondering if that was your problem since your C# code uses AES-256. – ProgrammingLlama Jun 18 '20 at 10:44
  • @Topaco - I'm a little confused here. C# uses block size of 256. And if this has to be changed to 128 to make it aes-256 algorithm, that is not feasible in my case. C# code is already in production from a long time and cannot be modified. Only the Node JS implementation has be done in such a way that it is in sync with the C#. – Suchetha B Jun 19 '20 at 05:40
  • I have tried updating the hash to sha512 and length 32 crypto.createHash('sha512').update(String(secret)).digest('utf8').substr(0, 32) but IV value length is invalid it says. Also, I'm concerned of the algorithm to be used as C# is not using AES-256. – Suchetha B Jun 19 '20 at 05:45
  • @Topaco - thanks let me check the alternates. – Suchetha B Jun 19 '20 at 08:11
  • I have got the alternate code in C#, which is now actually the AES 256 algorithm. Only place I need help is in getting the key and IV as both the values used are same. – Suchetha B Jun 19 '20 at 13:45

4 Answers4

3

TLDR;

You're using a different IV and algorithm (AES-128 vs AES-256) so you will get different results...

You will need to use the same IV as well as the same key and algorithm if you want to get identical results. This would be an anti-pattern (i.e. don't do this)! Check John's comment about how you're ignoring the algorithm variable in your code, as at a quick glance this and the different IV are responsible for why you're getting different results.

Longer Answer;

1) You actually want it so that the same message (plain text) encrypted with the same key does not always produce the same encrypted result (cipher text). Otherwise any party that is eavesdropping will always know when a duplicate message has been sent again.

2) The initialization vector (IV) is used to provide randomness so that the same plain text does not always result in the same cipher text when a given key is used.

3) This means that to decrypt a message you need to know not only the key but also the IV.

4) The IV should be random and not deterministically derived from the key, otherwise every use of the same key will have the same IV and thus each time the same plain text is encrypted the same cipher text would result. (Leaving you vulnerable to eavesdroppers observing the results of a given message being received and beginning to determine the meaning of the message).

Have a look at the answers to this question AES Encryption - Key versus IV and also this Wikipedia entry http://en.wikipedia.org/wiki/Initialization_vector for more info.

Alex Hague
  • 1,756
  • 1
  • 13
  • 20
  • key and IV value would be same in this case, as I'm trying to match the C# code which is using same string to generate these. – Suchetha B Jun 18 '20 at 12:45
  • 1
    Certainly while you're troubleshooting / debugging your code it is fine to set these to be the same. But when you move to using this in production be aware that you should use a random IV each time you encrypt. – Alex Hague Jun 18 '20 at 12:51
0

Try this:

var crypto = require('text');

var mykey = crypto.createCipher('aes-256-cbc',  keystring, iv);
var mystr = mykey.update('abc', 'utf8', 'hex')
mystr += mykey.final('hex');

console.log(mystr);
barath
  • 1
  • 3
0

The whole issue is likely with the encoding. .Net crypto class by default uses Unicode or utf16-le. While you have specified base64 as output encoding. Change it to utf16le ie

outputEncoding = 'utf16le';

look at Node Js documentation here

0

The resolution was pretty simple than expected. The RijndaelManaged code with keylen 128 referred to AES-128 algorithm and using aes-128-cbc in nodeJS.

Also, since the C# code used getBytes for key and iv value. Buffer.from(secret) had to be used for both key and iv value in nodeJS. Final solution looks like:

   const crypto = require('crypto');
      algorithm = "aes-128-cbc",
      secret = '1234567890123456',
      keystring = new Buffer(secret),
      iv = new Buffer(secret),
      inputEncoding = 'utf8',
      outputEncoding = 'base64';
    function encrypt(text) {
      let cipher = crypto.createCipheriv(algorithm,keystring, iv);
      let encrypted = cipher.update(text, inputEncoding, outputEncoding)
      encrypted += cipher.final(outputEncoding);
      return encrypted;
    }
    
    function decrypt(encrypted) {
      let decipher = crypto.createDecipheriv(algorithm,keystring, iv)
      let dec = decipher.update(encrypted, outputEncoding, inputEncoding)
      dec += decipher.final(inputEncoding);
      return dec;
    }
Suchetha B
  • 105
  • 1
  • 7