Wednesday, July 08, 2009

Encryption in Metastorm BPM

A question came up on the Metastorm forums about encrypting sensitive data contained in custom variables so I thought I’d see what I could come up with. I took this C# code and translated it to JScript.NET, which looks something like this

import System;
import System.IO;
import System.Security.Cryptography;
import System.Text;
import eWork.Engine.ScriptObject;

package Encrypt.Encrypt
{
    public class Encryption
    {
        private static const password : String = "password";
        public static function Encrypt( ework: SyncProcessData, args: Object[] ) : Object
        {
            // args[0] - string to encrypt
            // returns the encrypted string

            var encrypt : Encryption = new Encryption(password);
            return encrypt.Encrypt(args[0]);
        }

        public static function Decrypt( ework: SyncProcessData, args: Object[] ) : Object
        {
            // args[0] - string to decrypt
            // returns the decrypted string
            if (args[0] == "")
                return "";

            var encrypt : Encryption = new Encryption(password);
            return encrypt.Decrypt(args[0]);
        }

        function Encryption(password : String)
        {
            GenerateKey(password);
        }

        private var Key : byte[];
        private var Vector : byte[];

        private function GenerateKey(password : String)
        {
            var sha : SHA384Managed  = new SHA384Managed();
            var b : byte[] = sha.ComputeHash(new ASCIIEncoding().GetBytes(password));

            Key = new byte[32];
            Vector = new byte[16];

            System.Array.Copy(b, 0, Key, 0, 32);
            System.Array.Copy(b, 32, Vector, 0, 16);
        }

        public function Encrypt(plainText : String) : String
        {
            var data : byte[] = new ASCIIEncoding().GetBytes(plainText);

            var crypto : RijndaelManaged = new RijndaelManaged();
            var encryptor : ICryptoTransform = crypto.CreateEncryptor(Key, Vector);

            var memoryStream : MemoryStream = new MemoryStream();
            var crptoStream : CryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);

            crptoStream.Write(data, 0, data.Length);
            crptoStream.FlushFinalBlock();

            crptoStream.Close();
            memoryStream.Close();

            return Convert.ToBase64String(memoryStream.ToArray());
        }

        public function Decrypt(encryptedText : String) : String
        {
            var cipher : byte[] = Convert.FromBase64String(encryptedText);

            var crypto : RijndaelManaged = new RijndaelManaged();
            var encryptor : ICryptoTransform = crypto.CreateDecryptor(Key, Vector);

            var memoryStream : MemoryStream = new MemoryStream(cipher);
            var crptoStream : CryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Read);

            var data : byte[] = new byte[cipher.Length];
            var dataLength : int = crptoStream.Read(data, 0, data.Length);

            memoryStream.Close();
            crptoStream.Close();

            return (new ASCIIEncoding()).GetString(data, 0, dataLength);
        }
    }
}

Then all that is required is to decrypt the string when the form is loaded and encrypt it when the form is saved, like so

%sensitive:=%ScriptEval(JScript.NET,,%Procedure.Name,%MapName,"Encrypt.Encrypt.Encryption.Decrypt",%sensitive )

%sensitive:=%ScriptEval(JScript.NET,,%Procedure.Name,%MapName,"Encrypt.Encrypt.Encryption.Encrypt",%sensitive)

Now the user should see the unencrypted text and the encrypted version will be stored in the database. You will also need to decrypt the data anywhere else you need to use it.

One thing to realise at this point is that the system is still not secure. Although it will stop casual viewers who just run a query against the custom variable table, it won’t stop a more professional hacker. The script text is also stored in the database, so a hacker can have a look at that and find the password used to encrypt/decrypt the data. A more secure implementation would store the password in a location that only the engine account has access to, assuming the engine account is also locked down.

Download the demo procedure

No comments: