diff --git a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj
index acfe2486b..8b3915057 100644
--- a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj
+++ b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj
@@ -154,6 +154,7 @@
+
diff --git a/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs
index 9e35a845a..601bbd69d 100644
--- a/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs
+++ b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Security.Cryptography;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
@@ -26,20 +27,19 @@ using Orchard.Messaging.Events;
using Orchard.Messaging.Services;
using Orchard.Security;
using Orchard.Security.Permissions;
+using Orchard.Security.Providers;
using Orchard.Tests.Stubs;
using Orchard.UI.Notify;
using Orchard.Users.Controllers;
using Orchard.Users.Handlers;
using Orchard.Users.Models;
using Orchard.Users.Services;
-using Orchard.Users.ViewModels;
using Orchard.Settings;
using Orchard.Core.Settings.Services;
using Orchard.Tests.Messaging;
using Orchard.Environment.Configuration;
using Orchard.Core.Settings.Models;
using Orchard.Core.Settings.Handlers;
-using Orchard.Messaging.Models;
using System.Collections.Specialized;
namespace Orchard.Tests.Modules.Users.Controllers {
@@ -74,11 +74,14 @@ namespace Orchard.Tests.Modules.Users.Controllers {
builder.RegisterType().As();
builder.RegisterType().As();
builder.RegisterType().As();
+
builder.RegisterInstance(new Mock().Object);
builder.RegisterInstance(new Mock().Object);
builder.RegisterType().As();
builder.RegisterType().As();
- builder.RegisterInstance(new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" });
+
+ builder.RegisterType().As();
+ builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled());
_authorizer = new Mock();
builder.RegisterInstance(_authorizer.Object);
diff --git a/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs b/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs
index 6f6d395cb..ca2f20493 100644
--- a/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs
+++ b/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs
@@ -26,6 +26,7 @@ using Orchard.Messaging.Events;
using Orchard.Messaging.Services;
using Orchard.Security;
using Orchard.Security.Permissions;
+using Orchard.Security.Providers;
using Orchard.Tests.Stubs;
using Orchard.UI.Notify;
using Orchard.Users.Controllers;
@@ -69,7 +70,8 @@ namespace Orchard.Tests.Modules.Users.Controllers {
builder.RegisterInstance(new Mock().Object);
builder.RegisterType().As();
builder.RegisterType().As();
- builder.RegisterInstance(new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" });
+ builder.RegisterType().As();
+ builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled());
_authorizer = new Mock();
builder.RegisterInstance(_authorizer.Object);
diff --git a/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs b/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs
index efa354cc8..34b572dc7 100644
--- a/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs
+++ b/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs
@@ -1,5 +1,4 @@
using System;
-using System.Web.Security;
using System.Xml.Linq;
using Autofac;
using Moq;
@@ -21,6 +20,7 @@ using Orchard.Environment.Extensions;
using Orchard.Messaging.Events;
using Orchard.Messaging.Services;
using Orchard.Security;
+using Orchard.Security.Providers;
using Orchard.Tests.Stubs;
using Orchard.Tests.Utility;
using Orchard.Users.Handlers;
@@ -96,7 +96,9 @@ namespace Orchard.Tests.Modules.Users.Services {
builder.RegisterType().As();
builder.RegisterType().As();
builder.RegisterType().As();
- builder.RegisterInstance(new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" });
+
+ builder.RegisterType().As();
+ builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled());
_session = _sessionFactory.OpenSession();
builder.RegisterInstance(new TestSessionLocator(_session)).As();
@@ -121,25 +123,5 @@ namespace Orchard.Tests.Modules.Users.Services {
Assert.That(username, Is.EqualTo("foo"));
Assert.That(validateByUtc, Is.GreaterThan(_clock.UtcNow));
}
-
- [Test]
- public void NonceShouldNotBeUsedOnAnotherTenant() {
- var user = _membershipService.CreateUser(new CreateUserParams("foo", "66554321", "foo@bar.com", "", "", true));
- var nonce = _userService.CreateNonce(user, new TimeSpan(1, 0, 0));
-
- Assert.That(nonce, Is.Not.Empty);
-
- string username;
- DateTime validateByUtc;
-
- _container.Resolve().Name = "Beta";
-
- var result = _userService.DecryptNonce(nonce, out username, out validateByUtc);
-
- Assert.That(result, Is.False);
- Assert.That(username, Is.EqualTo("foo"));
- Assert.That(validateByUtc, Is.GreaterThan(_clock.UtcNow));
- }
-
}
}
diff --git a/src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs b/src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs
new file mode 100644
index 000000000..ff2a020be
--- /dev/null
+++ b/src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Security.Cryptography;
+using Orchard.Environment.Configuration;
+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);
+ }
+
+ return new ShellSettings {
+ Name = "Alpha",
+ RequestUrlHost = "wiki.example.com",
+ RequestUrlPrefix = "~/foo",
+ EncryptionAlgorithm = "AES",
+ EncryptionKey = key.ToHexString(),
+ EncryptionIV = iv.ToHexString()
+ };
+ }
+ }
+}
diff --git a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs
index e50dd1567..6f1d02806 100644
--- a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs
+++ b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs
@@ -58,7 +58,7 @@ namespace Orchard.Tests.Environment.Configuration {
_appDataFolder.CreateFile("Sites\\Default\\Settings.txt", "Name: Default\r\nDataProvider: SqlCe\r\nDataConnectionString: something else");
IShellSettingsManager loader = new ShellSettingsManager(_appDataFolder, new Mock().Object);
- var foo = new ShellSettings { Name = "Foo", DataProvider = "Bar", DataConnectionString = "Quux" };
+ var foo = new ShellSettings {Name = "Foo", DataProvider = "Bar", DataConnectionString = "Quux"};
Assert.That(loader.LoadSettings().Count(), Is.EqualTo(1));
loader.SaveSettings(foo);
@@ -69,5 +69,19 @@ namespace Orchard.Tests.Environment.Configuration {
Assert.That(text, Is.StringContaining("Bar"));
Assert.That(text, Is.StringContaining("Quux"));
}
+
+ [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" };
+ loader.SaveSettings(foo);
+ Assert.That(loader.LoadSettings().Count(), Is.EqualTo(1));
+
+ var settings = loader.LoadSettings().First();
+
+ Assert.That(settings.EncryptionAlgorithm, Is.EqualTo("AES"));
+ Assert.That(settings.EncryptionKey, Is.EqualTo("ABCDEFG"));
+ Assert.That(settings.EncryptionIV, Is.EqualTo("HIJKL"));
+ }
}
}
diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj
index c1537c8af..0fcaf7102 100644
--- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj
+++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj
@@ -245,6 +245,7 @@
+
diff --git a/src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs b/src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs
new file mode 100644
index 000000000..37a9fbc45
--- /dev/null
+++ b/src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs
@@ -0,0 +1,53 @@
+using System.Security.Cryptography;
+using System.Text;
+using Autofac;
+using NUnit.Framework;
+using Orchard.Environment.Configuration;
+using Orchard.Security;
+using Orchard.Security.Providers;
+using Orchard.Utility.Extensions;
+
+namespace Orchard.Tests.Security {
+ [TestFixture]
+ public class DefaultEncryptionServiceTests {
+ 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);
+ }
+
+ var shellSettings = new ShellSettings {
+ Name = "Foo",
+ DataProvider = "Bar",
+ DataConnectionString = "Quux",
+ EncryptionAlgorithm = "AES",
+ EncryptionKey = key.ToHexString(),
+ EncryptionIV = iv.ToHexString()
+ };
+
+ var builder = new ContainerBuilder();
+ builder.RegisterInstance(shellSettings);
+ builder.RegisterType().As();
+ container = builder.Build();
+ }
+
+ [Test]
+ public void CanEncodeAndDecodeData() {
+ var encryptionService = container.Resolve();
+
+ var secretData = Encoding.Unicode.GetBytes("this is secret data");
+ var encrypted = encryptionService.Encode(secretData);
+ var decrypted = encryptionService.Decode(encrypted);
+
+ Assert.That(encrypted, Is.Not.EqualTo(decrypted));
+ Assert.That(decrypted, Is.EqualTo(secretData));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs
index d00d99d7b..6fdb834a1 100644
--- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs
+++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs
@@ -6,10 +6,8 @@ using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Models;
-using Orchard.ContentTypes.Extensions;
using Orchard.ContentTypes.ViewModels;
using Orchard.Core.Contents.Extensions;
-using Orchard.Core.Contents.Settings;
using Orchard.Localization;
namespace Orchard.ContentTypes.Services {
diff --git a/src/Orchard.Web/Modules/Orchard.Email/Handlers/SmtpSettingsPartHandler.cs b/src/Orchard.Web/Modules/Orchard.Email/Handlers/SmtpSettingsPartHandler.cs
index dda95b909..c7a2e1e20 100644
--- a/src/Orchard.Web/Modules/Orchard.Email/Handlers/SmtpSettingsPartHandler.cs
+++ b/src/Orchard.Web/Modules/Orchard.Email/Handlers/SmtpSettingsPartHandler.cs
@@ -1,14 +1,27 @@
-using JetBrains.Annotations;
+using System;
+using System.Text;
+using JetBrains.Annotations;
using Orchard.Email.Models;
using Orchard.Data;
using Orchard.ContentManagement.Handlers;
+using Orchard.Security;
namespace Orchard.Email.Handlers {
[UsedImplicitly]
public class SmtpSettingsPartHandler : ContentHandler {
- public SmtpSettingsPartHandler(IRepository repository) {
+ private readonly IEncryptionService _encryptionService;
+
+ public SmtpSettingsPartHandler(IRepository repository, IEncryptionService encryptionService) {
+ _encryptionService = encryptionService;
Filters.Add(new ActivatingFilter("Site"));
Filters.Add(StorageFilter.For(repository));
+
+ OnLoaded(LazyLoadHandlers);
+ }
+
+ void LazyLoadHandlers(LoadContentContext context, SmtpSettingsPart part) {
+ part.PasswordField.Getter(() => String.IsNullOrWhiteSpace(part.Record.Password) ? String.Empty : Encoding.UTF8.GetString(_encryptionService.Decode(Convert.FromBase64String(part.Record.Password))));
+ part.PasswordField.Setter(value => part.Record.Password = String.IsNullOrWhiteSpace(value) ? String.Empty : Convert.ToBase64String(_encryptionService.Encode(Encoding.UTF8.GetBytes(value))));
}
}
}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.Email/Models/SmtpSettingsPart.cs b/src/Orchard.Web/Modules/Orchard.Email/Models/SmtpSettingsPart.cs
index 964d022df..e371459b4 100644
--- a/src/Orchard.Web/Modules/Orchard.Email/Models/SmtpSettingsPart.cs
+++ b/src/Orchard.Web/Modules/Orchard.Email/Models/SmtpSettingsPart.cs
@@ -1,14 +1,13 @@
-using System.Text;
-using System.Web.Security;
-using Orchard.ContentManagement;
+using Orchard.ContentManagement;
using System;
+using Orchard.ContentManagement.Utilities;
namespace Orchard.Email.Models {
public class SmtpSettingsPart : ContentPart {
- public bool IsValid() {
- return !String.IsNullOrWhiteSpace(Record.Host)
- && Record.Port > 0
- && !String.IsNullOrWhiteSpace(Record.Address);
+ private readonly ComputedField _password = new ComputedField();
+
+ public ComputedField PasswordField {
+ get { return _password; }
}
public string Address {
@@ -42,8 +41,14 @@ namespace Orchard.Email.Models {
}
public string Password {
- get { return String.IsNullOrWhiteSpace(Record.Password) ? String.Empty : Encoding.UTF8.GetString(MachineKey.Decode(Record.Password, MachineKeyProtection.All)); ; }
- set { Record.Password = String.IsNullOrWhiteSpace(value) ? String.Empty : MachineKey.Encode(Encoding.UTF8.GetBytes(value), MachineKeyProtection.All); }
+ get { return _password.Value; }
+ set { _password.Value = value; }
+ }
+
+ public bool IsValid() {
+ return !String.IsNullOrWhiteSpace(Record.Host)
+ && Record.Port > 0
+ && !String.IsNullOrWhiteSpace(Record.Address);
}
}
}
\ 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 4c68417fc..2d26c4bbb 100644
--- a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs
+++ b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs
@@ -1,6 +1,7 @@
using System;
using System.Globalization;
using System.Linq;
+using System.Security.Cryptography;
using System.Web;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
@@ -29,6 +30,7 @@ using Orchard.Settings;
using Orchard.Environment.State;
using Orchard.Data.Migration;
using Orchard.Themes.Services;
+using Orchard.Utility.Extensions;
using Orchard.Widgets.Models;
using Orchard.Widgets;
@@ -118,6 +120,21 @@ namespace Orchard.Setup.Services {
shellSettings.DataTablePrefix = context.DatabaseTablePrefix;
}
+ #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();
+ #endregion
+
var shellDescriptor = new ShellDescriptor {
Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name })
};
diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs
index a1ed2f3fc..712936452 100644
--- a/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs
+++ b/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs
@@ -25,13 +25,15 @@ namespace Orchard.Users.Services {
private readonly IClock _clock;
private readonly IMessageManager _messageManager;
private readonly ShellSettings _shellSettings;
+ private readonly IEncryptionService _encryptionService;
- public UserService(IContentManager contentManager, IMembershipService membershipService, IClock clock, IMessageManager messageManager, ShellSettings shellSettings) {
+ public UserService(IContentManager contentManager, IMembershipService membershipService, IClock clock, IMessageManager messageManager, ShellSettings shellSettings, IEncryptionService encryptionService) {
_contentManager = contentManager;
_membershipService = membershipService;
_clock = clock;
_messageManager = messageManager;
_shellSettings = shellSettings;
+ _encryptionService = encryptionService;
Logger = NullLogger.Instance;
}
@@ -66,24 +68,22 @@ namespace Orchard.Users.Services {
}
public string CreateNonce(IUser user, TimeSpan delay) {
- // the tenant's name is added to the token to prevent cross-tenant requests
- var challengeToken = new XElement("n", new XAttribute("s", _shellSettings.Name), new XAttribute("un", user.UserName), new XAttribute("utc", _clock.UtcNow.ToUniversalTime().Add(delay).ToString(CultureInfo.InvariantCulture))).ToString();
- var data = Encoding.Unicode.GetBytes(challengeToken);
- return MachineKey.Encode(data, MachineKeyProtection.All);
+ var challengeToken = new XElement("n", new XAttribute("un", user.UserName), new XAttribute("utc", _clock.UtcNow.ToUniversalTime().Add(delay).ToString(CultureInfo.InvariantCulture))).ToString();
+ var data = Encoding.UTF8.GetBytes(challengeToken);
+ return Convert.ToBase64String(_encryptionService.Encode(data));
}
- public bool DecryptNonce(string challengeToken, out string username, out DateTime validateByUtc) {
+ public bool DecryptNonce(string nonce, out string username, out DateTime validateByUtc) {
username = null;
validateByUtc = _clock.UtcNow;
try {
- var data = MachineKey.Decode(challengeToken, MachineKeyProtection.All);
- var xml = Encoding.Unicode.GetString(data);
+ var data = _encryptionService.Decode(Convert.FromBase64String(nonce));
+ var xml = Encoding.UTF8.GetString(data);
var element = XElement.Parse(xml);
- var tenant = element.Attribute("s").Value;
username = element.Attribute("un").Value;
validateByUtc = DateTime.Parse(element.Attribute("utc").Value, CultureInfo.InvariantCulture);
- return String.Equals(_shellSettings.Name, tenant, StringComparison.Ordinal) && _clock.UtcNow <= validateByUtc;
+ return _clock.UtcNow <= validateByUtc;
}
catch {
return false;
diff --git a/src/Orchard/ContentManagement/Utilities/ComputedField.cs b/src/Orchard/ContentManagement/Utilities/ComputedField.cs
new file mode 100644
index 000000000..3bbd33b7a
--- /dev/null
+++ b/src/Orchard/ContentManagement/Utilities/ComputedField.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Orchard.ContentManagement.Utilities {
+ public class ComputedField {
+ private Func _getter;
+ private Action _setter;
+
+ public T Value {
+ get { return GetValue(); }
+ set { SetValue(value); }
+ }
+
+ public void Getter(Func loader) {
+ _getter = loader;
+ }
+
+ public void Setter(Action setter) {
+ _setter = setter;
+ }
+
+ private T GetValue() {
+ return _getter();
+ }
+
+ private void SetValue(T value) {
+ _setter(value);
+ }
+ }
+}
diff --git a/src/Orchard/Environment/Configuration/ShellSettings.cs b/src/Orchard/Environment/Configuration/ShellSettings.cs
index c6717938f..65795783f 100644
--- a/src/Orchard/Environment/Configuration/ShellSettings.cs
+++ b/src/Orchard/Environment/Configuration/ShellSettings.cs
@@ -16,6 +16,9 @@
DataTablePrefix = settings.DataTablePrefix;
RequestUrlHost = settings.RequestUrlHost;
RequestUrlPrefix = settings.RequestUrlPrefix;
+ EncryptionAlgorithm = settings.EncryptionAlgorithm;
+ EncryptionKey = settings.EncryptionKey;
+ EncryptionIV = settings.EncryptionIV;
State = settings.State;
}
@@ -28,6 +31,10 @@
public string RequestUrlHost { get; set; }
public string RequestUrlPrefix { get; set; }
+ public string EncryptionAlgorithm { get; set; }
+ public string EncryptionKey { get; set; }
+ public string EncryptionIV { get; set; }
+
public TenantState State { get; set; }
}
}
diff --git a/src/Orchard/Environment/Configuration/ShellSettingsManager.cs b/src/Orchard/Environment/Configuration/ShellSettingsManager.cs
index 9e819b36e..6e6d24044 100644
--- a/src/Orchard/Environment/Configuration/ShellSettingsManager.cs
+++ b/src/Orchard/Environment/Configuration/ShellSettingsManager.cs
@@ -84,6 +84,15 @@ namespace Orchard.Environment.Configuration {
case "RequestUrlPrefix":
shellSettings.RequestUrlPrefix = settingFields[1];
break;
+ case "EncryptionAlgorithm":
+ shellSettings.EncryptionAlgorithm = settingFields[1];
+ break;
+ case "EncryptionKey":
+ shellSettings.EncryptionKey = settingFields[1];
+ break;
+ case "EncryptionIV":
+ shellSettings.EncryptionIV = settingFields[1];
+ break;
}
}
}
@@ -94,14 +103,18 @@ namespace Orchard.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\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\nEncryptionIV: {9}\r\n",
settings.Name,
settings.DataProvider,
settings.DataConnectionString ?? "null",
settings.DataTablePrefix ?? "null",
settings.RequestUrlHost ?? "null",
settings.RequestUrlPrefix ?? "null",
- settings.State != null ? settings.State.ToString() : String.Empty);
+ settings.State != null ? settings.State.ToString() : String.Empty,
+ settings.EncryptionAlgorithm ?? "null",
+ settings.EncryptionKey ?? "null",
+ settings.EncryptionIV ?? "null"
+ );
}
}
}
diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj
index d89393040..a8350ed09 100644
--- a/src/Orchard/Orchard.Framework.csproj
+++ b/src/Orchard/Orchard.Framework.csproj
@@ -142,6 +142,7 @@
+
@@ -171,7 +172,9 @@
+
+
diff --git a/src/Orchard/Security/IEncryptionService.cs b/src/Orchard/Security/IEncryptionService.cs
new file mode 100644
index 000000000..e2d0471ce
--- /dev/null
+++ b/src/Orchard/Security/IEncryptionService.cs
@@ -0,0 +1,20 @@
+namespace Orchard.Security {
+ ///
+ /// Provides encryption services adapted to securing tenant level information
+ ///
+ public interface IEncryptionService : ISingletonDependency {
+ ///
+ /// Decodes data that has been encrypted.
+ ///
+ /// The encrypted data to decrypt.
+ /// A Byte[] array that represents the decrypted data.
+ byte[] Decode(byte[] encodedData);
+
+ ///
+ /// Encrypts data.
+ ///
+ /// The data to encrypt.
+ /// The encrypted value.
+ byte[] Encode(byte[] data);
+ }
+}
diff --git a/src/Orchard/Security/Providers/DefaultEncryptionService.cs b/src/Orchard/Security/Providers/DefaultEncryptionService.cs
new file mode 100644
index 000000000..9620f4157
--- /dev/null
+++ b/src/Orchard/Security/Providers/DefaultEncryptionService.cs
@@ -0,0 +1,62 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using Orchard.Environment.Configuration;
+using Orchard.Utility.Extensions;
+
+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();
+ }
+
+ // remove the salt part
+ return ms.ToArray().Skip(SaltSize).ToArray();
+ }
+ }
+ }
+
+ public byte[] Encode(byte[] data) {
+ var salt = new byte[SaltSize];
+
+ // 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);
+ cs.FlushFinalBlock();
+ }
+
+ return ms.ToArray();
+ }
+ }
+ }
+
+ private SymmetricAlgorithm CreateAlgorithm() {
+ var encryptionAlgorithm = SymmetricAlgorithm.Create(_shellSettings.EncryptionAlgorithm);
+
+ encryptionAlgorithm.Key = _shellSettings.EncryptionKey.ToByteArray();
+ encryptionAlgorithm.IV = _shellSettings.EncryptionIV.ToByteArray();
+
+ return encryptionAlgorithm;
+
+ }
+ }
+}
diff --git a/src/Orchard/Utility/Extensions/StringExtensions.cs b/src/Orchard/Utility/Extensions/StringExtensions.cs
index bc1225b1e..3a11a1c65 100644
--- a/src/Orchard/Utility/Extensions/StringExtensions.cs
+++ b/src/Orchard/Utility/Extensions/StringExtensions.cs
@@ -1,4 +1,5 @@
-using System.Linq;
+using System;
+using System.Linq;
using System.Text.RegularExpressions;
using Orchard.Localization;
@@ -55,5 +56,16 @@ namespace Orchard.Utility.Extensions {
? ""
: Regex.Replace(text, @"(\r?\n)", replacement, RegexOptions.Singleline);
}
+
+ public static string ToHexString(this byte[] bytes) {
+ return BitConverter.ToString(bytes).Replace("-", "");
+ }
+
+ public static byte[] ToByteArray(this string hex) {
+ return Enumerable.Range(0, hex.Length).
+ Where(x => 0 == x % 2).
+ Select(x => Convert.ToByte(hex.Substring(x, 2), 16)).
+ ToArray();
+ }
}
}
\ No newline at end of file