From 8f8d839e77a6c3935c9f1395f7738048394f0040 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Wed, 12 Jan 2011 18:58:02 -0800 Subject: [PATCH] Improving encryption utilities --HG-- branch : 1.x --- .../AzureShellSettingsManager.cs | 12 ++- .../Users/ShellSettingsUtility.cs | 18 ++-- .../DefaultTenantManagerTests.cs | 5 +- .../Security/DefaultEncryptionServiceTests.cs | 61 ++++++++++--- .../Orchard.Setup/Services/SetupService.cs | 17 ++-- src/Orchard.sln | 24 +++++- .../Configuration/ShellSettings.cs | 7 +- .../Configuration/ShellSettingsManager.cs | 21 ++--- .../Providers/DefaultEncryptionService.cs | 86 +++++++++++++------ 9 files changed, 171 insertions(+), 80 deletions(-) diff --git a/src/Orchard.Azure/Environment/Configuration/AzureShellSettingsManager.cs b/src/Orchard.Azure/Environment/Configuration/AzureShellSettingsManager.cs index 6e2392cd9..097ffbc70 100644 --- a/src/Orchard.Azure/Environment/Configuration/AzureShellSettingsManager.cs +++ b/src/Orchard.Azure/Environment/Configuration/AzureShellSettingsManager.cs @@ -99,8 +99,11 @@ namespace Orchard.Azure.Environment.Configuration { case "EncryptionKey": shellSettings.EncryptionKey = value; break; - case "EncryptionIV": - shellSettings.EncryptionIV = value; + case "HashAlgorithm": + shellSettings.HashAlgorithm = value; + break; + case "HashKey": + shellSettings.HashKey = value; break; } } @@ -113,7 +116,7 @@ namespace Orchard.Azure.Environment.Configuration { if (settings == null) return ""; - return string.Format("Name: {0}\r\nDataProvider: {1}\r\nDataConnectionString: {2}\r\nDataPrefix: {3}\r\nRequestUrlHost: {4}\r\nRequestUrlPrefix: {5}\r\nState: {6}\r\nEncryptionAlgorithm: {7}\r\nEncryptionKey: {8}\r\nEncryptionIV: {9}\r\n", + return string.Format("Name: {0}\r\nDataProvider: {1}\r\nDataConnectionString: {2}\r\nDataPrefix: {3}\r\nRequestUrlHost: {4}\r\nRequestUrlPrefix: {5}\r\nState: {6}\r\nEncryptionAlgorithm: {7}\r\nEncryptionKey: {8}\r\nHashAlgorithm: {9}\r\nHashKey: {10}\r\n", settings.Name, settings.DataProvider, settings.DataConnectionString ?? EmptyValue, @@ -123,7 +126,8 @@ namespace Orchard.Azure.Environment.Configuration { settings.State != null ? settings.State.ToString() : String.Empty, settings.EncryptionAlgorithm ?? EmptyValue, settings.EncryptionKey ?? EmptyValue, - settings.EncryptionIV ?? EmptyValue + settings.HashAlgorithm ?? EmptyValue, + settings.HashKey ?? EmptyValue ); } } diff --git a/src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs b/src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs index de67b3d19..577f2c526 100644 --- a/src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs +++ b/src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs @@ -5,22 +5,20 @@ using Orchard.Utility.Extensions; namespace Orchard.Tests.Modules.Users { public class ShellSettingsUtility { public static ShellSettings CreateEncryptionEnabled() { - // generate random keys for encryption - var key = new byte[32]; - var iv = new byte[16]; - using ( var random = new RNGCryptoServiceProvider() ) { - random.GetBytes(key); - random.GetBytes(iv); - } + + const string encryptionAlgorithm = "AES"; + const string hashAlgorithm = "HMACSHA256"; return new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo", - EncryptionAlgorithm = "AES", - EncryptionKey = key.ToHexString(), - EncryptionIV = iv.ToHexString() + EncryptionAlgorithm = encryptionAlgorithm, + EncryptionKey = SymmetricAlgorithm.Create(encryptionAlgorithm).Key.ToHexString(), + HashAlgorithm = hashAlgorithm, + HashKey = HMAC.Create(hashAlgorithm).Key.ToHexString() }; + } } } diff --git a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs index 530e8cd01..272df27bf 100644 --- a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs +++ b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs @@ -70,7 +70,7 @@ namespace Orchard.Tests.Environment.Configuration { [Test] public void EncryptionSettingsAreStoredAndReadable() { IShellSettingsManager loader = new ShellSettingsManager(_appDataFolder, new Mock().Object); - var foo = new ShellSettings { Name = "Foo", DataProvider = "Bar", DataConnectionString = "Quux", EncryptionAlgorithm = "AES", EncryptionKey = "ABCDEFG", EncryptionIV= "HIJKL" }; + var foo = new ShellSettings { Name = "Foo", DataProvider = "Bar", DataConnectionString = "Quux", EncryptionAlgorithm = "AES", EncryptionKey = "ABCDEFG", HashAlgorithm = "HMACSHA256", HashKey = "HIJKLMN" }; loader.SaveSettings(foo); Assert.That(loader.LoadSettings().Count(), Is.EqualTo(1)); @@ -78,7 +78,8 @@ namespace Orchard.Tests.Environment.Configuration { Assert.That(settings.EncryptionAlgorithm, Is.EqualTo("AES")); Assert.That(settings.EncryptionKey, Is.EqualTo("ABCDEFG")); - Assert.That(settings.EncryptionIV, Is.EqualTo("HIJKL")); + Assert.That(settings.HashAlgorithm, Is.EqualTo("HMACSHA256")); + Assert.That(settings.HashKey, Is.EqualTo("HIJKLMN")); } } } diff --git a/src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs b/src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs index 37a9fbc45..835ef031f 100644 --- a/src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs +++ b/src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs @@ -1,4 +1,5 @@ -using System.Security.Cryptography; +using System; +using System.Security.Cryptography; using System.Text; using Autofac; using NUnit.Framework; @@ -10,36 +11,33 @@ using Orchard.Utility.Extensions; namespace Orchard.Tests.Security { [TestFixture] public class DefaultEncryptionServiceTests { - private IContainer container; + private IContainer _container; [SetUp] public void Init() { - var key = new byte[32]; - var iv = new byte[16]; - using ( var random = new RNGCryptoServiceProvider() ) { - random.GetBytes(key); - random.GetBytes(iv); - } + const string encryptionAlgorithm = "AES"; + const string hashAlgorithm = "HMACSHA256"; var shellSettings = new ShellSettings { Name = "Foo", DataProvider = "Bar", DataConnectionString = "Quux", - EncryptionAlgorithm = "AES", - EncryptionKey = key.ToHexString(), - EncryptionIV = iv.ToHexString() + EncryptionAlgorithm = encryptionAlgorithm, + EncryptionKey = SymmetricAlgorithm.Create(encryptionAlgorithm).Key.ToHexString(), + HashAlgorithm = hashAlgorithm, + HashKey = HMAC.Create(hashAlgorithm).Key.ToHexString() }; var builder = new ContainerBuilder(); builder.RegisterInstance(shellSettings); builder.RegisterType().As(); - container = builder.Build(); + _container = builder.Build(); } [Test] public void CanEncodeAndDecodeData() { - var encryptionService = container.Resolve(); + var encryptionService = _container.Resolve(); var secretData = Encoding.Unicode.GetBytes("this is secret data"); var encrypted = encryptionService.Encode(secretData); @@ -49,5 +47,42 @@ namespace Orchard.Tests.Security { Assert.That(decrypted, Is.EqualTo(secretData)); } + [Test] + public void ShouldDetectTamperedData() { + var encryptionService = _container.Resolve(); + + var secretData = Encoding.Unicode.GetBytes("this is secret data"); + var encrypted = encryptionService.Encode(secretData); + + try { + // tamper the data + encrypted[encrypted.Length - 1] ^= 66; + var decrypted = encryptionService.Decode(encrypted); + } + catch { + return; + } + Assert.Fail(); + } + + [Test] + public void SuccessiveEncodeCallsShouldNotReturnTheSameData() { + var encryptionService = _container.Resolve(); + + var secretData = Encoding.Unicode.GetBytes("this is secret data"); + byte[] previousEncrypted = null; + for (int i = 0; i < 10; i++) { + var encrypted = encryptionService.Encode(secretData); + var decrypted = encryptionService.Decode(encrypted); + + Assert.That(encrypted, Is.Not.EqualTo(decrypted)); + Assert.That(decrypted, Is.EqualTo(secretData)); + + if(previousEncrypted != null) { + Assert.That(encrypted, Is.Not.EqualTo(previousEncrypted)); + } + previousEncrypted = encrypted; + } + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs index a27f70638..50718f08b 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs @@ -126,17 +126,14 @@ namespace Orchard.Setup.Services { #region Encryption Settings - // generate random keys for encryption - var key = new byte[32]; - var iv = new byte[16]; - using ( var random = new RNGCryptoServiceProvider() ) { - random.GetBytes(key); - random.GetBytes(iv); - } - shellSettings.EncryptionAlgorithm = "AES"; - shellSettings.EncryptionKey = key.ToHexString(); - shellSettings.EncryptionIV = iv.ToHexString(); + // randomly generated key + shellSettings.EncryptionKey = SymmetricAlgorithm.Create(shellSettings.EncryptionAlgorithm).Key.ToHexString(); + + shellSettings.HashAlgorithm = "HMACSHA256"; + // randomly generated key + shellSettings.HashKey = HMAC.Create(shellSettings.HashAlgorithm).Key.ToHexString(); + #endregion var shellDescriptor = new ShellDescriptor { diff --git a/src/Orchard.sln b/src/Orchard.sln index 10d7b6af4..1f34e989c 100644 --- a/src/Orchard.sln +++ b/src/Orchard.sln @@ -104,6 +104,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Scripting", "Orchar EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{3E10BF6D-ADA5-417D-B36C-EBB0660B475E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Contrib.Taxonomies", "Orchard.Web\Modules\Contrib.Taxonomies\Contrib.Taxonomies.csproj", "{E649EA64-D213-461B-87F7-D67035801443}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Contrib.Voting", "Orchard.Web\Modules\Contrib.Voting\Contrib.Voting.csproj", "{4DA5F35F-E62C-4B49-B5A8-379503257B3A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeCoverage|Any CPU = CodeCoverage|Any CPU @@ -113,6 +117,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4DA5F35F-E62C-4B49-B5A8-379503257B3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DA5F35F-E62C-4B49-B5A8-379503257B3A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DA5F35F-E62C-4B49-B5A8-379503257B3A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DA5F35F-E62C-4B49-B5A8-379503257B3A}.Release|Any CPU.Build.0 = Release|Any CPU {50B779EA-EC00-4699-84C0-03B395C365D2}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU {50B779EA-EC00-4699-84C0-03B395C365D2}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {50B779EA-EC00-4699-84C0-03B395C365D2}.Coverage|Any CPU.ActiveCfg = Release|Any CPU @@ -550,6 +558,16 @@ Global {99002B65-86F7-415E-BF4A-381AA8AB9CCC}.FxCop|Any CPU.Build.0 = Release|Any CPU {99002B65-86F7-415E-BF4A-381AA8AB9CCC}.Release|Any CPU.ActiveCfg = Release|Any CPU {99002B65-86F7-415E-BF4A-381AA8AB9CCC}.Release|Any CPU.Build.0 = Release|Any CPU + {E649EA64-D213-461B-87F7-D67035801443}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {E649EA64-D213-461B-87F7-D67035801443}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU + {E649EA64-D213-461B-87F7-D67035801443}.Coverage|Any CPU.ActiveCfg = Release|Any CPU + {E649EA64-D213-461B-87F7-D67035801443}.Coverage|Any CPU.Build.0 = Release|Any CPU + {E649EA64-D213-461B-87F7-D67035801443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E649EA64-D213-461B-87F7-D67035801443}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E649EA64-D213-461B-87F7-D67035801443}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {E649EA64-D213-461B-87F7-D67035801443}.FxCop|Any CPU.Build.0 = Release|Any CPU + {E649EA64-D213-461B-87F7-D67035801443}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E649EA64-D213-461B-87F7-D67035801443}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -585,6 +603,7 @@ Global {FBC8B571-ED50-49D8-8D9D-64AB7454A0D6} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {2AD6973D-C7BB-416E-89FE-EEE34664E05F} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {99002B65-86F7-415E-BF4A-381AA8AB9CCC} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} + {E649EA64-D213-461B-87F7-D67035801443} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {ABC826D4-2FA1-4F2F-87DE-E6095F653810} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} {F112851D-B023-4746-B6B1-8D2E5AD8F7AA} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} {6CB3EB30-F725-45C0-9742-42599BA8E8D2} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} @@ -593,8 +612,9 @@ Global {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56} = {383DBA32-4A3E-48D1-AAC3-75377A694452} {33B1BC8D-E292-4972-A363-22056B207156} = {383DBA32-4A3E-48D1-AAC3-75377A694452} {0DFA2E10-96C8-4E05-BC10-B710B97ECCDE} = {383DBA32-4A3E-48D1-AAC3-75377A694452} - {CB70A642-8CEC-4DDE-8C9F-AD08900EC98D} = {74492CBC-7201-417E-BC29-28B4C25A58B0} - {7354DF37-934B-46CF-A13C-455D5F5F5413} = {3E10BF6D-ADA5-417D-B36C-EBB0660B475E} {94E694A2-D140-468D-A277-C5FCE1D13E9B} = {3E10BF6D-ADA5-417D-B36C-EBB0660B475E} + {7354DF37-934B-46CF-A13C-455D5F5F5413} = {3E10BF6D-ADA5-417D-B36C-EBB0660B475E} + {CB70A642-8CEC-4DDE-8C9F-AD08900EC98D} = {74492CBC-7201-417E-BC29-28B4C25A58B0} + {4DA5F35F-E62C-4B49-B5A8-379503257B3A} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} EndGlobalSection EndGlobal diff --git a/src/Orchard/Environment/Configuration/ShellSettings.cs b/src/Orchard/Environment/Configuration/ShellSettings.cs index 65795783f..24b23a389 100644 --- a/src/Orchard/Environment/Configuration/ShellSettings.cs +++ b/src/Orchard/Environment/Configuration/ShellSettings.cs @@ -18,7 +18,8 @@ RequestUrlPrefix = settings.RequestUrlPrefix; EncryptionAlgorithm = settings.EncryptionAlgorithm; EncryptionKey = settings.EncryptionKey; - EncryptionIV = settings.EncryptionIV; + HashAlgorithm = settings.HashAlgorithm; + HashKey = settings.HashKey; State = settings.State; } @@ -33,7 +34,9 @@ public string EncryptionAlgorithm { get; set; } public string EncryptionKey { get; set; } - public string EncryptionIV { get; set; } + + public string HashAlgorithm { get; set; } + public string HashKey { get; set; } public TenantState State { get; set; } } diff --git a/src/Orchard/Environment/Configuration/ShellSettingsManager.cs b/src/Orchard/Environment/Configuration/ShellSettingsManager.cs index 2708747df..21591322c 100644 --- a/src/Orchard/Environment/Configuration/ShellSettingsManager.cs +++ b/src/Orchard/Environment/Configuration/ShellSettingsManager.cs @@ -49,15 +49,13 @@ namespace Orchard.Environment.Configuration { } } - static ShellSettings ParseSettings(string text) - { + static ShellSettings ParseSettings(string text) { var shellSettings = new ShellSettings(); if (String.IsNullOrEmpty(text)) return shellSettings; var settings = text.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); - foreach (var setting in settings) - { + foreach (var setting in settings) { var separatorIndex = setting.IndexOf(Separator); if (separatorIndex == -1) { @@ -97,8 +95,11 @@ namespace Orchard.Environment.Configuration { case "EncryptionKey": shellSettings.EncryptionKey = value; break; - case "EncryptionIV": - shellSettings.EncryptionIV = value; + case "HashAlgorithm": + shellSettings.HashAlgorithm = value; + break; + case "HashKey": + shellSettings.HashKey = value; break; } } @@ -107,12 +108,11 @@ namespace Orchard.Environment.Configuration { return shellSettings; } - static string ComposeSettings(ShellSettings settings) - { + static string ComposeSettings(ShellSettings settings) { if (settings == null) return ""; - return string.Format("Name: {0}\r\nDataProvider: {1}\r\nDataConnectionString: {2}\r\nDataPrefix: {3}\r\nRequestUrlHost: {4}\r\nRequestUrlPrefix: {5}\r\nState: {6}\r\nEncryptionAlgorithm: {7}\r\nEncryptionKey: {8}\r\nEncryptionIV: {9}\r\n", + return string.Format("Name: {0}\r\nDataProvider: {1}\r\nDataConnectionString: {2}\r\nDataPrefix: {3}\r\nRequestUrlHost: {4}\r\nRequestUrlPrefix: {5}\r\nState: {6}\r\nEncryptionAlgorithm: {7}\r\nEncryptionKey: {8}\r\nHashAlgorithm: {9}\r\nHashKey: {10}\r\n", settings.Name, settings.DataProvider, settings.DataConnectionString ?? EmptyValue, @@ -122,7 +122,8 @@ namespace Orchard.Environment.Configuration { settings.State != null ? settings.State.ToString() : String.Empty, settings.EncryptionAlgorithm ?? EmptyValue, settings.EncryptionKey ?? EmptyValue, - settings.EncryptionIV ?? EmptyValue + settings.HashAlgorithm ?? EmptyValue, + settings.HashKey ?? EmptyValue ); } } diff --git a/src/Orchard/Security/Providers/DefaultEncryptionService.cs b/src/Orchard/Security/Providers/DefaultEncryptionService.cs index 2c4b94f4a..fc8366a16 100644 --- a/src/Orchard/Security/Providers/DefaultEncryptionService.cs +++ b/src/Orchard/Security/Providers/DefaultEncryptionService.cs @@ -3,59 +3,91 @@ using System.Linq; using System.Security.Cryptography; using Orchard.Environment.Configuration; using Orchard.Utility.Extensions; +using System; namespace Orchard.Security.Providers { public class DefaultEncryptionService : IEncryptionService { private readonly ShellSettings _shellSettings; - private const int SaltSize = 16; public DefaultEncryptionService(ShellSettings shellSettings ) { _shellSettings = shellSettings; } public byte[] Decode(byte[] encodedData) { - using ( var ms = new MemoryStream() ) { - using (var algorithm = CreateAlgorithm()) { - using ( var cs = new CryptoStream(ms, algorithm.CreateDecryptor(), CryptoStreamMode.Write) ) { - cs.Write(encodedData, 0, encodedData.Length); - cs.FlushFinalBlock(); + // extract parts of the encoded data + using (var symmetricAlgorithm = CreateSymmetricAlgorithm()) { + using (var hashAlgorithm = CreateHashAlgorithm()) { + var iv = new byte[symmetricAlgorithm.BlockSize / 8]; + var signature = new byte[hashAlgorithm.HashSize / 8]; + var data = new byte[encodedData.Length - iv.Length - signature.Length]; + + Array.Copy(encodedData, 0, iv, 0, iv.Length); + Array.Copy(encodedData, iv.Length, data, 0, data.Length); + Array.Copy(encodedData, iv.Length + data.Length, signature, 0, signature.Length); + + // validate the signature + var mac = hashAlgorithm.ComputeHash(iv.Concat(data).ToArray()); + + if (!mac.SequenceEqual(signature)) { + // message has been tampered + throw new ArgumentException(); } - // remove the salt part - return ms.ToArray().Skip(SaltSize).ToArray(); + symmetricAlgorithm.IV = iv; + + using (var ms = new MemoryStream()) { + using (var cs = new CryptoStream(ms, symmetricAlgorithm.CreateDecryptor(), CryptoStreamMode.Write)) { + cs.Write(data, 0, data.Length); + cs.FlushFinalBlock(); + } + return ms.ToArray(); + } } } } public byte[] Encode(byte[] data) { - var salt = new byte[SaltSize]; + // cipherText ::= IV || ENC(EncryptionKey, IV, plainText) || HMAC(SigningKey, IV || ENC(EncryptionKey, IV, plainText)) + + byte[] encryptedData; + byte[] iv; - // generate a random salt to happend to encoded data - using ( var random = new RNGCryptoServiceProvider() ) { - random.GetBytes(salt); - } using ( var ms = new MemoryStream() ) { - using (var algorithm = CreateAlgorithm()) { - using ( var cs = new CryptoStream(ms, algorithm.CreateEncryptor(), CryptoStreamMode.Write) ) { - // append the salt to the data and encrypt - var salted = salt.Concat(data).ToArray(); - cs.Write(salted, 0, salted.Length); + using (var symmetricAlgorithm = CreateSymmetricAlgorithm()) { + // generate a new IV each time the Encode is called + symmetricAlgorithm.GenerateIV(); + iv = symmetricAlgorithm.IV; + + using (var cs = new CryptoStream(ms, symmetricAlgorithm.CreateEncryptor(), CryptoStreamMode.Write)) { + cs.Write(data, 0, data.Length); cs.FlushFinalBlock(); } - return ms.ToArray(); + encryptedData = ms.ToArray(); } } + + byte[] signedData; + + // signing IV || encrypted data + using (var hashAlgorithm = CreateHashAlgorithm()) { + signedData = hashAlgorithm.ComputeHash(iv.Concat(encryptedData).ToArray()); + } + + return iv.Concat(encryptedData).Concat(signedData).ToArray(); } - private SymmetricAlgorithm CreateAlgorithm() { - var encryptionAlgorithm = SymmetricAlgorithm.Create(_shellSettings.EncryptionAlgorithm); - - encryptionAlgorithm.Key = _shellSettings.EncryptionKey.ToByteArray(); - encryptionAlgorithm.IV = _shellSettings.EncryptionIV.ToByteArray(); - - return encryptionAlgorithm; - + private SymmetricAlgorithm CreateSymmetricAlgorithm() { + var algorithm = SymmetricAlgorithm.Create(_shellSettings.EncryptionAlgorithm); + algorithm.Key = _shellSettings.EncryptionKey.ToByteArray(); + return algorithm; } + + private HMAC CreateHashAlgorithm() { + var algorithm = HMAC.Create(_shellSettings.HashAlgorithm); + algorithm.Key = _shellSettings.HashKey.ToByteArray(); + return algorithm; + } + } }