--HG--
branch : 1.x
This commit is contained in:
Andre Rodrigues
2011-01-12 19:00:27 -08:00
9 changed files with 171 additions and 80 deletions

View File

@@ -99,8 +99,11 @@ namespace Orchard.Azure.Environment.Configuration {
case "EncryptionKey": case "EncryptionKey":
shellSettings.EncryptionKey = value; shellSettings.EncryptionKey = value;
break; break;
case "EncryptionIV": case "HashAlgorithm":
shellSettings.EncryptionIV = value; shellSettings.HashAlgorithm = value;
break;
case "HashKey":
shellSettings.HashKey = value;
break; break;
} }
} }
@@ -113,7 +116,7 @@ namespace Orchard.Azure.Environment.Configuration {
if (settings == null) if (settings == null)
return ""; 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.Name,
settings.DataProvider, settings.DataProvider,
settings.DataConnectionString ?? EmptyValue, settings.DataConnectionString ?? EmptyValue,
@@ -123,7 +126,8 @@ namespace Orchard.Azure.Environment.Configuration {
settings.State != null ? settings.State.ToString() : String.Empty, settings.State != null ? settings.State.ToString() : String.Empty,
settings.EncryptionAlgorithm ?? EmptyValue, settings.EncryptionAlgorithm ?? EmptyValue,
settings.EncryptionKey ?? EmptyValue, settings.EncryptionKey ?? EmptyValue,
settings.EncryptionIV ?? EmptyValue settings.HashAlgorithm ?? EmptyValue,
settings.HashKey ?? EmptyValue
); );
} }
} }

View File

@@ -5,22 +5,20 @@ using Orchard.Utility.Extensions;
namespace Orchard.Tests.Modules.Users { namespace Orchard.Tests.Modules.Users {
public class ShellSettingsUtility { public class ShellSettingsUtility {
public static ShellSettings CreateEncryptionEnabled() { public static ShellSettings CreateEncryptionEnabled() {
// generate random keys for encryption
var key = new byte[32]; const string encryptionAlgorithm = "AES";
var iv = new byte[16]; const string hashAlgorithm = "HMACSHA256";
using ( var random = new RNGCryptoServiceProvider() ) {
random.GetBytes(key);
random.GetBytes(iv);
}
return new ShellSettings { return new ShellSettings {
Name = "Alpha", Name = "Alpha",
RequestUrlHost = "wiki.example.com", RequestUrlHost = "wiki.example.com",
RequestUrlPrefix = "~/foo", RequestUrlPrefix = "~/foo",
EncryptionAlgorithm = "AES", EncryptionAlgorithm = encryptionAlgorithm,
EncryptionKey = key.ToHexString(), EncryptionKey = SymmetricAlgorithm.Create(encryptionAlgorithm).Key.ToHexString(),
EncryptionIV = iv.ToHexString() HashAlgorithm = hashAlgorithm,
HashKey = HMAC.Create(hashAlgorithm).Key.ToHexString()
}; };
} }
} }
} }

View File

