--HG--
branch : 1.x
This commit is contained in:
bertrandleroy
2011-01-13 16:08:34 -08:00
12 changed files with 168 additions and 86 deletions

View File

@@ -8,3 +8,6 @@ a6b8d094848d4efee67c787e52e5f7d358e3f0c1 0.5
083d09cd94a7c9175272fba453a156673be9db78 0.8
523f3564d2c053ff068970eab8c64eaf74e3dcec perf baseline
d2cca954cbf94750946a6325e5ec23e267430bd2 perf snapshot 1
83536b451ffe01a2d94867894f2b284cea2e68aa 1.0
83536b451ffe01a2d94867894f2b284cea2e68aa 1.0
5cc01ad95f11fcd241163685effaf01ef4b6cb02 1.0

View File

@@ -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
);
}
}

View File

@@ -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()
};
}
}
}

View File

@@ -70,7 +70,7 @@ namespace Orchard.Tests.Environment.Configuration {
[Test]
public void EncryptionSettingsAreStoredAndReadable() {
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);
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"));
}
}
}

View File

@@ -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<DefaultEncryptionService>().As<IEncryptionService>();
container = builder.Build();
_container = builder.Build();
}
[Test]
public void CanEncodeAndDecodeData() {
var encryptionService = container.Resolve<IEncryptionService>();
var encryptionService = _container.Resolve<IEncryptionService>();
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<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

@@ -79,7 +79,8 @@ namespace Orchard.Blogs.Controllers {
if (!ModelState.IsValid) {
Services.TransactionManager.Cancel();
return View(model);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
conditionallyPublish(blogPost.ContentItem);
@@ -151,7 +152,8 @@ namespace Orchard.Blogs.Controllers {
var model = Services.ContentManager.UpdateEditor(blogPost, this);
if (!ModelState.IsValid) {
Services.TransactionManager.Cancel();
return View(model);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
conditionallyPublish(blogPost.ContentItem);

View File

@@ -72,7 +72,8 @@ namespace Orchard.Experimental.Controllers {
// get at the first input?
model.Fieldsets[0][0].Attributes(new {autofocus = "autofocus"}); // <-- could be applied by some other behavior - need to be able to modify attributes instead of clobbering them like this
return View(model);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
[HttpPost, ActionName("FormShapes")]
@@ -104,7 +105,8 @@ namespace Orchard.Experimental.Controllers {
ViewBag.Page.Messages.Add(new HtmlString("<hr/>abuse<hr/>"));
ViewBag.Page.Messages.Add("<hr/>encoded<hr/>");
return View(model);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
public static string Break(dynamic view) {

View File

@@ -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 {

View File

@@ -134,7 +134,8 @@ namespace Orchard.Widgets.Controllers {
var model = Services.ContentManager.UpdateEditor(widgetPart, this);
if (!ModelState.IsValid) {
Services.TransactionManager.Cancel();
return View(model);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
Services.Notifier.Information(T("Your {0} has been created.", widgetPart.TypeDefinition.DisplayName));
@@ -179,7 +180,8 @@ namespace Orchard.Widgets.Controllers {
if (!ModelState.IsValid) {
Services.TransactionManager.Cancel();
return View(model);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
Services.Notifier.Information(T("Your {0} has been created.", layerPart.TypeDefinition.DisplayName));
@@ -226,7 +228,8 @@ namespace Orchard.Widgets.Controllers {
if (!ModelState.IsValid) {
Services.TransactionManager.Cancel();
return View(model);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
Services.Notifier.Information(T("Your {0} has been saved.", layerPart.TypeDefinition.DisplayName));
@@ -297,7 +300,8 @@ namespace Orchard.Widgets.Controllers {
var model = Services.ContentManager.UpdateEditor(widgetPart, this);
if (!ModelState.IsValid) {
Services.TransactionManager.Cancel();
return View(model);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
Services.Notifier.Information(T("Your {0} has been saved.", widgetPart.TypeDefinition.DisplayName));

View File

@@ -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; }
}

View File

@@ -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
);
}
}

View File

@@ -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;
}
}
}