From ac5142443a660eb2d041968b5d21bb2bc702c566 Mon Sep 17 00:00:00 2001 From: "pcw@pcw-PC.shout.local" Date: Tue, 15 Jan 2013 20:40:28 +0000 Subject: [PATCH] Fixed EncryptWithMachineKey to handle values longer than FormsAuthentication.Encrypt max length limit http://issues.umbraco.org/issue/U4-1455 --- src/Umbraco.Core/StringExtensions.cs | 52 ++++++++++++++++--- src/Umbraco.Tests/StringExtensionsTests.cs | 24 ++++++++- src/Umbraco.Web/HtmlHelperRenderExtensions.cs | 6 +-- src/Umbraco.Web/Mvc/RenderRouteHandler.cs | 4 +- 4 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 3250a21d24..2ff10f04e8 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -23,22 +23,60 @@ namespace Umbraco.Core /// Encrypt the string using the MachineKey in medium trust /// /// + /// The string value to be encrypted. /// - public static string EncryptWithMachineKey(this string toEncrypt) + public static string EncryptWithMachineKey(this string value) { - var output = FormsAuthentication.Encrypt(new FormsAuthenticationTicket(0, "temp", DateTime.Now, DateTime.MaxValue, false, toEncrypt)); - return output; + if (value == null) + return null; + + string valueToEncrypt = value; + List parts = new List(); + + const int EncrpytBlockSize = 500; + + while (valueToEncrypt.Length > EncrpytBlockSize) + { + parts.Add(valueToEncrypt.Substring(0, EncrpytBlockSize)); + valueToEncrypt = valueToEncrypt.Remove(0, EncrpytBlockSize); + } + + if (valueToEncrypt.Length > 0) + { + parts.Add(valueToEncrypt); + } + + StringBuilder encrpytedValue = new StringBuilder(); + + foreach (var part in parts) + { + var encrpytedBlock = FormsAuthentication.Encrypt(new FormsAuthenticationTicket(0, string.Empty, DateTime.Now, DateTime.MaxValue, false, part)); + encrpytedValue.AppendLine(encrpytedBlock); + } + + return encrpytedValue.ToString().TrimEnd(); } /// /// Decrypt the encrypted string using the Machine key in medium trust /// - /// + /// The string value to be decrypted /// - public static string DecryptWithMachineKey(this string encrypted) + public static string DecryptWithMachineKey(this string value) { - var output = FormsAuthentication.Decrypt(encrypted); - return output.UserData; + if (value == null) + return null; + + string[] parts = value.Split('\n'); + + StringBuilder decryptedValue = new StringBuilder(); + + foreach (var part in parts) + { + decryptedValue.Append(FormsAuthentication.Decrypt(part.TrimEnd()).UserData); + } + + return decryptedValue.ToString(); } //this is from SqlMetal and just makes it a bit of fun to allow pluralisation diff --git a/src/Umbraco.Tests/StringExtensionsTests.cs b/src/Umbraco.Tests/StringExtensionsTests.cs index c00b3cebac..c0f3634a04 100644 --- a/src/Umbraco.Tests/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/StringExtensionsTests.cs @@ -11,7 +11,10 @@ namespace Umbraco.Tests public class StringExtensionsTests { - [TestCase("This is a string to encrypt")] + [TestCase("This is a string to encrypt")] + [TestCase("This is a string to encrypt\nThis is a second line")] + [TestCase(" White space is preserved ")] + [TestCase("\nWhite space is preserved\n")] public void Encrypt_And_Decrypt(string input) { var encrypted = input.EncryptWithMachineKey(); @@ -20,6 +23,25 @@ namespace Umbraco.Tests Assert.AreEqual(input, decrypted); } + [Test()] + public void Encrypt_And_Decrypt_Long_Value() + { + // Generate a really long string + char[] chars = { 'a', 'b', 'c', '1', '2', '3', '\n' }; + + string valueToTest = string.Empty; + + // Create a string 7035 chars long + for (int i = 0; i < 1005; i++) + for (int j = 0; j < chars.Length; j++) + valueToTest += chars[j].ToString(); + + var encrypted = valueToTest.ToString().EncryptWithMachineKey(); + var decrypted = encrypted.DecryptWithMachineKey(); + Assert.AreNotEqual(valueToTest, encrypted); + Assert.AreEqual(valueToTest, decrypted); + } + [TestCase("Hello this is my string", " string", "Hello this is my")] [TestCase("Hello this is my string strung", " string", "Hello this is my string strung")] [TestCase("Hello this is my string string", " string", "Hello this is my")] diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index cec5267497..f64282d73f 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -119,7 +119,7 @@ namespace Umbraco.Web if (!string.IsNullOrWhiteSpace(surfaceRouteParams)) { - _base64String = Convert.ToBase64String(Encoding.UTF8.GetBytes(surfaceRouteParams)); + _encryptedString = surfaceRouteParams.EncryptWithMachineKey(); } _textWriter = viewContext.Writer; @@ -127,7 +127,7 @@ namespace Umbraco.Web private bool _disposed; - private readonly string _base64String; + private readonly string _encryptedString; private readonly TextWriter _textWriter; protected override void Dispose(bool disposing) @@ -137,7 +137,7 @@ namespace Umbraco.Web this._disposed = true; //write out the hidden surface form routes - _textWriter.Write(""); + _textWriter.Write(""); base.Dispose(disposing); } diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index 73a0916c36..fab342df7b 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -115,8 +115,8 @@ namespace Umbraco.Web.Mvc return null; var encodedVal = requestContext.HttpContext.Request["uformpostroutevals"]; - var decodedString = Encoding.UTF8.GetString(Convert.FromBase64String(encodedVal)); - var parsedQueryString = HttpUtility.ParseQueryString(decodedString); + var decryptedString = encodedVal.DecryptWithMachineKey(); + var parsedQueryString = HttpUtility.ParseQueryString(decryptedString); var decodedParts = new Dictionary();