Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Chris Hey 4 posts 24 karma points
    Mar 11, 2014 @ 13:31
    Chris Hey
    0

    Extending Umbraco Contour for encrypt/decrypt of data

    Hi,

    I need to encrypt data coming from a set of Contour forms, which i believe i can do by implementing a custom workflow http://our.umbraco.org/projects/umbraco-pro/contour/documentation/Developer/Extending-Contour/Adding-a-Workflowtype

    I would then like to use the "Entries" data grid in Contour to show the data that has been submitted in the Umbraco Back office, is there a workflow i can implement to do this? Or an interface i need to implement that Contour will execute before the data is rendered?

    Thanks in advance.

    Chris

  • Comment author was deleted

    Mar 11, 2014 @ 13:46

    Yup custom workflow on the approved stage should allow you to encrypt

    For the entries viewer, it fetches the entries through a xslt that is run on a xml version of the records, so you could probabibly place decrypt logic in the xslt file

    That's done with the \umbraco\plugins\umbracoContour\xslt\DataTables_json.xslt file

  • Comment author was deleted

    Mar 11, 2014 @ 13:47

    or if it's easier for you we can place in an event 

  • Chris Hey 4 posts 24 karma points
    Mar 11, 2014 @ 14:58
    Chris Hey
    0

    Hi,

    Thanks for your speedy reply!

    I am up for amending the xslt, what do you mean about placing it in an event? Is there an event i can override?

    Thanks

    Chris

  • alan 6 posts 25 karma points
    Oct 21, 2014 @ 09:22
    alan
    0

    Hello,

    I have been given a site to look after and it uses an old version of contour (I don't know what version) the data should be encrypted as the forms can have sensitive information.

    I have been playing around with contour and encryption and have it working for me.

    This code is not finished but it's working and hopefully it will help someone else. I'm posting this on this page because this is where I kept coming back to when googleing.

    To encrypt the data I created a new class library project in visual studio

    My encryption class is as follows

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Web;
    using System.IO;
    using System.Security.Cryptography;
    
    namespace UmbracoWorkflowProvider
    {
        public class EncryptData
        {
            private static readonly int _saltSize = 32;
    
        /// <summary>
        /// Encrypts the plainText input using the given Key.
        /// A 128 bit random salt will be generated and prepended to the ciphertext before it is base64 encoded.
        /// </summary>
        /// <param name="plainText">The plain text to encrypt.</param>
        /// <param name="key">The plain text encryption key.</param>
        /// <returns>The salt and the ciphertext, Base64 encoded for convenience.</returns>
        public static string Encrypt(string plainText, string key)
        {
            if (string.IsNullOrEmpty(plainText))
                throw new ArgumentNullException("plainText");
            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException("key");
    
            // Derive a new Salt and IV from the Key
            using (var keyDerivationFunction = new Rfc2898DeriveBytes(key, _saltSize))
            {
                var saltBytes = keyDerivationFunction.Salt;
                // 256 bit encryption
                var keyBytes = keyDerivationFunction.GetBytes(32);
                var ivBytes = keyDerivationFunction.GetBytes(16);
    
                // Create an encryptor to perform the stream transform.
                // Create the streams used for encryption.
                using (var aesManaged = new AesManaged())
                using (var encryptor = aesManaged.CreateEncryptor(keyBytes, ivBytes))
                using (var memoryStream = new MemoryStream())
                {
                    using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                    using (var streamWriter = new StreamWriter(cryptoStream))
                    {
                        // Send the data through the StreamWriter, through the CryptoStream, to the underlying MemoryStream
                        streamWriter.Write(plainText);
                    }
    
                    // Return the encrypted bytes from the memory stream, in Base64 form so we can send it right to a database (if we want).
                    var cipherTextBytes = memoryStream.ToArray();
                    Array.Resize(ref saltBytes, saltBytes.Length + cipherTextBytes.Length);
                    Array.Copy(cipherTextBytes, 0, saltBytes, _saltSize, cipherTextBytes.Length);
    
                    return Convert.ToBase64String(saltBytes);
                }
            }
        }
    
        /// <summary>
        /// Decrypts the ciphertext using the Key.
        /// </summary>
        /// <param name="ciphertext">The ciphertext to decrypt.</param>
        /// <param name="key">The plain text encryption key.</param>
        /// <returns>The decrypted text.</returns>
        public static string Decrypt(string ciphertext, string key)
        {     
           if (string.IsNullOrEmpty(ciphertext))
                throw new ArgumentNullException("cipherText");
            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException("key");
    
            try { 
            // Extract the salt from our ciphertext
            var allTheBytes = Convert.FromBase64String(ciphertext);
            var saltBytes = allTheBytes.Take(_saltSize).ToArray();
            var ciphertextBytes = allTheBytes.Skip(_saltSize).Take(allTheBytes.Length - _saltSize).ToArray();
    
            using (var keyDerivationFunction = new Rfc2898DeriveBytes(key, saltBytes))
            {
                // Derive the previous IV from the Key and Salt
                var keyBytes = keyDerivationFunction.GetBytes(32);
                var ivBytes = keyDerivationFunction.GetBytes(16);
    
                // Create a decrytor to perform the stream transform.
                // Create the streams used for decryption.
                // The default Cipher Mode is CBC and the Padding is PKCS7 which are both good
                using (var aesManaged = new AesManaged())
                using (var decryptor = aesManaged.CreateDecryptor(keyBytes, ivBytes))
                using (var memoryStream = new MemoryStream(ciphertextBytes))
                using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                using (var streamReader = new StreamReader(cryptoStream))
                {
                    // Return the decrypted bytes from the decrypting stream.
                    return streamReader.ReadToEnd();
                }
            }
                }
            catch(Exception ex)
            {
                return ciphertext; // this will return values that have not been encrypted. This was for testing!
            }
        }
    }
    }
    

    The next class in the new project will be used in a contour workflow.

    You'll need to add a reference to dlls (right click on your visual studio project and select Add Reference). I think all I had to reference was umbraco.dll, Umbraco.Forms.Core.dll, businesslogic.dll (I think this one is from contour). You might need to add more, you'll know if visual studio gives an error about a missing assembly.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Umbraco.Forms.Core;
    using Umbraco.Forms.Core.Enums;
    using Umbraco.Forms.Data.Storage;
    using Umbraco.Forms.Core.Services;
    using umbraco.BusinessLogic;
    using umbraco.NodeFactory;
    
    namespace UmbracoWorkflowProvider
    {
        public class Class1 : Umbraco.Forms.Core.WorkflowType 
        {
        public Class1()
        {
            this.Name = "The encryption workflow";
            this.Id = new Guid("D6A2C406-CF89-11DE-B075-55B055D89593"); // Change GUID this one is from an example on a site
            this.Description = "This will encrypt and save an entry";
        }
    
    
        [Umbraco.Forms.Core.Attributes.Setting("Encryption Header",
        description = "Encrypt item header",
        control = "Umbraco.Forms.Core.FieldSetting.TextField")]
        public string EncryptHeader { get; set; }
    
        [Umbraco.Forms.Core.Attributes.Setting("Document ID",
        description = "Node the log entry belongs to",
        control = "Umbraco.Forms.Core.FieldSetting.Pickers.Content")]
        public string document { get; set; }
    
        public override Umbraco.Forms.Core.Enums.WorkflowExecutionStatus Execute(Record record, Umbraco.Forms.Core.RecordEventArgs e)
        {
    
    
            foreach (RecordField rf in record.RecordFields.Values)
            {
                var fieldValue = rf.ValuesAsString();
    
                rf.Values[0] = EncryptData.Encrypt(rf.ValuesAsString(), "Crypto-key!"); // Crypto-key! = the encryption Key to use, change this!
    
                fieldValue = rf.ValuesAsString();
    
            }
    
    
            RecordStorage rs = new RecordStorage();
            record.State = FormState.Approved;
            rs.UpdateRecord(record, e.Form);
            rs.UpdateRecordXml(record, e.Form);
    
            rs.Dispose();
    
            Log.Add(LogTypes.Custom, 34343434, "Record saved with Encryption");
    
            return Umbraco.Forms.Core.Enums.WorkflowExecutionStatus.Completed;
    
        }
    
        public override List<Exception> ValidateSettings()
        {
            List<Exception> exceptions = new List<Exception>();
            //TODO
            return exceptions;       
        }
    
    }
    }
    

    Compile the project and copy the projects new dll in to your umbraco sites bin folder. Compile your umbraco site > then login.

    Open contour > select your form > click on the forms work flows > On the "When the form has been Approved" I added the work flow.

    It will be called "The encryption workflow" as above, you can change this in the Class1 constructor.

    After that the forms have been encrypting fine for me.

    Now to view the encrypted data as readable text Like Tim said above open \umbraco\plugins\umbracoContour\xslt\DataTables_json.xslt

    This is my file with the decryption added (No matter what I done I couldn't get it to reference my dll above from the xslt so I added the decryption method in to the xslt)

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:umbraco.library="urn:umbraco.library"
    xmlns:user="urn:my-scripts"
    xmlns:cs="urn:custom-cs"                
    exclude-result-prefixes="msxsl user cs umbraco.library">
    
      <xsl:output method="text" indent="no" encoding="utf-8" />
    
      <xsl:param name="records" />
      <xsl:param name="form" />
    
      <msxsl:script implements-prefix="user" language="JScript">
        function date2number(s) { return Number(new Date(s)); }
      </msxsl:script>
    
    <msxsl:script implements-prefix="cs" language="CSharp">
    <msxsl:using namespace="System"/>
    <msxsl:assembly name="System" />
    <msxsl:assembly name="System.IO" />
    <msxsl:assembly name="System.Linq" />
    <msxsl:assembly name="System.Security" />
    <msxsl:assembly name="System.Core" />
    <msxsl:assembly name="umbraco" />
    <msxsl:assembly name="BusinessLogic" />
    <msxsl:using namespace="System.Collections"/>
    <msxsl:using namespace="System.Collections.Generic"/>
    <msxsl:using namespace="System.Text"/>
    <msxsl:using namespace="System.Linq"/>
    <msxsl:using namespace="System.Threading.Tasks"/>
    <msxsl:using namespace="System.Web"/>
    <msxsl:using namespace="System.IO"/>
    <msxsl:using namespace="System.Security.Cryptography"/>
    <msxsl:using namespace="umbraco.BusinessLogic"/>
    <msxsl:using namespace="umbraco.NodeFactory"/>     
    
    <![CDATA[
    
        public string Decrypt(string ciphertext, string key)
        {   
     if (string.IsNullOrEmpty(ciphertext))
                throw new ArgumentNullException("cipherText");
            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException("key");
    
            try { 
    
            var allTheBytes = Convert.FromBase64String(ciphertext);
            //var saltBytes = allTheBytes.Take(32).ToArray();
            byte[] saltBytes = new byte[32];
            for(int i = 0; i < 32; i++)
            {
              saltBytes[i] = allTheBytes[i];
            }
            //var ciphertextBytes = allTheBytes.Skip(32).Take(allTheBytes.Length - 32).ToArray();
            byte[] ciphertextBytes = new byte[allTheBytes.Length - 32];
            int x = 0;
            for (int i = 32; i < allTheBytes.Length; i++)
            {
              ciphertextBytes[x] = allTheBytes[i];
              x++;
            }
    
    
            using (var keyDerivationFunction = new Rfc2898DeriveBytes(key, saltBytes))
            {
                // Derive the previous IV from the Key and Salt
                var keyBytes = keyDerivationFunction.GetBytes(32);
                var ivBytes = keyDerivationFunction.GetBytes(16);
    
                // Create a decrytor to perform the stream transform.
                // Create the streams used for decryption.
                // The default Cipher Mode is CBC and the Padding is PKCS7 which are both good
                using (System.Security.Cryptography.AesManaged aesManaged = new System.Security.Cryptography.AesManaged())
                using (var decryptor = aesManaged.CreateDecryptor(keyBytes, ivBytes))
                using (var memoryStream = new MemoryStream(ciphertextBytes))
                using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                using (var streamReader = new StreamReader(cryptoStream))
                {
                    // Return the decrypted bytes from the decrypting stream.
                    return streamReader.ReadToEnd();
                }
            }
                }
            catch(Exception ex)
            {
                //return ex.ToString() + "  :  " + ciphertext;
                //Log.Add(LogTypes.Error, 101010, ex.ToString());
                return ciphertext;
                //throw new Exception(ex.ToString());
            }
        }
    ]]>
    </msxsl:script>
    
      <xsl:template match="/">
    
    
        { "iTotalDisplayRecords": "<xsl:value-of select="$records/@totalRecords"/>","iTotalRecords": "<xsl:value-of select="$records/@totalRecords"/>",  "aaData": [
    <xsl:for-each select="$records//uformrecord">
      <xsl:sort select="user:date2number(string(created))" data-type="number" order="descending" />
    
      <xsl:variable name="record" select="." />
    
      [
      "<xsl:value-of select="id"/>",
      "<xsl:value-of select="translate(created,'T',' ')"/>",
      "<xsl:value-of select="ip"/>",
      "<xsl:value-of select="pageid"/>",
      "&lt;a href='<xsl:value-of select="umbraco.library:NiceUrl(pageid)"/>' target='_blank'&gt;<xsl:value-of select="umbraco.library:NiceUrl(pageid)"/>&lt;/a&gt;"
    
      <xsl:for-each select="$form//field">
        <xsl:sort select="caption" order="ascending"/>
    
        <xsl:variable name="key" select="id" />
        <xsl:variable name="fieldValues">
          <xsl:choose>
            <xsl:when test="count($record//fields/child::* [fieldKey = $key]//value) &gt; 1">
              <xsl:for-each select="$record//fields/child::* [fieldKey = $key]//value">
                <xsl:value-of select="normalize-space(translate(.,',',' '))"/>
                <xsl:if test="position() != last()">, </xsl:if>
              </xsl:for-each>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="normalize-space(translate( $record//fields/child::* [fieldKey = $key]//value,',',' '))"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>
    
        <xsl:choose>
          <xsl:when test="position() = last() and position() = 1">
            ,"<xsl:value-of select="cs:Decrypt($fieldValues, 'Crypto-key!')"/>"
          </xsl:when>
    
          <xsl:when test="position() = 1 and position() != last()">
            ,
            "<xsl:value-of select="cs:Decrypt($fieldValues, 'Crypto-key!')"/>",
          </xsl:when>
    
          <xsl:when test="position() != last() and position() &gt; 1">
            "<xsl:value-of select="cs:Decrypt($fieldValues, 'Crypto-key!')"/>",
          </xsl:when>
    
          <xsl:when test="position() = last() and position() &gt; 1">
            "<xsl:value-of select="cs:Decrypt($fieldValues, 'Crypto-key!')"/>"
          </xsl:when>
    
          <xsl:otherwise>
            <xsl:value-of select="caption"/>
          </xsl:otherwise>
        </xsl:choose>
    
      </xsl:for-each>
    
      ]<xsl:if test="position() != last()">,</xsl:if>
    </xsl:for-each>
    ]}
    

    Hopefully this post is useful!

    Cheers, Alan.

  • Ivan Saric 40 posts 89 karma points
    Oct 21, 2014 @ 16:35
    Ivan Saric
    0

    Thank you Alen, this is very useful. I have one question is it possible to decrypt form string somewhere in middle layer (in cs file like encrypt) and not in xslt? Idea is to have one place in code for encrypt/decrypt all Contour forms data.

  • alan 6 posts 25 karma points
    Oct 22, 2014 @ 12:58
    alan
    0

    Ivan, I couldn't get it working from the cs file last time that's why I put the decrypt code in the xslt.

    I have made the changes to call the cs file and it's working now. Don't know what I was doing wrong before.

    If you replace the script block in the above posts xslt file to

    <msxsl:script implements-prefix="cs" language="CSharp">
    <msxsl:using namespace="UmbracoWorkflowProvider"/>
    <msxsl:assembly name="UmbracoWorkflowProvider" />
    
    <![CDATA[
        public string Decrypt(string ciphertext, string key)
        {   
         // call the EncryptData class to handle the encryption
         return EncryptData.Decrypt(ciphertext,key );
    
        }
    ]]>
    </msxsl:script>
    

    It should work from the cs file (It does for me).

    I'm not sure if this answers your question or if you are looking to change the contour code to do the encryption/decryption automatically without editing the xslt and without adding a workflow. The project I have has such an old contour version and no code with it that I never attempted this.

    Thanks, Alan.

  • Ivan Saric 40 posts 89 karma points
    Oct 22, 2014 @ 18:14
    Ivan Saric
    0

    Alan, I'm looking to change the contour code to do the decryption automatically without editing the xslt. Custom workflow is OK for encryption. So I want create a project where I can encrypt/decrypt automatically in cs code and include that as a custom workflow or whatever.

  • alan 6 posts 25 karma points
    Oct 30, 2014 @ 16:15
    alan
    0

    Sorry Ivan, I don't know. Maybe another user on here could help.

    The users who use contour in the system I am looking after export using csv and html. To ensure they can export in these formats decrypting the information I have changed the files \umbraco\plugins\umbracoContour\xslt\excel.xslt and \umbraco\plugins\umbracoContour\xslt\Html.xslt to the following

    Excel:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:user="urn:my-scripts"
    xmlns:cs="urn:custom-cs" 
    >    
    
    <xsl:output method="text" indent="no" encoding="utf-8" />
    
    <xsl:param name="records" />
    
    
      <msxsl:script implements-prefix="cs" language="CSharp">
    <msxsl:using namespace="UmbracoWorkflowProvider"/>
    <msxsl:assembly name="UmbracoWorkflowProvider" />
    
    <![CDATA[
        public string Decrypt(string ciphertext, string key)
        {   
         return EncryptData.Decrypt(ciphertext,key );
        }
        ]]>
      </msxsl:script>
    
    
    <xsl:template match="/">
    "State","Submitted","PageId","IP","MemberId",<xsl:for-each select="$records//uformrecord[1]/fields/child::*"><xsl:sort    select="caption" order="ascending"/><xsl:if test="position() != last()">"<xsl:value-of select="normalize-   space(translate(caption,',',''))"/>",</xsl:if><xsl:if test="position()  = last()">"<xsl:value-of select="normalize-   space(translate(caption,',',''))"/>"<xsl:text>&#xD;</xsl:text></xsl:if></xsl:for-each>
    <xsl:for-each select="$records//uformrecord">"<xsl:value-of select="state"/>","<xsl:value-of select="updated"/>","<xsl:value-   of select="pageid"/>","<xsl:value-of select="ip"/>","<xsl:value-of select="memberkey"/>",<xsl:for-each    select="./fields/child::*"><xsl:sort select="caption" order="ascending"/><xsl:if test="position() != last()"><xsl:choose>    <xsl:when test="count(values//value) &gt; 1">"<xsl:for-each select="values//value"><xsl:if test="position() != last()">    <xsl:value-of select="normalize-space(translate(.,',',''))"/>;</xsl:if><xsl:if test="position() = last()"><xsl:value-of    select="normalize-space(translate(.,',',''))"/></xsl:if></xsl:for-each>",</xsl:when><xsl:otherwise>"<xsl:value-of    select="normalize-space(translate(cs:Decrypt(values//value, 'Crypto-key!'),',',''))"/>",</xsl:otherwise></xsl:choose></xsl:if>    <xsl:if test="position() = last()"><xsl:choose><xsl:when test="count(values//value) &gt; 1">"<xsl:for-each    select="values//value"><xsl:if test="position() != last()"><xsl:value-of select="normalize-space(translate(.,',',''))"/>;    </xsl:if><xsl:if test="position() = last()"><xsl:value-of select="normalize-space(translate(.,',',''))"/></xsl:if></xsl:for-   each>"</xsl:when><xsl:otherwise>"<xsl:value-of select="normalize-space(translate(cs:Decrypt(values//value, 'Crypto-   key!'),',',''))"/>"</xsl:otherwise></xsl:choose><xsl:text>&#xD;</xsl:text></xsl:if></xsl:for-each>
    </xsl:for-each>
    </xsl:template>
    
    </xsl:stylesheet>
    

    HTML:

    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:user="urn:my-scripts"
    exclude-result-prefixes="xsl msxsl user"
    xmlns:cs="urn:custom-cs" >
    
    <xsl:output method="xml" media-type="text/html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="DTD/xhtml1-strict.dtd"
    cdata-section-elements="script style"
    indent="yes"
    encoding="utf-8"/>
    
    <xsl:param name="records" />
    <xsl:param name="sortBy" />
    
      <msxsl:script implements-prefix="cs" language="CSharp">
        <msxsl:using namespace="UmbracoWorkflowProvider"/>
        <msxsl:assembly name="UmbracoWorkflowProvider" />
    
    <![CDATA[
        public string Decrypt(string ciphertext, string key)
        {   
         return EncryptData.Decrypt(ciphertext,key );
        }
    ]]>
      </msxsl:script>
    
        <xsl:template match="/">
    
        <html xmlns="http://www.w3.org/1999/xhtml">
            <head>
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                <title>
                    Export of data from umbraco forms
                </title>
            </head>
        <body>
            <table>
                <caption>Export of data from umbraco forms</caption>
                <thead>
                    <tr>
                        <th scope="col">
                            State
                        </th>
                        <th scope="col">
                            Submitted
                        </th>
                        <th scope="col">
                            Page ID
                        </th>
                        <th scope="col">
                            IP
                        </th>
                        <th scope="col">
                            Member Key
                        </th>
                        <xsl:for-each select="$records//uformrecord[1]/fields/child::*">
                            <xsl:sort select="caption" order="ascending"/>
                            <th scope="col">
                                <xsl:value-of select="normalize-space(caption)"/>
                            </th>
                        </xsl:for-each>
                    </tr>
                </thead>
                <tbody>
                    <xsl:for-each select="$records//uformrecord">
                        <tr>
                            <td>
                                <xsl:value-of select="state"/>
                            </td>
    
                            <td>
                                <xsl:value-of select="updated"/>
                            </td>
    
                            <td>
                            <xsl:value-of select="pageid"/>
                            </td>
    
                            <td>
                            <xsl:value-of select="ip"/>
                            </td>
    
                            <td>
                            <xsl:value-of select="memberkey"/>
                            </td>
    
                            <xsl:for-each select="./fields/child::*">
                                <xsl:sort select="caption" order="ascending"/>
                                <td>
                                  <xsl:for-each select="values//value">
                                    <xsl:value-of select="cs:Decrypt(normalize-space(.), 'Crypto-key!')"/>
                                    <xsl:if test="position() != last()">,</xsl:if>
                                  </xsl:for-each>
                                </td>
                            </xsl:for-each>
                        </tr>
                    </xsl:for-each>
                </tbody>
            </table>
        </body>
        </html>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Hope this helps.

    Alan.

Please Sign in or register to post replies

Write your reply to:

Draft