@@ -70,7 +70,7 @@ namespace Orchard.Tests.Environment.Configuration {
[Test] [Test]
public void EncryptionSettingsAreStoredAndReadable() { public void EncryptionSettingsAreStoredAndReadable() {
IShellSettingsManager loader = new ShellSettingsManager(_appDataFolder, new Mock<IShellSettingsManagerEventHandler>().Object); IShellSettingsManager loader = new ShellSettingsManager(_appDataFolder, new Mock<IShellSettingsManagerEventHandler>().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); loader.SaveSettings(foo);
Assert.That(loader.LoadSettings().Count(), Is.EqualTo(1)); 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.EncryptionAlgorithm, Is.EqualTo("AES"));
Assert.That(settings.EncryptionKey, Is.EqualTo("ABCDEFG")); 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"));
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using System.Security.Cryptography; using System;
using System.Security.Cryptography;
using System.Text; using System.Text;
using Autofac; using Autofac;
using NUnit.Framework; using NUnit.Framework;
@@ -10,36 +11,33 @@ using Orchard.Utility.Extensions;
namespace Orchard.Tests.Security { namespace Orchard.Tests.Security {
[TestFixture] [TestFixture]
public class DefaultEncryptionServiceTests { public class DefaultEncryptionServiceTests {
private IContainer container; private IContainer _container;
[SetUp] [SetUp]
public void Init() { public void Init() {
var key = new byte[32]; const string encryptionAlgorithm = "AES";
var iv = new byte[16]; const string hashAlgorithm = "HMACSHA256";
using ( var random = new RNGCryptoServiceProvider() ) {
random.GetBytes(key);
random.GetBytes(iv);
}
var shellSettings = new ShellSettings { var shellSettings = new ShellSettings {
Name = "Foo", Name = "Foo",
DataProvider = "Bar", DataProvider = "Bar",
DataConnectionString = "Quux", DataConnectionString = "Quux",
EncryptionAlgorithm = "AES", EncryptionAlgorithm = encryptionAlgorithm,
EncryptionKey = key.ToHexString(), EncryptionKey = SymmetricAlgorithm.Create(encryptionAlgorithm).Key.ToHexString(),
EncryptionIV = iv.ToHexString() HashAlgorithm = hashAlgorithm,
HashKey = HMAC.Create(hashAlgorithm).Key.ToHexString()
}; };
var builder = new ContainerBuilder(); var builder = new ContainerBuilder();
builder.RegisterInstance(shellSettings); builder.RegisterInstance(shellSettings);
builder.RegisterType<DefaultEncryptionService>().As<IEncryptionService>(); builder.RegisterType<DefaultEncryptionService>().As<IEncryptionService>();
container = builder.Build(); _container = builder.Build();
} }
[Test] [Test]
public void CanEncodeAndDecodeData() { public void CanEncodeAndDecodeData() {
var encryptionService = container.Resolve<IEncryptionService>(); var encryptionService = _container.Resolve<IEncryptionService>();
var secretData = Encoding.Unicode.GetBytes("this is secret data"); var secretData = Encoding.Unicode.GetBytes("this is secret data");
var encrypted = encryptionService.Encode(secretData); var encrypted = encryptionService.Encode(secretData);
@@ -49,5 +47,42 @@ namespace Orchard.Tests.Security {
Assert.That(decrypted, Is.EqualTo(secretData)); Assert.That(decrypted, Is.EqualTo(secretData));
} }
[Test]
public void ShouldDetectTamperedData() {
var encryptionService = _container.Resolve<IEncryptionService>();
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<IEncryptionService>();
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;
}
}
} }
} }

View File

@@ -126,17 +126,14 @@ namespace Orchard.Setup.Services {
#region Encryption Settings #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.EncryptionAlgorithm = "AES";
shellSettings.EncryptionKey = key.ToHexString(); // randomly generated key
shellSettings.EncryptionIV = iv.ToHexString(); shellSettings.EncryptionKey = SymmetricAlgorithm.Create(shellSettings.EncryptionAlgorithm).Key.ToHexString();
shellSettings.HashAlgorithm = "HMACSHA256";
// randomly generated key
shellSettings.HashKey = HMAC.Create(shellSettings.HashAlgorithm).Key.ToHexString();
#endregion #endregion
var shellDescriptor = new ShellDescriptor { var shellDescriptor = new ShellDescriptor {

View File

@@ -104,6 +104,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Scripting", "Orchar
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{3E10BF6D-ADA5-417D-B36C-EBB0660B475E}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{3E10BF6D-ADA5-417D-B36C-EBB0660B475E}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
CodeCoverage|Any CPU = CodeCoverage|Any CPU CodeCoverage|Any CPU = CodeCoverage|Any CPU
@@ -113,6 +117,10 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution 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.ActiveCfg = Release|Any CPU
{50B779EA-EC00-4699-84C0-03B395C365D2}.CodeCoverage|Any CPU.Build.0 = 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 {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}.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.ActiveCfg = Release|Any CPU
{99002B65-86F7-415E-BF4A-381AA8AB9CCC}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -585,6 +603,7 @@ Global
{FBC8B571-ED50-49D8-8D9D-64AB7454A0D6} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {FBC8B571-ED50-49D8-8D9D-64AB7454A0D6} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
{2AD6973D-C7BB-416E-89FE-EEE34664E05F} = {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} {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} {ABC826D4-2FA1-4F2F-87DE-E6095F653810} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
{F112851D-B023-4746-B6B1-8D2E5AD8F7AA} = {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} {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} {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56} = {383DBA32-4A3E-48D1-AAC3-75377A694452}
{33B1BC8D-E292-4972-A363-22056B207156} = {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} {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} {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 EndGlobalSection
EndGlobal EndGlobal

View File

@@ -18,7 +18,8 @@
RequestUrlPrefix = settings.RequestUrlPrefix; RequestUrlPrefix = settings.RequestUrlPrefix;
EncryptionAlgorithm = settings.EncryptionAlgorithm; EncryptionAlgorithm = settings.EncryptionAlgorithm;
EncryptionKey = settings.EncryptionKey; EncryptionKey = settings.EncryptionKey;
EncryptionIV = settings.EncryptionIV; HashAlgorithm = settings.HashAlgorithm;
HashKey = settings.HashKey;
State = settings.State; State = settings.State;
} }
@@ -33,7 +34,9 @@
public string EncryptionAlgorithm { get; set; } public string EncryptionAlgorithm { get; set; }
public string EncryptionKey { 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; } public TenantState State { get; set; }
} }

View File

@@ -49,15 +49,13 @@ namespace Orchard.Environment.Configuration {
} }
} }
static ShellSettings ParseSettings(string text) static ShellSettings ParseSettings(string text) {
{
var shellSettings = new ShellSettings(); var shellSettings = new ShellSettings();
if (String.IsNullOrEmpty(text)) if (String.IsNullOrEmpty(text))
return shellSettings; return shellSettings;
var settings = text.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); var settings = text.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
foreach (var setting in settings) foreach (var setting in settings) {
{
var separatorIndex = setting.IndexOf(Separator); var separatorIndex = setting.IndexOf(Separator);
if (separatorIndex == -1) if (separatorIndex == -1)
{ {
@@ -97,8 +95,11 @@ namespace Orchard.Environment.Configuration {
case "EncryptionKey": case "EncryptionKey":
shellSettings.EncryptionKey = value; shellSettings.EncryptionKey = value;
break; break;
case "EncryptionIV": case "HashAlgorithm":
shellSettings.EncryptionIV = value; shellSettings.HashAlgorithm = value;
break;
case "HashKey":
shellSettings.HashKey = value;
break; break;
} }
} }
@@ -107,12 +108,11 @@ namespace Orchard.Environment.Configuration {
return shellSettings; return shellSettings;
} }
static string ComposeSettings(ShellSettings settings) static string ComposeSettings(ShellSettings settings) {
{
if (settings == null) if (settings == null)
return ""; 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.Name,
settings.DataProvider, settings.DataProvider,
settings.DataConnectionString ?? EmptyValue, settings.DataConnectionString ?? EmptyValue,
@@ -122,7 +122,8 @@ namespace Orchard.Environment.Configuration {
settings.State != null ? settings.State.ToString() : String.Empty, settings.State != null ? settings.State.ToString() : String.Empty,
settings.EncryptionAlgorithm ?? EmptyValue, settings.EncryptionAlgorithm ?? EmptyValue,
settings.EncryptionKey ?? EmptyValue, settings.EncryptionKey ?? EmptyValue,
settings.EncryptionIV ?? EmptyValue settings.HashAlgorithm ?? EmptyValue,
settings.HashKey ?? EmptyValue
); );
} }
} }

