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?
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
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"/>",
"<a href='<xsl:value-of select="umbraco.library:NiceUrl(pageid)"/>' target='_blank'><xsl:value-of select="umbraco.library:NiceUrl(pageid)"/></a>"
<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) > 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() > 1">
"<xsl:value-of select="cs:Decrypt($fieldValues, 'Crypto-key!')"/>",
</xsl:when>
<xsl:when test="position() = last() and position() > 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>
]}
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.
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.
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.
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
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
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
or if it's easier for you we can place in an event
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
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
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.
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)
Hopefully this post is useful!
Cheers, Alan.
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.
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
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.
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.
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:
HTML:
Hope this helps.
Alan.
is working on a reply...