0

I am trying to convert below C# code to angular 9 using CryptoJS because when I tried to convert C# code to angular, it gives different encrypted string.

How to convert C# code using Rfc2898DeriveBytes into angular 9 using CryptoJS?

C# code:

public static string Encrypt(string clearText)
{
   clearText = '123456';
   let EncryptionKey:any = "secret key string";
   let clearBytes :any= Encoding.Unicode.GetBytes(clearText);
   using (Aes encryptor = Aes.Create())
   {
       Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
       encryptor.Key = pdb.GetBytes(32);
       encryptor.IV = pdb.GetBytes(16);
       using (MemoryStream ms = new MemoryStream())
       {
           using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
           {
               cs.Write(clearBytes, 0, clearBytes.Length);
               cs.Close();
           }
           clearText = Convert.ToBase64String(ms.ToArray());
       }
   }
   return clearText;
}

Angular 9 code:

import * as CryptoJS from 'crypto-js';

encryptionKey: any = 'secret key string';

let encryptedStr = CryptoJS.AES.encrypt('123456', this.encryptionKey.trim()).toString();

Can anyone please help me to get the same encryption like C# using angular 9 ?

Darshana
  • 662
  • 1
  • 10
  • 29
  • Does this answer your question? [CryptoJS and key/IV length](https://stackoverflow.com/questions/29512858/cryptojs-and-key-iv-length) – Crowcoder Jul 29 '20 at 13:10
  • So in your c# code you have `PBKDF2` over password, but in your JS you don't. You can look at this gist and adopt it: https://gist.github.com/billyxs/c9a338b5443d346ff9eb – zaitsman Jul 29 '20 at 14:48
  • @zaitsman Thank you for reply.I tried to adopt code from link you provided but it generates different string then C#. As I have not deep knowledge of encryption/cryptography, Can you please elaborate more on this? – Darshana Jul 29 '20 at 17:32
  • @Crowcoder Thank you for reply, I have tried but I haven't get solution from provided link. I did not use fixed IV in C# like EncryptionKey . As I am beginner in cryptography, can you please let me know how I can get same encrypted string like c# in this case? – Darshana Jul 29 '20 at 17:38
  • I don't have a solution for you, I was hoping that link would help. – Crowcoder Jul 29 '20 at 18:41
  • _I tried to adopt code from link you provided but it generates different string then C#._ You should still post your additions concerning `PBKDF2`, because without this part it won't work. – Topaco Jul 29 '20 at 19:36
  • It would be good if you included your c# decryption code so that we could test the JS implementation. – zaitsman Jul 30 '20 at 00:00

1 Answers1

3

Alright, I know it's probably frowned upon on Stackoverflow as it is not a code-writing service but I found this challenge to be a good sport, so here goes:

let clearText = '123456';
let encryptionKey = CryptoJS.enc.Utf8.parse('secret key string');
let salt = CryptoJS.enc.Base64.parse('SXZhbiBNZWR2ZWRldg=='); // this is the byte array in .net fiddle
  
let iterations = 1000; // https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes?view=netcore-3.1
let keyAndIv = CryptoJS.PBKDF2(encryptionKey, salt, { keySize: 256/32 + 128/32, iterations: iterations, hasher: CryptoJS.algo.SHA1 }); // so PBKDF2 in CryptoJS is direct in that it
// always begins at the beginning of the password, whereas the .net
// implementation offsets by the last length each time .GetBytes() is called
 // so we had to generate a Iv + Salt password and then split it
let hexKeyAndIv = CryptoJS.enc.Hex.stringify(keyAndIv);

let key = CryptoJS.enc.Hex.parse(hexKeyAndIv.substring(0, 64));
let iv = CryptoJS.enc.Hex.parse(hexKeyAndIv.substring(64, hexKeyAndIv.length));

// As you're using Encoding.Unicde in .net, we have to use CryptoJS.enc.Utf16LE here.
let encryptedStr = CryptoJS.AES.encrypt(CryptoJS.enc.Utf16LE.parse(clearText), key, {iv: iv}).toString();

console.log(encryptedStr)

JSFiddle: https://jsfiddle.net/nhupdk6q/1/

Here's the corresponding code I used in .Net fiddle (sadly that does not allow to 'save' without logging in), I used the .Net Core 3.1 runtime with System.Security.Cryptography.Algorithms nuget package.

using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;

public class Program
{
    public static void Main()
    {
        var clearText = "123456";
        var EncryptionKey = "secret key string";

        var clearBytes = Encoding.Unicode.GetBytes(clearText);
        using (System.Security.Cryptography.Aes encryptor = Aes.Create())
        {
             Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
             encryptor.Key = pdb.GetBytes(32);
             encryptor.IV = pdb.GetBytes(16);
             using (MemoryStream ms = new MemoryStream())
             {     
               using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
               {
                   cs.Write(clearBytes, 0, clearBytes.Length);
                   cs.Close();
               }
               clearText = Convert.ToBase64String(ms.ToArray());
            } 
        }
    
        Console.WriteLine(clearText);
    }
}

Both listings print the same string.

The biggest challenge, as mentioned in the comments, was that since your .Net code re-uses the same PBKDF2 instance, each .GetBytes() call is essentially the next cb of the password. (cb is parameter name in .net code https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes.getbytes?view=netcore-3.1#System_Security_Cryptography_Rfc2898DeriveBytes_GetBytes_System_Int32_)

The PBKDF2 in CryptoJS does not have this so we need to call it once and get both Key and Iv and then do the parsing ourselves.

zaitsman
  • 8,984
  • 6
  • 47
  • 79