View File

@@ -3,59 +3,91 @@ using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using Orchard.Environment.Configuration; using Orchard.Environment.Configuration;
using Orchard.Utility.Extensions; using Orchard.Utility.Extensions;
using System;
namespace Orchard.Security.Providers { namespace Orchard.Security.Providers {
public class DefaultEncryptionService : IEncryptionService { public class DefaultEncryptionService : IEncryptionService {
private readonly ShellSettings _shellSettings; private readonly ShellSettings _shellSettings;
private const int SaltSize = 16;
public DefaultEncryptionService(ShellSettings shellSettings ) { public DefaultEncryptionService(ShellSettings shellSettings ) {
_shellSettings = shellSettings; _shellSettings = shellSettings;
} }
public byte[] Decode(byte[] encodedData) { public byte[] Decode(byte[] encodedData) {
using ( var ms = new MemoryStream() ) { // extract parts of the encoded data
using (var algorithm = CreateAlgorithm()) { using (var symmetricAlgorithm = CreateSymmetricAlgorithm()) {
using ( var cs = new CryptoStream(ms, algorithm.CreateDecryptor(), CryptoStreamMode.Write) ) { using (var hashAlgorithm = CreateHashAlgorithm()) {
cs.Write(encodedData, 0, encodedData.Length); var iv = new byte[symmetricAlgorithm.BlockSize / 8];
cs.FlushFinalBlock(); 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 symmetricAlgorithm.IV = iv;
return ms.ToArray().Skip(SaltSize).ToArray();
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) { 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 ms = new MemoryStream() ) {
using (var algorithm = CreateAlgorithm()) { using (var symmetricAlgorithm = CreateSymmetricAlgorithm()) {
using ( var cs = new CryptoStream(ms, algorithm.CreateEncryptor(), CryptoStreamMode.Write) ) { // generate a new IV each time the Encode is called
// append the salt to the data and encrypt symmetricAlgorithm.GenerateIV();
var salted = salt.Concat(data).ToArray(); iv = symmetricAlgorithm.IV;
cs.Write(salted, 0, salted.Length);
using (var cs = new CryptoStream(ms, symmetricAlgorithm.CreateEncryptor(), CryptoStreamMode.Write)) {
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock(); 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() { private SymmetricAlgorithm CreateSymmetricAlgorithm() {
var encryptionAlgorithm = SymmetricAlgorithm.Create(_shellSettings.EncryptionAlgorithm); var algorithm = SymmetricAlgorithm.Create(_shellSettings.EncryptionAlgorithm);
algorithm.Key = _shellSettings.EncryptionKey.ToByteArray();
encryptionAlgorithm.Key = _shellSettings.EncryptionKey.ToByteArray(); return algorithm;
encryptionAlgorithm.IV = _shellSettings.EncryptionIV.ToByteArray();
return encryptionAlgorithm;
} }
private HMAC CreateHashAlgorithm() {
var algorithm = HMAC.Create(_shellSettings.HashAlgorithm);
algorithm.Key = _shellSettings.HashKey.ToByteArray();
return algorithm;
}
} }
} }