Skip to content

Added Data Protection support for storing keys in Redis and encryptin… #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Hiarc.Core.Settings;
using Hiarc.Core.Settings.Events;
using Hiarc.Core.Settings.Storage;
using HiarcCore.Settings.KeyStore;
using static Hiarc.Configuration.Strategies.HiarcConfigurationUtility;

namespace Hiarc.Configuration.Strategies
Expand All @@ -28,9 +29,17 @@ public void GetHiarcConfiguration(Action<string, string> set)
{
PropertyNameCaseInsensitive = true,
});

Load<HiarcSettingsModel>(settings, set);
Load<HiarcDatabaseSettings>(settings.Database, set);
Load<HiarcKeyStoreSettings>(settings.KeyStore, set);
Load<KeyStoreServiceStorageSettings>(settings.KeyStore.StorageSettings, set);
if (settings.KeyStore.EncryptionSettings != null)
{
Load<KeyStoreServiceEncryptionSettings>(settings.KeyStore.EncryptionSettings, set);
ProcessKeyStoreEncryptionService(settings.KeyStore.EncryptionSettings, set);
}
ProcessKeyStoreStorageService(settings.KeyStore.StorageSettings, set);

for (int i = 0; i < settings.StorageServices.Length; i++)
{
Expand Down
46 changes: 45 additions & 1 deletion Hiarc/Configuration/Strategies/HiarcStrategyUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
using Hiarc.Core.Settings.Storage.Azure;
using Hiarc.Core.Settings.Storage.Google;
using Hiarc.Core.Storage;
using HiarcCore.Settings.KeyStore;
using HiarcCore.Settings.KeyStore.Encryption.Certificate;
using HiarcCore.Settings.KeyStore.Storage.Redis;
using Microsoft.Extensions.Configuration;

namespace Hiarc.Configuration.Strategies
Expand All @@ -28,13 +31,17 @@ public class HiarcConfigurationUtility
// public const string FORCE_HTTPS_SETTING = "ForceHTTPS";
// public const string JWT_EXPIRATION_SETTING = "JWTTokenExpirationMinutes";
public const string DATABASE_SETTING = "Database";
public const string KEYSTORE_SETTING = "KeyStore";
// public const string DATABASE_URI_SETTING = "Uri";
// public const string DATABASE_USERNAME_SETTING = "Username";
// public const string DATABASE_PASSWORD_SETTING = "Password";
public const string STORAGE_SERVICES_SETTING = "StorageServices";
public const string EVENT_SERVICES_SETTING = "EventServices";
public const string CONFIG_SETTING = "Config";
public static readonly string HIARC_DATABASE_CONFIG_KEY = $"{HIARC_SETTING}{ConfigurationPath.KeyDelimiter}{DATABASE_SETTING}";
public static readonly string HIARC_KEYSTORE_CONFIG_KEY = $"{HIARC_SETTING}{ConfigurationPath.KeyDelimiter}{KEYSTORE_SETTING}";
public static readonly string HIARC_KEYSTORE_SERVICE_STORAGE_SETTING = $"{HIARC_SETTING}{ConfigurationPath.KeyDelimiter}{KEYSTORE_SETTING}{ConfigurationPath.KeyDelimiter}StorageSettings";
public static readonly string HIARC_KEYSTORE_SERVICE_ENCRYPTION_SETTING = $"{HIARC_SETTING}{ConfigurationPath.KeyDelimiter}{KEYSTORE_SETTING}{ConfigurationPath.KeyDelimiter}EncryptionSettings";
public static readonly string HIARC_STORAGE_SERVICES_CONFIG_KEY = $"{HIARC_SETTING}{ConfigurationPath.KeyDelimiter}{STORAGE_SERVICES_SETTING}";
public static readonly string HIARC_EVENT_SERVICES_CONFIG_KEY = $"{HIARC_SETTING}{ConfigurationPath.KeyDelimiter}{EVENT_SERVICES_SETTING}";

Expand All @@ -48,6 +55,11 @@ public class HiarcConfigurationUtility
{
{typeof(HiarcSettingsModel), HIARC_SETTING},
{typeof(HiarcDatabaseSettings), HIARC_DATABASE_CONFIG_KEY},
{typeof(HiarcKeyStoreSettings), HIARC_KEYSTORE_CONFIG_KEY},
{typeof(KeyStoreServiceStorageSettings), HIARC_KEYSTORE_SERVICE_STORAGE_SETTING},
{typeof(KeyStoreServiceEncryptionSettings), HIARC_KEYSTORE_SERVICE_ENCRYPTION_SETTING},
{typeof(KeyStoreCertificate), HIARC_KEYSTORE_SERVICE_ENCRYPTION_SETTING},
{typeof(RedisSettings), HIARC_KEYSTORE_SERVICE_STORAGE_SETTING},
{typeof(StorageServiceSetting), HIARC_STORAGE_SERVICES_CONFIG_KEY},
{typeof(EventServiceSetting), HIARC_EVENT_SERVICES_CONFIG_KEY},
{typeof(S3Settings), HIARC_STORAGE_SERVICES_CONFIG_KEY},
Expand All @@ -66,7 +78,9 @@ public class HiarcConfigurationUtility
typeof(KinesisSettings),
typeof(ServiceBusSettings),
typeof(PubSubSettings),
typeof(WebhookSettings)
typeof(WebhookSettings),
typeof(RedisSettings),
typeof(KeyStoreCertificate)
};
public static IEnumerable<(string k, string v)> CreateConfigFromProperties(string prefix, object o, IEnumerable<PropertyInfo> props, int i = -1, bool withConfig = false)
{
Expand Down Expand Up @@ -111,6 +125,36 @@ public static void Load<T>(T settings, Action<string, string> set, int i = -1)
set(p.k, p.v);
}
}
public static void ProcessKeyStoreStorageService(KeyStoreServiceStorageSettings stt, Action<string, string> set)
{
if (stt.Provider.ToLower() == KeyStoreServiceStorageSettings.REDIS)
{
Load<RedisSettings>(new RedisSettings
{
ConnectionString = ((dynamic)stt.Config).ConnectionString.ToString(),
KeySuffix = string.IsNullOrEmpty(((dynamic)stt.Config).KeySuffix.ToString()) ? "" : ((dynamic)stt.Config).KeySuffix.ToString()
}, set);
}
else
{
throw new Exception($"Unsupported keystore storage service provider: {stt.Provider}");
}
}
public static void ProcessKeyStoreEncryptionService(KeyStoreServiceEncryptionSettings stt, Action<string, string> set)
{
if (stt.Provider.ToLower() == KeyStoreServiceEncryptionSettings.INLINE)
{
Load<KeyStoreCertificate>(new KeyStoreCertificate
{
EncodedCert = ((dynamic)stt.Config).EncodedCert.ToString(),
Password = string.IsNullOrEmpty(((dynamic)stt.Config).Password.ToString()) ? "" : ((dynamic)stt.Config).Password.ToString()
}, set);
}
else
{
throw new Exception($"Unsupported keystore storage service provider: {stt.Provider}");
}
}
public static void ProcessStorageServices(StorageServiceSetting stt, Action<string, string> set, int i)
{
if (stt.Provider == StorageServiceProvider.AWS_S3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class HiarcSettingsModel
public bool ForceHTTPS { get; set; }
public int JWTTokenExpirationMinutes { get; set; }
public HiarcDatabaseSettings Database { get; set; }
public HiarcKeyStoreSettings KeyStore { get; set; }
public StorageServiceSetting[] StorageServices { get; set; }
public EventServiceSetting[] EventServices { get; set; }
}
Expand Down
1 change: 1 addition & 0 deletions Hiarc/Hiarc.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="5.0.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.5.0" />
</ItemGroup>

Expand Down
79 changes: 73 additions & 6 deletions Hiarc/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
using System;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Hiarc.Configuration.Strategies.Models;
using Hiarc.Core.Database;
using Hiarc.Core.Events;
using Hiarc.Core.Settings;
using Hiarc.Core.Storage;
using HiarcCore.Settings.KeyStore;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using StackExchange.Redis;

namespace Hiarc
{
public class Startup
{
public const string HIARC_SECTION_KEY = "Hiarc";
public const string HIARC_KEYSTORE_KEY = "hiarcEncryptionKey";
public const string HIARC_JWT_SIGNING_KEY_KEY = "Hiarc:JwtSigningKey";
public const string HIARC_ADMIN_API_KEY = "Hiarc:AdminApiKey";
public const string HIARC_KEYSTORE = "Hiarc:KeyStore";
public const string HIARC_KEYSTORE_STORAGE = "Hiarc:KeyStore:StorageSettings";
public const string HIARC_KEYSTORE_ENCRYPTION = "Hiarc:KeyStore:EncryptionSettings";
public readonly static string HIARC_KEYSTORE_ENCRYPTION_PROVIDER = $"{HIARC_KEYSTORE_ENCRYPTION}:Provider";
public readonly static string HIARC_KEYSTORE_ENCRYPTION_CONFIG = $"{HIARC_KEYSTORE_ENCRYPTION}:Config";
public readonly static string HIARC_KEYSTORE_STORAGE_PROVIDER = $"{HIARC_KEYSTORE_STORAGE}:Provider";
public readonly static string HIARC_KEYSTORE_STORAGE_CONFIG = $"{HIARC_KEYSTORE_STORAGE}:Config";
public const string NEO4J_URI_KEY = "Neo4j:Uri";
public const string NEO4J_USERNAME_KEY = "Neo4j:Username";
public const string NEO4J_PASSWORD_KEY = "Neo4j:Password";
public const string NEO4J_PASSWORD_KEY = "Neo4j:Password";

public Startup(IConfiguration configuration)
{
Expand All @@ -36,7 +50,7 @@ public Startup(IConfiguration configuration)
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddJsonOptions(options =>
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.WriteIndented = true;
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
Expand All @@ -55,9 +69,9 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton<IEventServiceProvider, HiarcEventServiceProvider>();

var jwtSigningSecret = Encoding.ASCII.GetBytes(Configuration.GetValue<string>(HIARC_JWT_SIGNING_KEY_KEY));

services.AddAuthentication()
.AddApiKeySupport(options => {})
.AddApiKeySupport(options => { })
.AddJwtBearer(Auth.JWT_BEARER_SCHEME, options =>
{
options.RequireHttpsMetadata = true;
Expand All @@ -71,7 +85,7 @@ public void ConfigureServices(IServiceCollection services)
};
});

services.AddAuthorization(options =>
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
Expand All @@ -84,6 +98,59 @@ public void ConfigureServices(IServiceCollection services)
.RequireClaim(ClaimTypes.Role, Auth.ADMIN_ROLE_CLAIM_VALUE)
.Build());
});

var ksStorageProvider = Configuration.GetValue<string>(HIARC_KEYSTORE_STORAGE_PROVIDER);
var ksEncryptionProvider = Configuration.GetValue<string>(HIARC_KEYSTORE_ENCRYPTION_PROVIDER);
byte[] encryptionCert;
var encryptionCertPassword = "";
if (ksEncryptionProvider != null && ksEncryptionProvider.ToLower() == KeyStoreServiceEncryptionSettings.INLINE)
{
var encodedEncryptionCert = Configuration.GetValue<string>($"{HIARC_KEYSTORE_ENCRYPTION_CONFIG}:EncodedCert");
encryptionCertPassword = Configuration.GetValue<string>($"{HIARC_KEYSTORE_ENCRYPTION_CONFIG}:Password");
try
{
encryptionCert = Convert.FromBase64String(encodedEncryptionCert);
}
catch (FormatException)
{
encryptionCert = Encoding.UTF8.GetBytes(encodedEncryptionCert);
}
}
else
{
encryptionCert = new Byte[0];
}
if (ksStorageProvider.ToLower() == KeyStoreServiceStorageSettings.REDIS)
{
var redisConnection = Configuration.GetValue<string>($"{HIARC_KEYSTORE_STORAGE_CONFIG}:ConnectionString");
var suffix = Configuration.GetValue<string>($"{HIARC_KEYSTORE_STORAGE_CONFIG}:KeySuffix");
string redisKeySuffix = string.IsNullOrEmpty(suffix) ? "" : suffix;
var redis = ConnectionMultiplexer.Connect(redisConnection);
if (encryptionCert.Length > 0 && encryptionCertPassword != "")
{
var cert = new X509Certificate2(encryptionCert, encryptionCertPassword);
services
.AddDataProtection()
.ProtectKeysWithCertificate(cert)
.PersistKeysToStackExchangeRedis(redis, $"{HIARC_KEYSTORE_KEY}{redisKeySuffix}");

}
else if (encryptionCert.Length > 0)
{
var cert = new X509Certificate2(encryptionCert);
services
.AddDataProtection()
.ProtectKeysWithCertificate(cert)
.PersistKeysToStackExchangeRedis(redis, $"{HIARC_KEYSTORE_KEY}{redisKeySuffix}");

}
else
{
services
.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, $"{HIARC_KEYSTORE_KEY}{redisKeySuffix}");
}
}
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand All @@ -92,7 +159,7 @@ public void Configure(IApplicationBuilder app, IOptions<HiarcSettings> hiarchSet
// The default is to NOT use Https redirection as Hiarc is most commonly accessed behind the firewall.
// However, if you wish to allow direct access to Hiarc from mobile devices or Javascript you should set ForceHTTPS to true in your settings
if (!string.IsNullOrEmpty(hiarchSettings.Value.ForceHTTPS) && hiarchSettings.Value.ForceHTTPS.ToLower() == "true")
{
{
app.UseHttpsRedirection();
}

Expand Down
8 changes: 8 additions & 0 deletions HiarcCore/Settings/HiarcSettings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Hiarc.Core.Settings.Events;
using Hiarc.Core.Settings.Storage;
using HiarcCore.Settings.KeyStore;

namespace Hiarc.Core.Settings
{
Expand All @@ -11,6 +12,7 @@ public class HiarcSettings
public string ForceHTTPS { get; set; }
public int JWTTokenExpirationMinutes { get; set; }
public HiarcDatabaseSettings Database { get; set; }
public HiarcKeyStoreSettings KeyStore { get; set; }
public StorageServiceSetting[] StorageServices { get; set; }
public EventServiceSetting[] EventServices { get; set; }
}
Expand All @@ -21,4 +23,10 @@ public class HiarcDatabaseSettings
public string Username { get; set; }
public string Password { get; set; }
}

public class HiarcKeyStoreSettings
{
public KeyStoreServiceStorageSettings StorageSettings { get; set; }
public KeyStoreServiceEncryptionSettings EncryptionSettings { get; set; } = null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace HiarcCore.Settings.KeyStore.Encryption.Certificate
{
public class KeyStoreCertificate
{
public string EncodedCert { get; set; }
public string Password { get; set; } = "";
}
}
12 changes: 12 additions & 0 deletions HiarcCore/Settings/KeyStore/KeyStoreServiceEncryptionSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Dynamic;

namespace HiarcCore.Settings.KeyStore
{
public class KeyStoreServiceEncryptionSettings
{
public static readonly string INLINE = "inline";
public string Provider { get; set; }
public string Name { get; set; }
public ExpandoObject Config { get; set; }
}
}
12 changes: 12 additions & 0 deletions HiarcCore/Settings/KeyStore/KeyStoreServiceStorageSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Dynamic;

namespace HiarcCore.Settings.KeyStore
{
public class KeyStoreServiceStorageSettings
{
public static readonly string REDIS = "redis";
public string Provider { get; set; }
public string Name { get; set; }
public ExpandoObject Config { get; set; }
}
}
8 changes: 8 additions & 0 deletions HiarcCore/Settings/KeyStore/Storage/Redis/RedisSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace HiarcCore.Settings.KeyStore.Storage.Redis
{
public class RedisSettings
{
public string ConnectionString { get; set; }
public string KeySuffix { get; set; } = "";
}
}
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ Copy the string in `appsettings.txt` and replace `HIARC_SETTINGS=<base64 encoded
"Username": "<neo4j-username>",
"Password": "<neo4j-password>"
},
"KeyStore": {
"StorageSettings": {
"Provider": "redis",
"Name": "redis-store",
"Config": {
"ConnectionString": "localhost:6379",
"KeySuffix": "-finance"
}
},
"EncryptionSettings": {
"Provider": "inline",
"Name": "inline-cert",
"Config": {
"EncodedCert": "<base64 encoded .pfx certificate>",
"Password": "securepassword"
}
}
},
"StorageServices": [
{
"Provider": "AWS-S3",
Expand Down
Loading