mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-04-05 21:01:35 +08:00
Adding IEncryptionServices
Implements symetric encoding/decoding services based on a per-tenant key generated randomly during setup in the ShellSettings. Replaces MachineKey.Encode/Decode usages. Adding ComputedField to wrap get/set calls from parts, making the Smtp password encrypted in the db automatically. --HG-- branch : dev
This commit is contained in:
parent
7b4025b8cb
commit
fadcc4ef6e
@ -154,6 +154,7 @@
|
||||
<Compile Include="Themes\Services\ThemeServiceTests.cs" />
|
||||
<Compile Include="Users\Controllers\AccountControllerTests.cs" />
|
||||
<Compile Include="Users\Services\UserServiceTests.cs" />
|
||||
<Compile Include="Users\ShellSettingsUtility.cs" />
|
||||
<Compile Include="Values.cs" />
|
||||
<Compile Include="Users\Controllers\AdminControllerTests.cs" />
|
||||
<Compile Include="Users\Services\MembershipServiceTests.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<StubExtensionManager>().As<IExtensionManager>();
|
||||
builder.RegisterType<SiteSettingsPartHandler>().As<IContentHandler>();
|
||||
builder.RegisterType<RegistrationSettingsPartHandler>().As<IContentHandler>();
|
||||
|
||||
builder.RegisterInstance(new Mock<INotifier>().Object);
|
||||
builder.RegisterInstance(new Mock<IContentDisplay>().Object);
|
||||
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
|
||||
builder.RegisterType<Signals>().As<ISignals>();
|
||||
builder.RegisterInstance(new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" });
|
||||
|
||||
builder.RegisterType<DefaultEncryptionService>().As<IEncryptionService>();
|
||||
builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled());
|
||||
|
||||
_authorizer = new Mock<IAuthorizer>();
|
||||
builder.RegisterInstance(_authorizer.Object);
|
||||
|
@ -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<IContentDisplay>().Object);
|
||||
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
|
||||
builder.RegisterType<Signals>().As<ISignals>();
|
||||
builder.RegisterInstance(new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" });
|
||||
builder.RegisterType<DefaultEncryptionService>().As<IEncryptionService>();
|
||||
builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled());
|
||||
|
||||
_authorizer = new Mock<IAuthorizer>();
|
||||
builder.RegisterInstance(_authorizer.Object);
|
||||
|
@ -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<DefaultShapeFactory>().As<IShapeFactory>();
|
||||
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
|
||||
builder.RegisterType<DefaultContentDisplay>().As<IContentDisplay>();
|
||||
builder.RegisterInstance(new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" });
|
||||
|
||||
builder.RegisterType<DefaultEncryptionService>().As<IEncryptionService>();
|
||||
builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled());
|
||||
|
||||
_session = _sessionFactory.OpenSession();
|
||||
builder.RegisterInstance(new TestSessionLocator(_session)).As<ISessionLocator>();
|
||||
@ -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<ShellSettings>().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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
27
src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs
Normal file
27
src/Orchard.Tests.Modules/Users/ShellSettingsUtility.cs
Normal file
@ -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()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -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<IShellSettingsManagerEventHandler>().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<IShellSettingsManagerEventHandler>().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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,6 +245,7 @@
|
||||
<Compile Include="Mvc\Routes\ShellRouteTests.cs" />
|
||||
<Compile Include="Mvc\Routes\UrlPrefixTests.cs" />
|
||||
<Compile Include="Records\BigRecord.cs" />
|
||||
<Compile Include="Security\DefaultEncryptionServiceTests.cs" />
|
||||
<Compile Include="Stubs\InMemoryWebSiteFolder.cs" />
|
||||
<Compile Include="Stubs\StubHttpContextAccessor.cs" />
|
||||
<Compile Include="Stubs\StubWorkContextAccessor.cs" />
|
||||
|
53
src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs
Normal file
53
src/Orchard.Tests/Security/DefaultEncryptionServiceTests.cs
Normal file
@ -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<DefaultEncryptionService>().As<IEncryptionService>();
|
||||
container = builder.Build();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanEncodeAndDecodeData() {
|
||||
var encryptionService = container.Resolve<IEncryptionService>();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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<SmtpSettingsPartRecord> repository) {
|
||||
private readonly IEncryptionService _encryptionService;
|
||||
|
||||
public SmtpSettingsPartHandler(IRepository<SmtpSettingsPartRecord> repository, IEncryptionService encryptionService) {
|
||||
_encryptionService = encryptionService;
|
||||
Filters.Add(new ActivatingFilter<SmtpSettingsPart>("Site"));
|
||||
Filters.Add(StorageFilter.For(repository));
|
||||
|
||||
OnLoaded<SmtpSettingsPart>(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))));
|
||||
}
|
||||
}
|
||||
}
|
@ -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<SmtpSettingsPartRecord> {
|
||||
public bool IsValid() {
|
||||
return !String.IsNullOrWhiteSpace(Record.Host)
|
||||
&& Record.Port > 0
|
||||
&& !String.IsNullOrWhiteSpace(Record.Address);
|
||||
private readonly ComputedField<string> _password = new ComputedField<string>();
|
||||
|
||||
public ComputedField<string> 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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 })
|
||||
};
|
||||
|
@ -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;
|
||||
|
29
src/Orchard/ContentManagement/Utilities/ComputedField.cs
Normal file
29
src/Orchard/ContentManagement/Utilities/ComputedField.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace Orchard.ContentManagement.Utilities {
|
||||
public class ComputedField<T> {
|
||||
private Func<T> _getter;
|
||||
private Action<T> _setter;
|
||||
|
||||
public T Value {
|
||||
get { return GetValue(); }
|
||||
set { SetValue(value); }
|
||||
}
|
||||
|
||||
public void Getter(Func<T> loader) {
|
||||
_getter = loader;
|
||||
}
|
||||
|
||||
public void Setter(Action<T> setter) {
|
||||
_setter = setter;
|
||||
}
|
||||
|
||||
private T GetValue() {
|
||||
return _getter();
|
||||
}
|
||||
|
||||
private void SetValue(T value) {
|
||||
_setter(value);
|
||||
}
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,6 +142,7 @@
|
||||
<Compile Include="ContentManagement\DefaultContentDisplay.cs" />
|
||||
<Compile Include="ContentManagement\Drivers\ContentShapeResult.cs" />
|
||||
<Compile Include="ContentManagement\Handlers\BuildShapeContext.cs" />
|
||||
<Compile Include="ContentManagement\Utilities\ComputedField.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ResourceBindingStrategy\StylesheetBindingStrategy.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeDescriptor.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeAlteration.cs" />
|
||||
@ -171,7 +172,9 @@
|
||||
<Compile Include="Mvc\IOrchardViewPage.cs" />
|
||||
<Compile Include="Mvc\Spooling\HtmlStringWriter.cs" />
|
||||
<Compile Include="Mvc\ViewEngines\Razor\IRazorCompilationEvents.cs" />
|
||||
<Compile Include="Security\IEncryptionService.cs" />
|
||||
<Compile Include="Security\CurrentUserWorkContext.cs" />
|
||||
<Compile Include="Security\Providers\DefaultEncryptionService.cs" />
|
||||
<Compile Include="Settings\CurrentSiteWorkContext.cs" />
|
||||
<Compile Include="Settings\ResourceDebugMode.cs" />
|
||||
<Compile Include="Themes\ThemeManager.cs" />
|
||||
|
20
src/Orchard/Security/IEncryptionService.cs
Normal file
20
src/Orchard/Security/IEncryptionService.cs
Normal file
@ -0,0 +1,20 @@
|
||||
namespace Orchard.Security {
|
||||
/// <summary>
|
||||
/// Provides encryption services adapted to securing tenant level information
|
||||
/// </summary>
|
||||
public interface IEncryptionService : ISingletonDependency {
|
||||
/// <summary>
|
||||
/// Decodes data that has been encrypted.
|
||||
/// </summary>
|
||||
/// <param name="encodedData">The encrypted data to decrypt.</param>
|
||||
/// <returns>A Byte[] array that represents the decrypted data.</returns>
|
||||
byte[] Decode(byte[] encodedData);
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to encrypt.</param>
|
||||
/// <returns>The encrypted value.</returns>
|
||||
byte[] Encode(byte[] data);
|
||||
}
|
||||
}
|
62
src/Orchard/Security/Providers/DefaultEncryptionService.cs
Normal file
62
src/Orchard/Security/Providers/DefaultEncryptionService.cs
Normal file
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user