From: João Matos Date: Tue, 16 Dec 2014 19:26:08 +0000 (-0500) Subject: Merge pull request #1349 from martinjt/MachineKeyProtect X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=5742335f66bc628ad0ce75be7870127da04916df;hp=8597a88b69881c0a3c70bc5795d70f801cae6b1e;p=mono.git Merge pull request #1349 from martinjt/MachineKeyProtect Implement MachineKey.Protect and MachineKey.Unprotect --- diff --git a/mcs/class/System.Web/System.Web.Security/MachineKey.cs b/mcs/class/System.Web/System.Web.Security/MachineKey.cs index 636997e5d9b..193ec751952 100644 --- a/mcs/class/System.Web/System.Web.Security/MachineKey.cs +++ b/mcs/class/System.Web/System.Web.Security/MachineKey.cs @@ -30,6 +30,9 @@ using System; using System.Web; using System.Web.Configuration; using System.Web.Util; +using System.IO; +using System.Security.Cryptography; +using System.Text; namespace System.Web.Security { @@ -104,5 +107,61 @@ namespace System.Web.Security return MachineKeySectionUtils.GetHexString (result); } + +#if NET_4_5 + public static byte[] Protect (byte[] userData, params string[] purposes) + { + if (userData == null) + throw new ArgumentNullException ("userData"); + + foreach (var purpose in purposes) + { + if (string.IsNullOrWhiteSpace (purpose)) + throw new ArgumentException ("all purpose parameters must contain text"); + } + + var config = WebConfigurationManager.GetWebApplicationSection ("system.web/machineKey") as MachineKeySection; + var purposeJoined = string.Join (";", purposes); + var purposeBytes = GetHashed (purposeJoined); + var bytes = new byte [userData.Length + purposeBytes.Length]; + purposeBytes.CopyTo (bytes, 0); + userData.CopyTo (bytes, purposeBytes.Length); + return MachineKeySectionUtils.Encrypt (config, bytes); + } + + public static byte[] Unprotect (byte[] protectedData, params string[] purposes) + { + if (protectedData == null) + throw new ArgumentNullException ("protectedData"); + + foreach (var purpose in purposes) { + if (string.IsNullOrWhiteSpace (purpose)) + throw new ArgumentException ("all purpose parameters must contain text"); + } + + var config = WebConfigurationManager.GetWebApplicationSection ("system.web/machineKey") as MachineKeySection; + var purposeJoined = string.Join (";", purposes); + var purposeBytes = GetHashed (purposeJoined); + var unprotected = MachineKeySectionUtils.Decrypt (config, protectedData); + + for (int i = 0; i < purposeBytes.Length; i++) { + if (purposeBytes [i] != unprotected [i]) + throw new CryptographicException (); + } + + var dataLength = unprotected.Length - purposeBytes.Length; + var result = new byte [dataLength]; + Array.Copy (unprotected, purposeBytes.Length, result, 0, dataLength); + return result; + } + + static byte[] GetHashed (string purposes) + { + using (var hash = SHA512.Create ()) { + var bytes = Encoding.UTF8.GetBytes (purposes); + return hash.ComputeHash (bytes, 0, bytes.Length); + } + } +#endif } } diff --git a/mcs/class/System.Web/Test/System.Web.Security/MachineKeyTest.cs b/mcs/class/System.Web/Test/System.Web.Security/MachineKeyTest.cs index f00e5c500e0..d00e0eb60d7 100644 --- a/mcs/class/System.Web/Test/System.Web.Security/MachineKeyTest.cs +++ b/mcs/class/System.Web/Test/System.Web.Security/MachineKeyTest.cs @@ -25,6 +25,9 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +using System.Security.Cryptography; + + #if NET_4_0 using System; using System.Text; @@ -166,6 +169,39 @@ namespace MonoTests.System.Web.Security // Success } } + +#if NET_4_5 + [Test] + public void Protect () + { + AssertExtensions.Throws (() => + MachineKey.Protect (null, null), + "MachineKey.Protect not throwing an ArgumentNullException"); + + AssertExtensions.Throws (() => + MachineKey.Protect (null, new [] { "test" }), + "MachineKey.Protect not throwing an ArgumentNullException"); + + var testString = "asfgasd43tqrt4"; + var validUsages = new [] { "usage1", "usage2" }; + var oneUsage = new [] { "usage1" }; + var invalidUsages = new [] { "usage1", "invalidUsage" }; + + var plainBytes = Encoding.ASCII.GetBytes (testString); + var encryptedBytes = MachineKey.Protect (plainBytes, validUsages); + var validDecryptedBytes = MachineKey.Unprotect (encryptedBytes, validUsages); + + Assert.AreEqual (plainBytes, validDecryptedBytes, "Decryption didn't work"); + + AssertExtensions.Throws (() => + MachineKey.Unprotect (encryptedBytes, invalidUsages), + "Purposes not encrypting properly"); + + AssertExtensions.Throws (() => + MachineKey.Unprotect (encryptedBytes, oneUsage), + "Single purpose working when multiple supplied"); + } +#endif } } -#endif \ No newline at end of file +#endif