Common encrypt/decrypt code example for C# and Node.js/crypto

I’m attempting to use Application Request Routing (ARR) in IIS for passing a set of paths to a Node.js website. My issue is being able to get/set the authentication ticket on either side.

I just really need a simple example of an Encrypt/Decrypt pair that will work for C# and Node.js close to out of the box with the same results for both. I’ll be working on this problem myself as time permits over the next few days, and intend to answer if nobody comes up with an answer before me.

My intention is to write the node side as a connect/express module on the Node.js side. I am already doing a custom authentication in the ASP.Net solution, and can easily replace my current method with something that can be secure from both platforms (so long as they share the same key).


Current code to create the authentication cookie in AccountController.cs

private void ProcessUserLogin(MyEntityModel db, SiteUser user, bool remember=false)
{
  var roles = String.Join("|", value:user.SiteRoles.Select(sr => sr.Name.ToLowerInvariant().Trim()).Distinct().ToArray());

  //update the laston record(s)
  user.UserAgent = Request.UserAgent;
  user.LastOn = DateTimeOffset.UtcNow;
  db.SaveChanges();

  // Create and tuck away the cookie
  var authTicket = new FormsAuthenticationTicket(
    1
    ,user.Username
    ,DateTime.Now
    ,DateTime.Now.AddDays(31) //max 31 days
    ,remember
    ,string.IsNullOrWhiteSpace(roles) ? "guest" : roles
  );
  var ticket = FormsAuthentication.Encrypt(authTicket);
  var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, ticket);
  if (remember) cookie.Expires = DateTime.Now.AddDays(8);
  Response.Cookies.Add(cookie);
}

Current code to read the authentication cookie in Global.asax.cs

void Application_AuthenticateRequest(object sender, EventArgs args)
{
  HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
  if (authCookie == null) return;

  FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);

  string[] roles = authTicket.UserData.Split(new Char[] { '|' });

  //create new generic identity, and corresponding principal...
  var g = new GenericIdentity(authTicket.Name);
  var up = new GenericPrincipal(g, roles);

  //set principal for current request & thread (app will handle transitions from here)
  Thread.CurrentPrincipal = Context.User = up;
}

Relevant portion of the Web.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <membership>
      <providers>
        <!-- Remove default provider(s), so custom override works -->
        <clear />
      </providers>
    </membership>
  </system.web>
</configuration>

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

Here is a work example using DES algorithm. reference

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

public class Test
{
    public static string Encrypt(string toEncrypt, string key, bool useHashing) 
    {     
        byte[] keyArray;     
        byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);      

        if (useHashing)     
        {         
            MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
            keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));     
        }     
        else
            keyArray = UTF8Encoding.UTF8.GetBytes(key);      

        var tdes = new TripleDESCryptoServiceProvider();
        tdes.Key = keyArray;     
        // tdes.Mode = CipherMode.CBC;  // which is default     
        // tdes.Padding = PaddingMode.PKCS7;  // which is default

        Console.WriteLine("iv: {0}", Convert.ToBase64String(tdes.IV));

        ICryptoTransform cTransform = tdes.CreateEncryptor();     
        byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0,
            toEncryptArray.Length);      
        return Convert.ToBase64String(resultArray, 0, resultArray.Length); 
    }  

    public static void Main()
    {
        Console.WriteLine("encrypted as: {0}", Encrypt("12345", "abcdefghijklmnop", false));
    }
}

which outputs

iv: pdMBMjdeFdo=
encrypted as: 3uDkdT6aQ3c=

And use the right algorithm des-ede-cbc in node.js:

var crypto = require('crypto');

var alg = 'des-ede-cbc';

var key = new Buffer('abcdefghijklmnop', 'utf-8');
var iv = new Buffer('pdMBMjdeFdo=', 'base64');

var encrypted = new Buffer('3uDkdT6aQ3c=', 'base64');
var source = '12345';

var cipher = crypto.createCipheriv(alg, key, iv);
var encoded = cipher.update(source, 'ascii', 'base64');
encoded += cipher.final('base64');

console.log(encoded, encrypted.toString('base64'));

var decipher = crypto.createDecipheriv(alg, key, iv);
var decoded = decipher.update(encrypted, 'binary', 'ascii');
decoded += decipher.final('ascii');

console.log(decoded, source);

which outputs

3uDkdT6aQ3c= 3uDkdT6aQ3c=
12345 12345

Solution 2

@xiaoyi answer is correct. In addition, I am just added a bit clear details code.

Encryption with DES algorithm in C#

  public static string EncryptTest(string toEncrypt, string key, bool useHashing)
        {
            byte[] keyArray;
            byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
            byte[] initVectorBytes = Encoding.ASCII.GetBytes("$secure#");

            if (useHashing)
            {
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
            }
            else
                keyArray = UTF8Encoding.UTF8.GetBytes(key);

            var tdes = new TripleDESCryptoServiceProvider();
            tdes.Key = keyArray;
            tdes.Mode = CipherMode.CBC;  // which is default     
            tdes.Padding = PaddingMode.PKCS7;  // which is default
            tdes.IV = initVectorBytes;

            ICryptoTransform cTransform = tdes.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0,
                toEncryptArray.Length);
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }

When EncryptTest("1234", "abcdefghijklmnop", false) Output: GD/JSd91ZnQ=

Encryption with DES algorithm in nodejs

var algorithm = 'des-ede-cbc';

function encryption(plainText) {

    var key = new Buffer.from("abcdefghijklmnop", 'utf-8');
    var iv = new Buffer.from("$secure#", "ascii");

    var cipher = crypto.createCipheriv(algorithm, key, iv);
    var encoded = cipher.update(plainText, 'ascii', 'base64');
    encoded += cipher.final('base64');

    console.log(encoded);

    return encoded;
}

When encryption("1234") output: GD/JSd91ZnQ=

Node: new Buffer('', '') is deprecated. I replaced it with new Buffer.from()

Decryption with DES algorithm in C#

public static string DecryptTest(string cipherString, string key, bool useHashing)
        {
            byte[] keyArray;
            byte[] toEncryptArray = Convert.FromBase64String(cipherString);
            byte[] initVectorBytes = Encoding.ASCII.GetBytes("$secure#");

            if (useHashing)
            {
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
            }
            else
                 keyArray = UTF8Encoding.UTF8.GetBytes(key);

            var tdes = new TripleDESCryptoServiceProvider();
            tdes.Key = keyArray;
            tdes.Mode = CipherMode.CBC;  // which is default     
            tdes.Padding = PaddingMode.PKCS7;  // which is default
            tdes.IV = initVectorBytes;

            ICryptoTransform cTransform = tdes.CreateDecryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

            return UTF8Encoding.UTF8.GetString(resultArray);
        }

When DecryptTest("GD/JSd91ZnQ=", "abcdefghijklmnop", false) Output: 1234

Decryption with DES algorithm in nodejs

var algorithm = 'des-ede-cbc';

function decryption(chipertext) {

   var key = new Buffer.from("abcdefghijklmnop", 'utf-8');
    var iv = new Buffer.from("$secure#", "ascii");
    var encrypted = new Buffer.from(chipertext, 'base64');

    var decipher = crypto.createDecipheriv(algorithm, key, iv);
    var decoded = decipher.update(encrypted, 'binary', 'ascii');
    decoded += decipher.final('ascii');

    console.log(decoded);

    return decoded;
}

When decryption("GD/JSd91ZnQ=") output: 1234

More details on DES algorithm

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply