Skip to content

Commit

Permalink
Removed default secrets for the string encryption service. (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
mgernand authored Nov 15, 2022
1 parent 9eef178 commit ec2ee67
Show file tree
Hide file tree
Showing 32 changed files with 254 additions and 102 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<PropertyGroup>
Expand All @@ -22,7 +22,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Fluxera.Guards" Version="7.0.2" />
<PackageReference Include="Fluxera.Guards" Version="7.0.3" />
<PackageReference Include="GitVersion.MsBuild" Version="5.11.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace Fluxera.Extensions.Common
{
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;

/// <summary>
Expand All @@ -17,6 +19,16 @@ public interface IStringEncryptionService
/// <returns>Encrypted text</returns>
string Encrypt(string plainText, string passPhrase = null, byte[] salt = null);

/// <summary>
/// Encrypts a text.
/// </summary>
/// <param name="plainText">The text in plain format</param>
/// <param name="passPhrase">A phrase to use as the encryption key (optional, uses default if not provided)</param>
/// <param name="salt">Salt value (optional, uses default if not provided)</param>
/// <param name="cancellationToken"></param>
/// <returns>Encrypted text</returns>
Task<string> EncryptAsync(string plainText, string passPhrase = null, byte[] salt = null, CancellationToken cancellationToken = default);

/// <summary>
/// Decrypts a text that is encrypted by the <see cref="Encrypt" /> method.
/// </summary>
Expand All @@ -25,5 +37,15 @@ public interface IStringEncryptionService
/// <param name="salt">Salt value (optional, uses default if not provided)</param>
/// <returns>Decrypted text</returns>
string Decrypt(string cipherText, string passPhrase = null, byte[] salt = null);

/// <summary>
/// Decrypts a text that is encrypted by the <see cref="Encrypt" /> method.
/// </summary>
/// <param name="cipherText">The text in encrypted format</param>
/// <param name="passPhrase">A phrase to use as the encryption key (optional, uses default if not provided)</param>
/// <param name="salt">Salt value (optional, uses default if not provided)</param>
/// <param name="cancellationToken"></param>
/// <returns>Decrypted text</returns>
Task<string> DecryptAsync(string cipherText, string passPhrase = null, byte[] salt = null, CancellationToken cancellationToken = default);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
namespace Fluxera.Extensions.Common
{
using System.Text;
using JetBrains.Annotations;

/// <summary>
Expand All @@ -15,34 +14,28 @@ public sealed class StringEncryptionOptions
public StringEncryptionOptions()
{
this.KeySize = 256;
this.DefaultPassPhrase = "gsKnGZ041HLL4IM8";
this.InitVectorBytes = Encoding.ASCII.GetBytes("jkE49230Tf093b42");
this.DefaultSalt = Encoding.ASCII.GetBytes("hgt!16kl");
}

/// <summary>
/// This constant is used to determine the key-size of the encryption algorithm.
/// Default value: 256.
/// The default value is 256.
/// </summary>
public int KeySize { get; set; }

/// <summary>
/// Default password to encrypt/decrypt texts.
/// It's recommended to set to another value for security.
/// Default value: "gsKnGZ041HLL4IM8"
/// The default password to encrypt/decrypt texts.
/// </summary>
public string DefaultPassPhrase { get; set; }

/// <summary>
/// This constant string is used as a "salt" value for the PasswordDeriveBytes function calls.
/// This size of the IV (in bytes) must = (key-size / 8). Default key-size is 256, so the IV must be
/// 32 bytes long. Using a 16 character string here gives us 32 bytes when converted to a byte array.
/// Default value: Encoding.ASCII.GetBytes("jkE49230Tf093b42")
/// This size of the IV (in bytes) must = (key-size / 16). The default key-size is 256, so the
/// IV must be 16 bytes long.
/// </summary>
public byte[] InitVectorBytes { get; set; }

/// <summary>
/// Default value: Encoding.ASCII.GetBytes("hgt!16kl")
/// Gets the default salt bytes.
/// </summary>
public byte[] DefaultSalt { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<PropertyGroup>
Expand All @@ -22,8 +22,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Fluxera.Guards" Version="7.0.2" />
<PackageReference Include="Fluxera.Utilities" Version="7.0.3" />
<PackageReference Include="Fluxera.Guards" Version="7.0.3" />
<PackageReference Include="Fluxera.Utilities" Version="7.0.4" />
<PackageReference Include="GitVersion.MsBuild" Version="5.11.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
4 changes: 2 additions & 2 deletions src/Fluxera.Extensions.Common/PasswordGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ internal sealed class PasswordGenerator : IPasswordGenerator
{
private static readonly char[] PwdCharArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!$+-".ToCharArray();

private readonly RNGCryptoServiceProvider rng;
private readonly RandomNumberGenerator rng;

public PasswordGenerator()
{
this.ConsecutiveCharacters = false;
this.RepeatCharacters = true;
this.rng = new RNGCryptoServiceProvider();
this.rng = RandomNumberGenerator.Create();
}

private bool RepeatCharacters { get; }
Expand Down
20 changes: 8 additions & 12 deletions src/Fluxera.Extensions.Common/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;

/// <summary>
/// Extension methods for the <see cref="IServiceCollection" /> type.
Expand Down Expand Up @@ -165,17 +166,7 @@ public static IServiceCollection AddGuidGenerator(this IServiceCollection servic
/// <returns>The service collection.</returns>
public static IServiceCollection AddStringEncryptionService(this IServiceCollection services)
{
Guard.Against.Null(services);

// Add logging infrastructure.
services.AddLogging();

// Add options infrastructure.
services.AddOptions();

services.AddTransient<IStringEncryptionService, StringEncryptionService>();

return services;
return services.AddStringEncryptionService(null);
}

/// <summary>
Expand All @@ -195,7 +186,12 @@ public static IServiceCollection AddStringEncryptionService(this IServiceCollect
// Add options infrastructure.
services.AddOptions();

services.Configure(configureOptions);
if(configureOptions is not null)
{
services.Configure(configureOptions);
}

services.AddSingleton<IValidateOptions<StringEncryptionOptions>, StringEncryptionOptionsValidator>();

services.AddTransient<IStringEncryptionService, StringEncryptionService>();

Expand Down
45 changes: 45 additions & 0 deletions src/Fluxera.Extensions.Common/StringEncryptionOptionsValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
namespace Fluxera.Extensions.Common
{
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Options;

internal sealed class StringEncryptionOptionsValidator : IValidateOptions<StringEncryptionOptions>
{
public ValidateOptionsResult Validate(string name, StringEncryptionOptions options)
{
IList<string> failures = new List<string>();

if(options.KeySize % 8 != 0)
{
failures.Add("The key size must be a multiple of 8");
}

if(string.IsNullOrEmpty(options.DefaultPassPhrase))
{
failures.Add("A default pass phrase must be configured");
}

if(options.InitVectorBytes is null || !options.InitVectorBytes.Any())
{
failures.Add("The init vector bytes must be configured");
}
else
{
if(options.InitVectorBytes.Length != options.KeySize / 16)
{
failures.Add("The init vector must be of length key-size / 8");
}
}

if(options.DefaultSalt is null || !options.DefaultSalt.Any())
{
failures.Add("The default salt must be configured");
}

return failures.Any()
? ValidateOptionsResult.Fail(failures)
: ValidateOptionsResult.Success;
}
}
}
75 changes: 36 additions & 39 deletions src/Fluxera.Extensions.Common/StringEncryptionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Fluxera.Utilities;
using Microsoft.Extensions.Options;

/// <summary>
Expand All @@ -20,38 +23,35 @@ public StringEncryptionService(IOptions<StringEncryptionOptions> options)

public string Encrypt(string plainText, string passPhrase = null, byte[] salt = null)
{
if (plainText == null)
{
return null;
}
return AsyncHelper.RunSync(() => this.EncryptAsync(plainText, passPhrase, salt, CancellationToken.None));
}

if (passPhrase == null)
/// <inheritdoc />
public async Task<string> EncryptAsync(string plainText, string passPhrase = null, byte[] salt = null, CancellationToken cancellationToken = default)
{
if(plainText == null)
{
passPhrase = options.DefaultPassPhrase;
return null;
}

if (salt == null)
{
salt = options.DefaultSalt;
}
passPhrase ??= this.options.DefaultPassPhrase;
salt ??= this.options.DefaultSalt;

byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, salt))
using(Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, salt, 1000, HashAlgorithmName.SHA256))
{
byte[] keyBytes = password.GetBytes(options.KeySize / 8);
using (Aes symmetricKey = Aes.Create())
byte[] keyBytes = password.GetBytes(this.options.KeySize / 8);
using(Aes symmetricKey = Aes.Create())
{
symmetricKey.Mode = CipherMode.CBC;
using (ICryptoTransform cryptoTransform =
symmetricKey.CreateEncryptor(keyBytes, options.InitVectorBytes))
using(ICryptoTransform cryptoTransform = symmetricKey.CreateEncryptor(keyBytes, this.options.InitVectorBytes))
{
using (MemoryStream memoryStream = new MemoryStream())
using(MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream =
new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
await using(CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
await cryptoStream.WriteAsync(plainTextBytes, 0, plainTextBytes.Length, cancellationToken);
await cryptoStream.FlushFinalBlockAsync(cancellationToken);
byte[] cipherTextBytes = memoryStream.ToArray();
return Convert.ToBase64String(cipherTextBytes);
}
Expand All @@ -63,38 +63,35 @@ public string Encrypt(string plainText, string passPhrase = null, byte[] salt =

public string Decrypt(string cipherText, string passPhrase = null, byte[] salt = null)
{
if (string.IsNullOrEmpty(cipherText))
{
return null;
}
return AsyncHelper.RunSync(() => this.DecryptAsync(cipherText, passPhrase, salt, CancellationToken.None));
}

if (passPhrase == null)
/// <inheritdoc />
public async Task<string> DecryptAsync(string cipherText, string passPhrase = null, byte[] salt = null, CancellationToken cancellationToken = default)
{
if(string.IsNullOrEmpty(cipherText))
{
passPhrase = options.DefaultPassPhrase;
return null;
}

if (salt == null)
{
salt = options.DefaultSalt;
}
passPhrase ??= this.options.DefaultPassPhrase;
salt ??= this.options.DefaultSalt;

byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
using (Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, salt))
using(Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, salt, 1000, HashAlgorithmName.SHA256))
{
byte[] keyBytes = password.GetBytes(options.KeySize / 8);
using (Aes symmetricKey = Aes.Create())
byte[] keyBytes = password.GetBytes(this.options.KeySize / 8);
using(Aes symmetricKey = Aes.Create())
{
symmetricKey.Mode = CipherMode.CBC;
using (ICryptoTransform cryptoTransform =
symmetricKey.CreateDecryptor(keyBytes, options.InitVectorBytes))
using(ICryptoTransform cryptoTransform = symmetricKey.CreateDecryptor(keyBytes, this.options.InitVectorBytes))
{
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
using(MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
{
using (CryptoStream cryptoStream =
new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Read))
await using(CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Read))
{
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
int decryptedByteCount = await cryptoStream.ReadAsync(plainTextBytes, 0, plainTextBytes.Length, cancellationToken);
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<PropertyGroup>
Expand All @@ -23,7 +23,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Fluxera.Utilities" Version="7.0.3" />
<PackageReference Include="Fluxera.Utilities" Version="7.0.4" />
<PackageReference Include="GitVersion.MsBuild" Version="5.11.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<PropertyGroup>
Expand Down
Loading

0 comments on commit ec2ee67

Please sign in to comment.