RSA Encryption with Power Platform Custom Connector

 Introduction:

I recently embarked on a mission to bring RSA encryption into the world of Power Platform. But it wasn't easy. The tools available just didn't fit the bill. The main problem? The C# code needed for encryption was outdated, leaving out crucial methods. Plus, there was no direct way to encrypt using the given key. So, I had to get creative.

The Challenge:
Picture this: I'm staring at a mountain of code, knowing I have to find a way to make it work within the limited environment of Power Platform. The biggest hurdle? I needed to extract specific parts from the public key – the exponent and modulus – to enable encryption.

Solving the Encryption:

Armed with the extracted components, I was ready to put the pieces together. With the exponent and modulus in hand, I could finally encrypt data within Power Platform. What once seemed like an impossible task was now within reach, thanks to perseverance and a little creativity.













C# Code Part

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

    public class Script : ScriptBase
    {
        public override async Task<HttpResponseMessage> ExecuteAsync()
        {
     

            // Check if the operation ID matches what is specified in the OpenAPI definition of the connector
            if (this.Context.OperationId == "GetEncryptedToken")
            {
                return await this.HandleReverseOperation().ConfigureAwait(false);
            }

            // Handle an invalid operation ID
            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
            response.Content = CreateJsonContent($"Unknown operation ID '{this.Context.OperationId}'");
            return response;
        }
        private async Task<HttpResponseMessage> HandleReverseOperation()
        {
            HttpResponseMessage response;

            var contentAsString = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false);

            var contentAsJson = JObject.Parse(contentAsString);
            string dataToEncrypt = ((string)contentAsJson["DataToEncrypt"]);
            string publicKeydata = ((string)contentAsJson["PublicKeyData"]);            
            string retValue = string.Empty;

            byte[] publicKeyBytes = Convert.FromBase64String(publicKeydata);
            RSAParameters p = RSAX509KeyParser.GetRSAPublicKeyParameters(publicKeyBytes, 0);

            using (RSACryptoServiceProvider rsaEncrypt = new RSACryptoServiceProvider())
            {
                rsaEncrypt.ImportParameters(p);
                byte[] dataBytes = Encoding.UTF8.GetBytes(dataToEncrypt);
                retValue = Convert.ToBase64String(rsaEncrypt.Encrypt(dataBytes, false));
            }
            JObject output = new JObject
            {
                ["ResultData"] = ((string)contentAsJson["DataToEncrypt"]).ToUpper(),
                ["EncData"] = retValue
            };

            response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content = CreateJsonContent(output.ToString());
            return response;
        }
    }

    public abstract class AbstractAsn1Base
    {
        private int _offset;
        private byte tag;
        private byte[] data;

        public AbstractAsn1Base(byte[] abyte, int i, byte tagByte)
        {
            this.tag = tagByte;
            if (abyte[i] != tagByte)
            {
                throw new Exception("Not able to parse key");
            }
            int Length = DetermineLength(abyte, i + 1);
            int bytesInLengthField = DetermineLengthLen(abyte, i + 1);
            int Start = i + bytesInLengthField + 1;
            this._offset = Start + Length;
            data = new byte[Length];
            Array.Copy(abyte, Start, data, 0, Length);
        }

        internal int Offset
        {
            get
            {
                return _offset;
            }
        }

        internal byte[] Bytes
        {
            get
            {
                return this.data;
            }
        }

        internal protected virtual int DetermineLengthLen(byte[] abyte0, int i)
        {
            int j = abyte0[i] & 0xff;
            switch (j)
            {
                case 129:
                    return 2;

                case 130:
                    return 3;

                case 131:
                    return 4;

                case 132:
                    return 5;

                case 128:
                default:
                    return 1;
            }
        }

        internal protected virtual int DetermineLength(byte[] abyte0, int i)
        {
            int j = abyte0[i] & 0xff;
            switch (j)
            {
                case 128:
                    return DetermineIndefiniteLength(abyte0, i);

                case 129:
                    return abyte0[i + 1] & 0xff;

                case 130:
                    int k = (abyte0[i + 1] & 0xff) << 8;
                    k |= abyte0[i + 2] & 0xff;
                    return k;

                case 131:
                    int l = (abyte0[i + 1] & 0xff) << 16;
                    l |= (abyte0[i + 2] & 0xff) << 8;
                    l |= abyte0[i + 3] & 0xff;
                    return l;
            }
            return j;
        }

        internal protected virtual int DetermineIndefiniteLength(byte[] abyte0, int i)
        {
            if ((abyte0[i - 1] & 0xff & 0x20) == 0)
                throw new Exception("Invalid indefinite length.");
            int j = 0;
            int k;
            int l;
            for (i++; abyte0[i] != 0 && abyte0[i + 1] != 0; i += 1 + k + l)
            {
                j++;
                k = DetermineLengthLen(abyte0, i + 1);
                j += k;
                l = DetermineLength(abyte0, i + 1);
                j += l;
            }

            return j;
        }


    }
    public class RSAX509KeyParser
    {
        public static RSAParameters GetRSAPublicKeyParameters(byte[] bytes, int i)
        {
            SequenceData seq = new SequenceData(bytes, i);
            IntegerData modContainer = new IntegerData(seq.Bytes, 22);
            IntegerData expContainer = new IntegerData(seq.Bytes, modContainer.Offset);
            return LoadKeyData(modContainer.Bytes, 0, modContainer.Bytes.Length, expContainer.Bytes, 0, expContainer.Bytes.Length);
        }
        private static RSAParameters LoadKeyData(byte[] abyte0, int i, int j, byte[] abyte1, int k, int l)
        {
            byte[] modulus = null;
            byte[] publicExponent = null;
            for (; abyte0[i] == 0; i++)
                j--;

            modulus = new byte[j];
            Array.Copy(abyte0, i, modulus, 0, j);
            int i1 = modulus.Length * 8;
            int j1 = modulus[0] & 0xff;
            for (int k1 = j1 & 0x80; k1 == 0; k1 = j1 << 1 & 0xff)
                i1--;

            if (i1 < 256 || i1 > 2048)
                throw new Exception("Invalid RSA modulus size.");
            for (; abyte1[k] == 0; k++)
                l--;

            publicExponent = new byte[l];
            Array.Copy(abyte1, k, publicExponent, 0, l);
            RSAParameters p = new RSAParameters();
            p.Modulus = modulus;
            p.Exponent = publicExponent;
            return p;
        }
    }

    public class SequenceData : AbstractAsn1Base
    {
        public SequenceData(byte[] abyte, int i) : base(abyte, i, 0x30)
        {
        }
    }
    internal class IntegerData : AbstractAsn1Base
    {
        internal IntegerData(byte[] abyte, int i) : base(abyte, i, 0x2)
        {
        }
    }

Conclusion: My journey to integrate RSA encryption into Power Platform was anything but smooth sailing. But in the end, persistence paid off. By thinking outside the box, writing custom code, and overcoming obstacles, I unlocked the secrets of RSA encryption within Power Platform. It's a testament to the power of innovation and problem-solving in the world of technology.



Comments

Popular posts from this blog

Syncing Power Automate Approval Status Across Teams, Outlook, and Apps

Embedding Canvas Apps in Model-Driven Forms Using Logical Names: A Solution to Deployment Challenges