diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 0d4a6fd..0000000
--- a/.editorconfig
+++ /dev/null
@@ -1,39 +0,0 @@
-[*.cs]
-indent_style = space
-indent_size = 4
-
-csharp_new_line_before_else = true
-csharp_new_line_before_finally = true
-csharp_new_line_before_members_in_object_initializers = true
-csharp_new_line_before_open_brace = methods, object_collection_array_initializers, control_blocks, types
-dotnet_sort_system_directives_first = true
-
-csharp_space_after_cast = false
-csharp_space_after_colon_in_inheritance_clause = true
-csharp_space_after_keywords_in_control_flow_statements = true
-csharp_space_before_colon_in_inheritance_clause = true
-csharp_space_between_method_call_empty_parameter_list_parentheses = false
-csharp_space_between_method_call_name_and_opening_parenthesis = false
-csharp_space_between_method_call_parameter_list_parentheses = false
-csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
-csharp_space_between_method_declaration_parameter_list_parentheses = false
-
-csharp_preserve_single_line_blocks = true
-csharp_preserve_single_line_statements = false
-csharp_prefer_braces = true:warning
-
-csharp_style_var_when_type_is_apparent = true:suggestion
-csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
-
-dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
-csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,sealed,async,override,abstract:suggestion
-
-dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
-dotnet_style_predefined_type_for_member_access = true:suggestion
-dotnet_style_object_initializer = true:suggestion
-dotnet_style_prefer_inferred_tuple_names = true:suggestion
-csharp_prefer_simple_default_expression = true:suggestion
-
-dotnet_style_qualification_for_field = false:suggestion
-dotnet_style_qualification_for_method = false:suggestion
-dotnet_style_qualification_for_property = false:suggestion
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 208444e..61ceef2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,13 +9,22 @@ on:
jobs:
build-library:
runs-on: ubuntu-latest
+
steps:
- - uses: actions/checkout@v2
+ - name: Checkout repository
+ uses: actions/checkout@v3
- - name: Setup .NET
- uses: actions/setup-dotnet@v1
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v3
with:
- dotnet-version: 6.0.x
+ dotnet-version: |
+ 3.1.x
+ 5.0.x
+ 6.0.x
+ 7.0.x
+
+ - name: Display .NET version
+ run: dotnet --version
- name: Restore dependencies
run: dotnet restore
@@ -24,4 +33,13 @@ jobs:
run: dotnet build EntityFrameworkCore.DataEncryption.sln --configuration Release -f net6.0 --no-restore
- name: Run unit tests
- run: dotnet test EntityFrameworkCore.DataEncryption.sln --configuration Release /p:CollectCoverage=true /p:Exclude="[xunit*]*" /p:CoverletOutputFormat=opencover /p:CoverletOutput="../TestResults/TestResults.xml" /maxcpucount:1 -f net6.0 --no-build --verbosity normal
\ No newline at end of file
+ run: dotnet test --configuration Release --collect:"XPlat Code Coverage" --settings ./test/EntityFrameworkCore.DataEncryption.Test/runsettings.xml
+
+ - name: Copy coverage results
+ run: cp ./test/EntityFrameworkCore.DataEncryption.Test/TestResults/**/*.xml ./test/EntityFrameworkCore.DataEncryption.Test/TestResults/
+
+ - name: Upload code coverage
+ uses: codecov/codecov-action@v3
+ with:
+ files: ./test/EntityFrameworkCore.DataEncryption.Test/TestResults/coverage.opencover.xml
+ fail_ci_if_error: true
\ No newline at end of file
diff --git a/EntityFrameworkCore.DataEncryption.sln b/EntityFrameworkCore.DataEncryption.sln
index fb35659..dd88ef0 100644
--- a/EntityFrameworkCore.DataEncryption.sln
+++ b/EntityFrameworkCore.DataEncryption.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30804.86
+# Visual Studio Version 17
+VisualStudioVersion = 17.3.32901.215
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3EC10767-1816-46B2-A78E-9856071CCFDB}"
EndProject
@@ -22,17 +22,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{64C3
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AesSample", "samples\AesSample\AesSample.csproj", "{8AA1E576-4016-4623-96C8-90330F05F9A8}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".azure", ".azure", "{073FEA06-67CF-47F8-8CE4-2B153A7D8443}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{EEF46CDC-C438-48FC-BEF7-83AEE26C63F7}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pipelines", "pipelines", "{68558245-F605-413F-A1D9-A4F60D489D68}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{4F549FEF-C57B-4A34-A2C7-8A632762DF85}"
ProjectSection(SolutionItems) = preProject
- .azure\pipelines\azure-pipelines.yml = .azure\pipelines\azure-pipelines.yml
- EndProjectSection
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5EE4E8BE-6B15-49DB-A4A8-D2CD63D5E90C}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
+ .github\workflows\build.yml = .github\workflows\build.yml
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AesSample.Fluent", "samples\AesSample.Fluent\AesSample.Fluent.csproj", "{CF04DE64-713F-4ED3-9C14-B7C11D22454C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -51,6 +49,10 @@ Global
{8AA1E576-4016-4623-96C8-90330F05F9A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AA1E576-4016-4623-96C8-90330F05F9A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AA1E576-4016-4623-96C8-90330F05F9A8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CF04DE64-713F-4ED3-9C14-B7C11D22454C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CF04DE64-713F-4ED3-9C14-B7C11D22454C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CF04DE64-713F-4ED3-9C14-B7C11D22454C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CF04DE64-713F-4ED3-9C14-B7C11D22454C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -59,8 +61,9 @@ Global
{D037F8D0-E606-4C5A-8669-DB6AAE7B056B} = {3EC10767-1816-46B2-A78E-9856071CCFDB}
{5E023B6A-0B47-4EC2-90B9-2DF998E58ADB} = {E4089551-AF4E-41B3-A6F8-2501A3BE0E0C}
{8AA1E576-4016-4623-96C8-90330F05F9A8} = {64C3D7D1-67B8-4070-AE67-C71B761535CC}
- {073FEA06-67CF-47F8-8CE4-2B153A7D8443} = {3A8D800E-77BD-44EF-82DB-C672281ECAAA}
- {68558245-F605-413F-A1D9-A4F60D489D68} = {073FEA06-67CF-47F8-8CE4-2B153A7D8443}
+ {EEF46CDC-C438-48FC-BEF7-83AEE26C63F7} = {3A8D800E-77BD-44EF-82DB-C672281ECAAA}
+ {4F549FEF-C57B-4A34-A2C7-8A632762DF85} = {EEF46CDC-C438-48FC-BEF7-83AEE26C63F7}
+ {CF04DE64-713F-4ED3-9C14-B7C11D22454C} = {64C3D7D1-67B8-4070-AE67-C71B761535CC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4997BAE9-29BF-4D79-AE5E-5605E7A0F049}
diff --git a/README.md b/README.md
index 0ca13bd..96da64a 100644
--- a/README.md
+++ b/README.md
@@ -3,14 +3,17 @@
[![.NET](https://github.com/Eastrall/EntityFrameworkCore.DataEncryption/actions/workflows/build.yml/badge.svg)](https://github.com/Eastrall/EntityFrameworkCore.DataEncryption/actions/workflows/build.yml)
[![codecov](https://codecov.io/gh/Eastrall/EntityFrameworkCore.DataEncryption/branch/master/graph/badge.svg)](https://codecov.io/gh/Eastrall/EntityFrameworkCore.DataEncryption)
[![Nuget](https://img.shields.io/nuget/v/EntityFrameworkCore.DataEncryption.svg)](https://www.nuget.org/packages/EntityFrameworkCore.DataEncryption)
+[![Nuget Downloads](https://img.shields.io/nuget/dt/EntityFrameworkCore.DataEncryption)](https://www.nuget.org/packages/EntityFrameworkCore.DataEncryption)
`EntityFrameworkCore.DataEncryption` is a [Microsoft Entity Framework Core](https://github.com/aspnet/EntityFrameworkCore) extension to add support of encrypted fields using built-in or custom encryption providers.
## Disclaimer
-This library has been developed initialy for a personal project of mine. It provides a simple way to encrypt column data.
+
:warning: This project is **not** affiliated with Microsoft. :warning:
-I **do not** take responsability if you use this in a production environment and loose your encryption key.
+This library has been developed initialy for a personal project of mine which suits my use case. It provides a simple way to encrypt column data.
+
+I **do not** take responsability if you use/deploy this in a production environment and loose your encryption key or corrupt your data.
## How to install
@@ -19,17 +22,29 @@ Install the package from [NuGet](https://www.nuget.org/) or from the `Package Ma
PM> Install-Package EntityFrameworkCore.DataEncryption
```
-## How to use
+## Supported types
-To use `EntityFrameworkCore.DataEncryption`, you will need to decorate your `string` properties of your entities with the `[Encrypted]` attribute and enable the encryption on the `ModelBuilder`.
+| Type | Default storage type |
+|------|----------------------|
+| `string` | Base64 string |
+| `byte[]` | BINARY |
-To enable the encryption correctly, you will need to use an **encryption provider**, there is a list of the available providers:
+## Built-in providers
| Name | Class | Extra |
|------|-------|-------|
-| [AES](https://docs.microsoft.com/en-US/dotnet/api/system.security.cryptography.aes?view=netcore-2.2) | [AesProvider](https://github.com/Eastrall/EntityFrameworkCore.DataEncryption/blob/master/src/EntityFrameworkCore.DataEncryption/Providers/AesProvider.cs) | Can use a 128bits, 192bits or 256bits key |
+| [AES](https://learn.microsoft.com/en-US/dotnet/api/system.security.cryptography.aes?view=net-6.0) | [AesProvider](https://github.com/Eastrall/EntityFrameworkCore.DataEncryption/blob/main/src/EntityFrameworkCore.DataEncryption/Providers/AesProvider.cs) | Can use a 128bits, 192bits or 256bits key |
+
+## How to use
+
+`EntityFrameworkCore.DataEncryption` supports 2 differents initialization methods:
+* Attribute
+* Fluent configuration
-### Example with `AesProvider`
+Depending on the initialization method you will use, you will need to decorate your `string` or `byte[]` properties of your entities with the `[Encrypted]` attribute or use the fluent `IsEncrypted()` method in your model configuration process.
+To use an encryption provider on your EF Core model, and enable the encryption on the `ModelBuilder`.
+
+### Example with `AesProvider` and attribute
```csharp
public class UserEntity
@@ -58,34 +73,72 @@ public class DatabaseContext : DbContext
public DatabaseContext(DbContextOptions options)
: base(options)
{
- this._provider = new AesProvider(this._encryptionKey, this._encryptionIV);
+ _provider = new AesProvider(this._encryptionKey, this._encryptionIV);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
- modelBuilder.UseEncryption(this._provider);
+ modelBuilder.UseEncryption(_provider);
}
}
```
-The code bellow creates a new `AesEncryption` provider and gives it to the current model. It will encrypt every `string` fields of your model that has the `[Encrypted]` attribute when saving changes to database. As for the decrypt process, it will be done when reading the `DbSet` of your `DbContext`.
+The code bellow creates a new [`AesProvider`](https://github.com/Eastrall/EntityFrameworkCore.DataEncryption/blob/main/src/EntityFrameworkCore.DataEncryption/Providers/AesProvider.cs) and gives it to the current model. It will encrypt every `string` fields of your model that has the `[Encrypted]` attribute when saving changes to database. As for the decrypt process, it will be done when reading the `DbSet` of your `DbContext`.
-## Create an encryption provider
+### Example with `AesProvider` and fluent configuration
+
+```csharp
+public class UserEntity
+{
+ public int Id { get; set; }
+ public string Username { get; set; }
+ public string Password { get; set; }
+ public int Age { get; set; }
+}
-> :warning: This section is outdated and doesn't work for V3.0.0 and will be updated soon.
+public class DatabaseContext : DbContext
+{
+ // Get key and IV from a Base64String or any other ways.
+ // You can generate a key and IV using "AesProvider.GenerateKey()"
+ private readonly byte[] _encryptionKey = ...;
+ private readonly byte[] _encryptionIV = ...;
+ private readonly IEncryptionProvider _provider;
+
+ public DbSet Users { get; set; }
+
+ public DatabaseContext(DbContextOptions options)
+ : base(options)
+ {
+ _provider = new AesProvider(this._encryptionKey, this._encryptionIV);
+ }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ // Entities builder *MUST* be called before UseEncryption().
+ var userEntityBuilder = modelBuilder.Entity();
+
+ userEntityBuilder.Property(x => x.Username).IsRequired().IsEncrypted();
+ userEntityBuilder.Property(x => x.Password).IsRequired().IsEncrypted();
+
+ modelBuilder.UseEncryption(_provider);
+ }
+}
+```
+
+## Create an encryption provider
`EntityFrameworkCore.DataEncryption` gives the possibility to create your own encryption providers. To do so, create a new class and make it inherit from `IEncryptionProvider`. You will need to implement the `Encrypt(string)` and `Decrypt(string)` methods.
```csharp
public class MyCustomEncryptionProvider : IEncryptionProvider
{
- public string Encrypt(string dataToEncrypt)
+ public byte[] Encrypt(byte[] input)
{
- // Encrypt data and return as Base64 string
+ // Encrypt the given input and return the encrypted data as a byte[].
}
- public string Decrypt(string dataToDecrypt)
+ public byte[] Decrypt(byte[] input)
{
- // Decrypt a Base64 string to plain string
+ // Decrypt the given input and return the decrypted data as a byte[].
}
}
```
@@ -99,12 +152,12 @@ public class DatabaseContext : DbContext
public DatabaseContext(DbContextOptions options)
: base(options)
{
- this._provider = new MyCustomEncryptionProvider();
+ _provider = new MyCustomEncryptionProvider();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
- modelBuilder.UseEncryption(this._provider);
+ modelBuilder.UseEncryption(_provider);
}
}
```
diff --git a/samples/AesSample.Fluent/AesSample.Fluent.csproj b/samples/AesSample.Fluent/AesSample.Fluent.csproj
new file mode 100644
index 0000000..31cee5b
--- /dev/null
+++ b/samples/AesSample.Fluent/AesSample.Fluent.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net6.0
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/AesSample.Fluent/DatabaseContext.cs b/samples/AesSample.Fluent/DatabaseContext.cs
new file mode 100644
index 0000000..e68fab5
--- /dev/null
+++ b/samples/AesSample.Fluent/DatabaseContext.cs
@@ -0,0 +1,40 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.DataEncryption;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using System.ComponentModel.DataAnnotations;
+
+namespace AesSample.Fluent;
+
+public class DatabaseContext : DbContext
+{
+ private readonly IEncryptionProvider _encryptionProvider;
+
+ public DbSet Users { get; set; }
+
+ public DatabaseContext(DbContextOptions options, IEncryptionProvider encryptionProvider)
+ : base(options)
+ {
+ _encryptionProvider = encryptionProvider;
+ }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ EntityTypeBuilder userEntityBuilder = modelBuilder.Entity();
+
+ userEntityBuilder.HasKey(x => x.Id);
+ userEntityBuilder.Property(x => x.Id).IsRequired().ValueGeneratedOnAdd();
+ userEntityBuilder.Property(x => x.FirstName).IsRequired();
+ userEntityBuilder.Property(x => x.LastName).IsRequired();
+ userEntityBuilder.Property(x => x.Email).IsRequired().IsEncrypted();
+ userEntityBuilder.Property(x => x.Notes).IsRequired().HasColumnType("BLOB").IsEncrypted(StorageFormat.Binary);
+ userEntityBuilder.Property(x => x.EncryptedData).IsRequired().IsEncrypted();
+ userEntityBuilder.Property(x => x.EncryptedDataAsString).IsRequired().HasColumnType("TEXT").IsEncrypted(StorageFormat.Base64);
+
+ if (_encryptionProvider is not null)
+ {
+ modelBuilder.UseEncryption(_encryptionProvider);
+ }
+
+ base.OnModelCreating(modelBuilder);
+ }
+}
diff --git a/samples/AesSample.Fluent/EncryptedDatabaseContext.cs b/samples/AesSample.Fluent/EncryptedDatabaseContext.cs
new file mode 100644
index 0000000..03520a1
--- /dev/null
+++ b/samples/AesSample.Fluent/EncryptedDatabaseContext.cs
@@ -0,0 +1,11 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace AesSample.Fluent;
+
+public class EncryptedDatabaseContext : DatabaseContext
+{
+ public EncryptedDatabaseContext(DbContextOptions options)
+ : base(options, null)
+ {
+ }
+}
\ No newline at end of file
diff --git a/samples/AesSample.Fluent/Program.cs b/samples/AesSample.Fluent/Program.cs
new file mode 100644
index 0000000..539d827
--- /dev/null
+++ b/samples/AesSample.Fluent/Program.cs
@@ -0,0 +1,60 @@
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore.DataEncryption.Providers;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Linq;
+
+namespace AesSample.Fluent;
+
+internal class Program
+{
+ static void Main(string[] args)
+ {
+ using SqliteConnection connection = new("DataSource=:memory:");
+ connection.Open();
+
+ var options = new DbContextOptionsBuilder()
+ .UseSqlite(connection)
+ .Options;
+
+ // AES key randomly generated at each run.
+ AesKeyInfo keyInfo = AesProvider.GenerateKey(AesKeySize.AES256Bits);
+ byte[] encryptionKey = keyInfo.Key;
+ byte[] encryptionIV = keyInfo.IV;
+ var encryptionProvider = new AesProvider(encryptionKey, encryptionIV);
+
+ using (var context = new DatabaseContext(options, encryptionProvider))
+ {
+ context.Database.EnsureCreated();
+
+ var user = new UserEntity
+ {
+ FirstName = "John",
+ LastName = "Doe",
+ Email = "john@doe.com",
+ Notes = "Hello world!",
+ EncryptedData = new byte[2] { 1, 2 },
+ EncryptedDataAsString = new byte[2] { 3, 4 }
+ };
+
+ context.Users.Add(user);
+ context.SaveChanges();
+
+ Console.WriteLine($"Users count: {context.Users.Count()}");
+ }
+
+ using (var context = new EncryptedDatabaseContext(options))
+ {
+ UserEntity user = context.Users.First();
+
+ Console.WriteLine($"Encrypted User: {user.FirstName} {user.LastName} - {user.Email} (Notes: {user.Notes})");
+ }
+
+ using (var context = new DatabaseContext(options, encryptionProvider))
+ {
+ UserEntity user = context.Users.First();
+
+ Console.WriteLine($"User: {user.FirstName} {user.LastName} - {user.Email} (Notes: {user.Notes})");
+ }
+ }
+}
diff --git a/samples/AesSample.Fluent/UserEntity.cs b/samples/AesSample.Fluent/UserEntity.cs
new file mode 100644
index 0000000..2e22cd6
--- /dev/null
+++ b/samples/AesSample.Fluent/UserEntity.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace AesSample.Fluent;
+
+public class UserEntity
+{
+ public Guid Id { get; set; }
+
+ public string FirstName { get; set; }
+
+ public string LastName { get; set; }
+
+ public string Email { get; set; }
+
+ public string Notes { get; set; }
+
+ public byte[] EncryptedData { get; set; }
+
+ public byte[] EncryptedDataAsString { get; set; }
+}
diff --git a/samples/AesSample/AesSample.csproj b/samples/AesSample/AesSample.csproj
index aa33ab0..4a89054 100644
--- a/samples/AesSample/AesSample.csproj
+++ b/samples/AesSample/AesSample.csproj
@@ -1,23 +1,19 @@
-
- Exe
- net5.0;net6.0
-
+
+ Exe
+ net6.0
+ 10
+
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
+
+
+
diff --git a/samples/AesSample/DatabaseContext.cs b/samples/AesSample/DatabaseContext.cs
index 029b961..0bd48cb 100644
--- a/samples/AesSample/DatabaseContext.cs
+++ b/samples/AesSample/DatabaseContext.cs
@@ -1,25 +1,24 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.DataEncryption;
-namespace AesSample
+namespace AesSample;
+
+public class DatabaseContext : DbContext
{
- public class DatabaseContext : DbContext
- {
- private readonly IEncryptionProvider _encryptionProvider;
+ private readonly IEncryptionProvider _encryptionProvider;
- public DbSet Users { get; set; }
+ public DbSet Users { get; set; }
- public DatabaseContext(DbContextOptions options, IEncryptionProvider encryptionProvider)
- : base(options)
- {
- _encryptionProvider = encryptionProvider;
- }
+ public DatabaseContext(DbContextOptions options, IEncryptionProvider encryptionProvider)
+ : base(options)
+ {
+ _encryptionProvider = encryptionProvider;
+ }
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- modelBuilder.UseEncryption(_encryptionProvider);
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.UseEncryption(_encryptionProvider);
- base.OnModelCreating(modelBuilder);
- }
+ base.OnModelCreating(modelBuilder);
}
}
diff --git a/samples/AesSample/Program.cs b/samples/AesSample/Program.cs
index c39313d..a227d2d 100644
--- a/samples/AesSample/Program.cs
+++ b/samples/AesSample/Program.cs
@@ -1,56 +1,53 @@
-using Microsoft.EntityFrameworkCore;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.DataEncryption.Providers;
using System;
using System.Linq;
-using System.Security;
-namespace AesSample
+namespace AesSample;
+
+static class Program
{
- static class Program
+ static void Main()
{
- static void Main()
- {
- var options = new DbContextOptionsBuilder()
- .UseInMemoryDatabase(databaseName: "MyInMemoryDatabase")
- .Options;
+ using SqliteConnection connection = new("DataSource=:memory:");
+ connection.Open();
- // AES key randomly generated at each run.
- byte[] encryptionKey = AesProvider.GenerateKey(AesKeySize.AES256Bits).Key;
- var encryptionProvider = new AesProvider(encryptionKey);
+ var options = new DbContextOptionsBuilder()
+ .UseSqlite(connection)
+ .Options;
- using var context = new DatabaseContext(options, encryptionProvider);
+ // AES key randomly generated at each run.
+ AesKeyInfo keyInfo = AesProvider.GenerateKey(AesKeySize.AES256Bits);
+ byte[] encryptionKey = keyInfo.Key;
+ byte[] encryptionIV = keyInfo.IV;
+ var encryptionProvider = new AesProvider(encryptionKey, encryptionIV);
+
+ using (var context = new DatabaseContext(options, encryptionProvider))
+ {
+ context.Database.EnsureCreated();
var user = new UserEntity
{
FirstName = "John",
LastName = "Doe",
Email = "john@doe.com",
- Password = BuildPassword(),
+ Notes = "Hello world!",
+ EncryptedData = new byte[2] { 1, 2 },
+ EncryptedDataAsString = new byte[2] { 3, 4 }
};
context.Users.Add(user);
context.SaveChanges();
Console.WriteLine($"Users count: {context.Users.Count()}");
-
- user = context.Users.First();
-
- Console.WriteLine($"User: {user.FirstName} {user.LastName} - {user.Email} ({user.Password.Length})");
}
- static SecureString BuildPassword()
+ using (var context = new DatabaseContext(options, encryptionProvider))
{
- SecureString result = new();
- result.AppendChar('L');
- result.AppendChar('e');
- result.AppendChar('t');
- result.AppendChar('M');
- result.AppendChar('e');
- result.AppendChar('I');
- result.AppendChar('n');
- result.AppendChar('!');
- result.MakeReadOnly();
- return result;
+ UserEntity user = context.Users.First();
+
+ Console.WriteLine($"User: {user.FirstName} {user.LastName} - {user.Email} (Notes: {user.Notes})");
}
}
}
diff --git a/samples/AesSample/UserEntity.cs b/samples/AesSample/UserEntity.cs
index 671890b..0a9ee26 100644
--- a/samples/AesSample/UserEntity.cs
+++ b/samples/AesSample/UserEntity.cs
@@ -1,26 +1,35 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
-using System.Security;
-namespace AesSample
+namespace AesSample;
+
+public class UserEntity
{
- public class UserEntity
- {
- [Key]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public Guid Id { get; set; }
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public Guid Id { get; set; }
+
+ [Required]
+ public string FirstName { get; set; }
+
+ [Required]
+ public string LastName { get; set; }
- [Required]
- public string FirstName { get; set; }
+ [Required]
+ [Encrypted]
+ public string Email { get; set; }
- [Required]
- public string LastName { get; set; }
+ [Required]
+ [Encrypted(StorageFormat.Binary)]
+ public string Notes { get; set; }
- [Required]
- [Encrypted]
- public string Email { get; set; }
+ [Required]
+ [Encrypted]
+ public byte[] EncryptedData { get; set; }
- public SecureString Password { get; set; }
- }
+ [Required]
+ [Encrypted(StorageFormat.Base64)]
+ [Column(TypeName = "TEXT")]
+ public byte[] EncryptedDataAsString { get; set; }
}
diff --git a/src/EntityFrameworkCore.DataEncryption/Attributes/EncryptedAttribute.cs b/src/EntityFrameworkCore.DataEncryption/Attributes/EncryptedAttribute.cs
index c76c5cf..2e03135 100644
--- a/src/EntityFrameworkCore.DataEncryption/Attributes/EncryptedAttribute.cs
+++ b/src/EntityFrameworkCore.DataEncryption/Attributes/EncryptedAttribute.cs
@@ -1,32 +1,32 @@
-namespace System.ComponentModel.DataAnnotations
+namespace System.ComponentModel.DataAnnotations;
+
+///
+/// Specifies that the data field value should be encrypted.
+///
+[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
+public sealed class EncryptedAttribute : Attribute
{
///
- /// Specifies that the data field value should be encrypted.
+ /// Returns the storage format for the database value.
///
- [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
- public sealed class EncryptedAttribute : Attribute
- {
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The storage format.
- ///
- public EncryptedAttribute(StorageFormat format)
- {
- Format = format;
- }
+ public StorageFormat Format { get; }
- ///
- /// Initializes a new instance of the class.
- ///
- public EncryptedAttribute() : this(StorageFormat.Default)
- {
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The storage format.
+ ///
+ public EncryptedAttribute(StorageFormat format)
+ {
+ Format = format;
+ }
- ///
- /// Returns the storage format for the database value.
- ///
- public StorageFormat Format { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public EncryptedAttribute()
+ : this(StorageFormat.Default)
+ {
}
}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Attributes/StorageFormat.cs b/src/EntityFrameworkCore.DataEncryption/Attributes/StorageFormat.cs
index f948d12..e83f2aa 100644
--- a/src/EntityFrameworkCore.DataEncryption/Attributes/StorageFormat.cs
+++ b/src/EntityFrameworkCore.DataEncryption/Attributes/StorageFormat.cs
@@ -1,26 +1,27 @@
-namespace System.ComponentModel.DataAnnotations
+namespace System.ComponentModel.DataAnnotations;
+
+///
+/// Represents the storage format for an encrypted value.
+///
+public enum StorageFormat
{
///
- /// Represents the storage format for an encrypted value.
+ /// The format is determined by the model data type.
///
- public enum StorageFormat
- {
- ///
- /// The format is determined by the model data type.
- ///
- Default,
- ///
- /// The value is stored in binary.
- ///
- Binary,
- ///
- /// The value is stored in a Base64-encoded string.
- ///
- ///
- /// NB: If the source property is a ,
- /// and no encryption provider is configured,
- /// the string will not be modified.
- ///
- Base64,
- }
+ Default,
+
+ ///
+ /// The value is stored in binary.
+ ///
+ Binary,
+
+ ///
+ /// The value is stored in a Base64-encoded string.
+ ///
+ ///
+ /// NB: If the source property is a ,
+ /// and no encryption provider is configured,
+ /// the string will not be modified.
+ ///
+ Base64,
}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj b/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj
index 95d89c6..759f3b9 100644
--- a/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj
+++ b/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj
@@ -1,13 +1,13 @@
- netstandard2.0;net6.0;net5.0
- 9.0
+ netstandard2.0;net6.0;net7.0
+ 10.0
true
EntityFrameworkCore.DataEncryption
Microsoft.EntityFrameworkCore.DataEncryption
- true
- 3.0.1
+ true
+ 4.0.0
Filipe GOMES PEIXOTO
EntityFrameworkCore.DataEncryption
https://github.com/Eastrall/EntityFrameworkCore.DataEncryption
@@ -17,15 +17,15 @@
true
entity-framework-core, extensions, dotnet-core, dotnet, encryption, fluent-api
icon.png
- Filipe GOMES PEIXOTO © 2019 - 2021
+ Filipe GOMES PEIXOTO © 2019 - 2023
A plugin for Microsoft.EntityFrameworkCore to add support of encrypted fields using built-in or custom encryption providers.
LICENSE
- Add support for .NET 5 and .NET 6
+ https://github.com/Eastrall/EntityFrameworkCore.DataEncryption/releases/tag/v4.0.0
README.md
- Auto
+ Auto
@@ -34,24 +34,21 @@
-
-
-
-
-
-
-
+
-
+
+
+
+
True
- True
- \
+ True
+ \
@@ -62,4 +59,8 @@
+
+
+
+
diff --git a/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj.DotSettings b/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj.DotSettings
deleted file mode 100644
index 6162834..0000000
--- a/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj.DotSettings
+++ /dev/null
@@ -1,2 +0,0 @@
-
- CSharp90
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/IEncryptionProvider.cs b/src/EntityFrameworkCore.DataEncryption/IEncryptionProvider.cs
index 49a8c6d..77c9eb9 100644
--- a/src/EntityFrameworkCore.DataEncryption/IEncryptionProvider.cs
+++ b/src/EntityFrameworkCore.DataEncryption/IEncryptionProvider.cs
@@ -1,67 +1,21 @@
-using System;
-using System.IO;
+namespace Microsoft.EntityFrameworkCore.DataEncryption;
-namespace Microsoft.EntityFrameworkCore.DataEncryption
+///
+/// Provides a mechanism to encrypt and decrypt data.
+///
+public interface IEncryptionProvider
{
///
- /// Provides a mechanism for implementing a custom encryption provider.
+ /// Encrypts the given input byte array.
///
- public interface IEncryptionProvider
- {
- ///
- /// Encrypts a value.
- ///
- ///
- /// The type of data stored in the database.
- ///
- ///
- /// The type of value stored in the model.
- ///
- ///
- /// Input data to encrypt.
- ///
- ///
- /// Function which converts the model value to a byte array.
- ///
- ///
- /// Function which encodes the value for storing the the database.
- ///
- ///
- /// Encrypted data.
- ///
- ///
- /// is .
- /// -or-
- /// is .
- ///
- TStore Encrypt(TModel dataToEncrypt, Func converter, Func encoder);
+ /// Input to encrypt.
+ /// Encrypted input.
+ byte[] Encrypt(byte[] input);
- ///
- /// Decrypts a value.
- ///
- ///
- /// The type of data stored in the database.
- ///
- ///
- /// The type of value stored in the model.
- ///
- ///
- /// Encrypted data to decrypt.
- ///
- ///
- /// Function which converts the stored data to a byte array.
- ///
- ///
- /// Function which converts the decrypted to the return value.
- ///
- ///
- /// Decrypted data.
- ///
- ///
- /// is .
- /// -or-
- /// is .
- ///
- TModel Decrypt(TStore dataToDecrypt, Func decoder, Func converter);
- }
+ ///
+ /// Decrypts the given input byte array.
+ ///
+ /// Input to decrypt.
+ /// Decrypted input.
+ byte[] Decrypt(byte[] input);
}
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder.cs b/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder.cs
deleted file mode 100644
index 28144e6..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder.cs
+++ /dev/null
@@ -1,206 +0,0 @@
-using System;
-using System.IO;
-using System.Security;
-using System.Text;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
-{
- ///
- /// Utilities for building value converters.
- ///
- public static class ConverterBuilder
- {
- ///
- /// Builds a converter for a property with a custom model type.
- ///
- ///
- /// The model type.
- ///
- ///
- /// The , if any.
- ///
- ///
- /// The function used to decode the model type to a byte array.
- ///
- ///
- /// The function used to encode a byte array to the model type.
- ///
- ///
- /// An instance.
- ///
- ///
- /// is .
- /// -or-
- /// is .
- ///
- public static ConverterBuilder From(
- this IEncryptionProvider encryptionProvider,
- Func decoder,
- Func encoder)
- {
- if (decoder is null)
- {
- throw new ArgumentNullException(nameof(decoder));
- }
-
- if (encoder is null)
- {
- throw new ArgumentNullException(nameof(encoder));
- }
-
- return new ConverterBuilder(encryptionProvider, decoder, encoder);
- }
-
- ///
- /// Builds a converter for a binary property.
- ///
- ///
- /// The , if any.
- ///
- ///
- /// An instance.
- ///
- public static ConverterBuilder FromBinary(this IEncryptionProvider encryptionProvider)
- {
- return new ConverterBuilder(encryptionProvider, b => b, StandardConverters.StreamToBytes);
- }
-
- ///
- /// Builds a converter for a string property.
- ///
- ///
- /// The , if any.
- ///
- ///
- /// An instance.
- ///
- public static ConverterBuilder FromString(this IEncryptionProvider encryptionProvider)
- {
- return new ConverterBuilder(encryptionProvider, Encoding.UTF8.GetBytes, StandardConverters.StreamToString);
- }
-
- ///
- /// Builds a converter for a property.
- ///
- ///
- /// The , if any.
- ///
- ///
- /// An instance.
- ///
- public static ConverterBuilder FromSecureString(this IEncryptionProvider encryptionProvider)
- {
- return new ConverterBuilder(encryptionProvider, Encoding.UTF8.GetBytes, StandardConverters.StreamToSecureString);
- }
-
- ///
- /// Specifies that the property should be stored in the database using a custom format.
- ///
- ///
- /// The model type.
- ///
- ///
- /// The store type.
- ///
- ///
- /// The representing the model type.
- ///
- ///
- /// The function used to decode the store type into a byte array.
- ///
- ///
- /// The function used to encode a byte array into the store type.
- ///
- ///
- /// An instance.
- ///
- ///
- /// is .
- /// -or-
- /// is .
- /// -or-
- /// is .
- ///
- ///
- /// is not a supported type.
- ///
- public static ConverterBuilder To(
- ConverterBuilder modelType,
- Func decoder,
- Func encoder)
- {
- if (modelType.IsEmpty)
- {
- throw new ArgumentNullException(nameof(modelType));
- }
-
- if (decoder is null)
- {
- throw new ArgumentNullException(nameof(decoder));
- }
-
- if (encoder is null)
- {
- throw new ArgumentNullException(nameof(encoder));
- }
-
- return new ConverterBuilder(modelType, decoder, encoder);
- }
-
- ///
- /// Specifies that the property should be stored in the database in binary.
- ///
- ///
- /// The model type.
- ///
- ///
- /// The representing the model type.
- ///
- ///
- /// An instance.
- ///
- ///
- /// is .
- ///
- ///
- /// is not a supported type.
- ///
- public static ConverterBuilder ToBinary(this ConverterBuilder modelType)
- {
- if (modelType.IsEmpty)
- {
- throw new ArgumentNullException(nameof(modelType));
- }
-
- return new ConverterBuilder(modelType, b => b, StandardConverters.StreamToBytes);
- }
-
- ///
- /// Specifies that the property should be stored in the database in a Base64-encoded string.
- ///
- ///
- /// The model type.
- ///
- ///
- /// The representing the model type.
- ///
- ///
- /// An instance.
- ///
- ///
- /// is .
- ///
- ///
- /// is not a supported type.
- ///
- public static ConverterBuilder ToBase64(this ConverterBuilder modelType)
- {
- if (modelType.IsEmpty)
- {
- throw new ArgumentNullException(nameof(modelType));
- }
-
- return new ConverterBuilder(modelType, Convert.FromBase64String, StandardConverters.StreamToBase64String);
- }
- }
-}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`1.cs b/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`1.cs
deleted file mode 100644
index c0ae461..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`1.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
-{
- ///
- /// A converter builder class which has the model type specified.
- ///
- ///
- /// The model type.
- ///
- public readonly struct ConverterBuilder
- {
- internal ConverterBuilder(IEncryptionProvider encryptionProvider, Func decoder, Func encoder)
- {
- Debug.Assert(decoder is not null);
- Debug.Assert(encoder is not null);
-
- EncryptionProvider = encryptionProvider;
- Decoder = decoder;
- Encoder = encoder;
- }
-
- private readonly IEncryptionProvider EncryptionProvider;
- private readonly Func Decoder;
- private readonly Func Encoder;
-
- internal bool IsEmpty => Decoder is null || Encoder is null;
-
- internal void Deconstruct(out IEncryptionProvider encryptionProvider, out Func decoder, out Func encoder)
- {
- encryptionProvider = EncryptionProvider;
- decoder = Decoder;
- encoder = Encoder;
- }
- }
-}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`2.cs b/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`2.cs
deleted file mode 100644
index dddda9f..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`2.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
-{
- ///
- /// A converter builder class which has both the model type and store type specified.
- ///
- ///
- /// The model type.
- ///
- ///
- /// The store type.
- ///
- public readonly struct ConverterBuilder
- {
- internal ConverterBuilder(ConverterBuilder modelType, Func decoder, Func encoder)
- {
- Debug.Assert(!modelType.IsEmpty);
- Debug.Assert(decoder is not null);
- Debug.Assert(encoder is not null);
-
- ModelType = modelType;
- Decoder = decoder;
- Encoder = encoder;
- }
-
- private readonly ConverterBuilder ModelType;
- private readonly Func Decoder;
- private readonly Func Encoder;
-
- ///
- /// Builds the value converter.
- ///
- ///
- /// The mapping hints to use, if any.
- ///
- ///
- /// The .
- ///
- public ValueConverter Build(ConverterMappingHints mappingHints = null)
- {
- var (encryptionProvider, modelDecoder, modelEncoder) = ModelType;
- var storeDecoder = Decoder;
- var storeEncoder = Encoder;
-
- if (modelDecoder is null || modelEncoder is null || storeDecoder is null || storeEncoder is null)
- {
- return null;
- }
-
- if (encryptionProvider is null)
- {
- return new ValueConverter(
- m => storeEncoder(StandardConverters.BytesToStream(modelDecoder(m))),
- s => modelEncoder(StandardConverters.BytesToStream(storeDecoder(s))),
- mappingHints);
- }
-
- return new EncryptionConverter(
- encryptionProvider,
- m => encryptionProvider.Encrypt(m, modelDecoder, storeEncoder),
- s => encryptionProvider.Decrypt(s, storeDecoder, modelEncoder),
- mappingHints);
- }
- }
-}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/EncodingExtensions.cs b/src/EntityFrameworkCore.DataEncryption/Internal/EncodingExtensions.cs
deleted file mode 100644
index 7cf1634..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Internal/EncodingExtensions.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Security;
-using System.Text;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
-{
- internal static class EncodingExtensions
- {
- internal static byte[] GetBytes(this Encoding encoding, SecureString value)
- {
- if (encoding is null)
- {
- throw new ArgumentNullException(nameof(encoding));
- }
-
- if (value is null || value.Length == 0)
- {
- return Array.Empty();
- }
-
- IntPtr valuePtr = IntPtr.Zero;
- try
- {
- valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
- if (valuePtr == IntPtr.Zero)
- {
- return Array.Empty();
- }
-
- unsafe
- {
- char* chars = (char*)valuePtr;
- Debug.Assert(chars != null);
-
- int byteCount = encoding.GetByteCount(chars, value.Length);
-
- var result = new byte[byteCount];
- fixed (byte* bytes = result)
- {
- encoding.GetBytes(chars, value.Length, bytes, byteCount);
- }
-
- return result;
- }
- }
- finally
- {
- if (valuePtr != IntPtr.Zero)
- {
- Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/EncryptionConverter.cs b/src/EntityFrameworkCore.DataEncryption/Internal/EncryptionConverter.cs
index 20794f2..57ad58a 100644
--- a/src/EntityFrameworkCore.DataEncryption/Internal/EncryptionConverter.cs
+++ b/src/EntityFrameworkCore.DataEncryption/Internal/EncryptionConverter.cs
@@ -1,28 +1,71 @@
-using System;
-using System.Linq.Expressions;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.Text;
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
+namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal;
+
+///
+/// Defines the internal encryption converter for string values.
+///
+///
+///
+internal sealed class EncryptionConverter : ValueConverter
{
///
- /// Defines the internal encryption converter for string values.
+ /// Creates a new instance.
///
- internal sealed class EncryptionConverter : ValueConverter, IEncryptionValueConverter
+ /// Encryption provider to use.
+ /// Encryption storage format.
+ /// Mapping hints.
+ public EncryptionConverter(IEncryptionProvider encryptionProvider, StorageFormat storageFormat, ConverterMappingHints mappingHints = null)
+ : base(
+ x => Encrypt(x, encryptionProvider, storageFormat),
+ x => Decrypt(x, encryptionProvider, storageFormat),
+ mappingHints)
+ {
+ }
+
+ private static TOutput Encrypt(TInput input, IEncryptionProvider encryptionProvider, StorageFormat storageFormat)
+ {
+ byte[] inputData = input switch
+ {
+ string => Encoding.UTF8.GetBytes(input.ToString()),
+ byte[] => input as byte[],
+ _ => null,
+ };
+
+ byte[] encryptedRawBytes = encryptionProvider.Encrypt(inputData);
+
+ object encryptedData = storageFormat switch
+ {
+ StorageFormat.Default or StorageFormat.Base64 => Convert.ToBase64String(encryptedRawBytes),
+ _ => encryptedRawBytes
+ };
+
+ return (TOutput)Convert.ChangeType(encryptedData, typeof(TOutput));
+ }
+
+ private static TModel Decrypt(TProvider input, IEncryptionProvider encryptionProvider, StorageFormat storageFormat)
{
- ///
- /// Creates a new instance.
- ///
- public EncryptionConverter(
- IEncryptionProvider encryptionProvider,
- Expression> convertToProviderExpression,
- Expression> convertFromProviderExpression,
- ConverterMappingHints mappingHints = null)
- : base(convertToProviderExpression, convertFromProviderExpression, mappingHints)
+ Type destinationType = typeof(TModel);
+ byte[] inputData = storageFormat switch
+ {
+ StorageFormat.Default or StorageFormat.Base64 => Convert.FromBase64String(input.ToString()),
+ _ => input as byte[]
+ };
+ byte[] decryptedRawBytes = encryptionProvider.Decrypt(inputData);
+ object decryptedData = null;
+
+ if (destinationType == typeof(string))
+ {
+ decryptedData = Encoding.UTF8.GetString(decryptedRawBytes).Trim('\0');
+ }
+ else if (destinationType == typeof(byte[]))
{
- EncryptionProvider = encryptionProvider;
+ decryptedData = decryptedRawBytes;
}
- ///
- public IEncryptionProvider EncryptionProvider { get; }
+ return (TModel)Convert.ChangeType(decryptedData, typeof(TModel));
}
}
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/IEncryptionValueConverter.cs b/src/EntityFrameworkCore.DataEncryption/Internal/IEncryptionValueConverter.cs
deleted file mode 100644
index f54fa82..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Internal/IEncryptionValueConverter.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
-{
- ///
- /// Interface for an encryption value converter.
- ///
- public interface IEncryptionValueConverter
- {
- ///
- /// Returns the encryption provider, if any.
- ///
- ///
- /// The for this converter, if any.
- ///
- IEncryptionProvider EncryptionProvider { get; }
- }
-}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/PropertyAnnotations.cs b/src/EntityFrameworkCore.DataEncryption/Internal/PropertyAnnotations.cs
new file mode 100644
index 0000000..37187a9
--- /dev/null
+++ b/src/EntityFrameworkCore.DataEncryption/Internal/PropertyAnnotations.cs
@@ -0,0 +1,7 @@
+namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal;
+
+internal class PropertyAnnotations
+{
+ public const string IsEncrypted = "Microsoft.EntityFrameworkCore.DataEncryption.IsEncrypted";
+ public const string StorageFormat = "Microsoft.EntityFrameworkCore.DataEncryption.StorageFormat";
+}
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/StandardConverters.cs b/src/EntityFrameworkCore.DataEncryption/Internal/StandardConverters.cs
deleted file mode 100644
index 0a5df4d..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Internal/StandardConverters.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System;
-using System.IO;
-using System.Security;
-using System.Text;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
-{
- internal static class StandardConverters
- {
- internal static Stream BytesToStream(byte[] bytes) => new MemoryStream(bytes);
-
- internal static byte[] StreamToBytes(Stream stream)
- {
- if (stream is MemoryStream ms)
- {
- return ms.ToArray();
- }
-
- using var output = new MemoryStream();
- stream.CopyTo(output);
- return output.ToArray();
- }
-
- internal static string StreamToBase64String(Stream stream) => Convert.ToBase64String(StreamToBytes(stream));
-
- internal static string StreamToString(Stream stream)
- {
- using var reader = new StreamReader(stream, Encoding.UTF8);
- return reader.ReadToEnd().Trim('\0');
- }
-
- internal static SecureString StreamToSecureString(Stream stream)
- {
- using var reader = new StreamReader(stream, Encoding.UTF8);
-
- var result = new SecureString();
- var buffer = new char[100];
- while (!reader.EndOfStream)
- {
- var charsRead = reader.Read(buffer, 0, buffer.Length);
- if (charsRead != 0)
- {
- for (int index = 0; index < charsRead; index++)
- {
- char c = buffer[index];
- if (c != '\0')
- {
- result.AppendChar(c);
- }
- }
- }
- }
-
- return result;
- }
- }
-}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Migration/EncryptionMigrator.cs b/src/EntityFrameworkCore.DataEncryption/Migration/EncryptionMigrator.cs
deleted file mode 100644
index 333de7d..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Migration/EncryptionMigrator.cs
+++ /dev/null
@@ -1,208 +0,0 @@
-using System;
-using System.Linq;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore.DataEncryption.Internal;
-using Microsoft.EntityFrameworkCore.DataEncryption.Providers;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.Extensions.Logging;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Migration
-{
- ///
- /// Utilities for migrating encrypted data from one provider to another.
- ///
- ///
- /// To migrate from v1 to v2 of :
- ///
- /// var sourceProvider = new AesProvider(key, iv);
- /// var destinationProvider = new AesProvider(key);
- /// var migrationProvider = new MigrationEncryptionProvider(sourceProvider, destinationProvider);
- /// await using var migrationContext = new DatabaseContext(options, migrationProvider);
- /// await migrationContext.MigrateAsync(logger, cancellationToken);
- ///
- ///
- public static class EncryptionMigrator
- {
- private static readonly MethodInfo SetMethod = typeof(DbContext).GetMethod(nameof(DbContext.Set));
-
- private static IQueryable