Skip to content

Commit

Permalink
Merge pull request #45 from Eastrall/wip/v4
Browse files Browse the repository at this point in the history
Version 4.0.0
  • Loading branch information
Eastrall committed Jan 18, 2023
2 parents de814ed + b56a363 commit 0beb736
Show file tree
Hide file tree
Showing 48 changed files with 1,309 additions and 2,150 deletions.
39 changes: 0 additions & 39 deletions .editorconfig

This file was deleted.

28 changes: 23 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
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
25 changes: 14 additions & 11 deletions EntityFrameworkCore.DataEncryption.sln
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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}
Expand Down
89 changes: 71 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
<h4 align="center">:warning: This project is **not** affiliated with Microsoft. :warning:</h4><br>

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

Expand All @@ -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
Expand Down Expand Up @@ -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<T>` 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<T>` 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<UserEntity> 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<UserEntity>();

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[].
}
}
```
Expand All @@ -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);
}
}
```
Expand Down
19 changes: 19 additions & 0 deletions samples/AesSample.Fluent/AesSample.Fluent.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.10" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\EntityFrameworkCore.DataEncryption\EntityFrameworkCore.DataEncryption.csproj" />
</ItemGroup>

</Project>
40 changes: 40 additions & 0 deletions samples/AesSample.Fluent/DatabaseContext.cs
Original file line number Diff line number Diff line change
@@ -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<UserEntity> Users { get; set; }

public DatabaseContext(DbContextOptions<DatabaseContext> options, IEncryptionProvider encryptionProvider)
: base(options)
{
_encryptionProvider = encryptionProvider;
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
EntityTypeBuilder<UserEntity> userEntityBuilder = modelBuilder.Entity<UserEntity>();

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);
}
}
11 changes: 11 additions & 0 deletions samples/AesSample.Fluent/EncryptedDatabaseContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Microsoft.EntityFrameworkCore;

namespace AesSample.Fluent;

public class EncryptedDatabaseContext : DatabaseContext
{
public EncryptedDatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options, null)
{
}
}
Loading

0 comments on commit 0beb736

Please sign in to comment.