Skip to content

Instantly share code, notes, and snippets.

@albertbori
Last active June 13, 2023 18:11
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save albertbori/e95860644e69c1572441 to your computer and use it in GitHub Desktop.
Save albertbori/e95860644e69c1572441 to your computer and use it in GitHub Desktop.
.NET EntityFramework Encryption Example (C#)
public class MyDB : IdentityDbContext<User>
{
//DBSet properties go here
public MyDB()
{
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += new ObjectMaterializedEventHandler(ObjectMaterialized);
}
#region Encryption
public override int SaveChanges()
{
var contextAdapter = ((IObjectContextAdapter)this);
contextAdapter.ObjectContext.DetectChanges(); //force this. Sometimes entity state needs a handle jiggle
var pendingEntities = contextAdapter.ObjectContext.ObjectStateManager
.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
.Where(en => !en.IsRelationship).ToList();
foreach (var entry in pendingEntities) //Encrypt all pending changes
EncryptEntity(entry.Entity);
int result = base.SaveChanges();
foreach (var entry in pendingEntities) //Decrypt updated entities for continued use
DecryptEntity(entry.Entity);
return result;
}
public override async Task<int> SaveChangesAsync(System.Threading.CancellationToken cancellationToken)
{
var contextAdapter = ((IObjectContextAdapter)this);
contextAdapter.ObjectContext.DetectChanges(); //force this. Sometimes entity state needs a handle jiggle
var pendingEntities = contextAdapter.ObjectContext.ObjectStateManager
.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
.Where(en => !en.IsRelationship).ToList();
foreach (var entry in pendingEntities) //Encrypt all pending changes
EncryptEntity(entry.Entity);
var result = await base.SaveChangesAsync(cancellationToken);
foreach (var entry in pendingEntities) //Decrypt updated entities for continued use
DecryptEntity(entry.Entity);
return result;
}
void ObjectMaterialized(object sender, ObjectMaterializedEventArgs e)
{
DecryptEntity(e.Entity);
}
private void EncryptEntity(object entity)
{
//Get all the properties that are encryptable and encrypt them
var encryptedProperties = entity.GetType().GetProperties()
.Where(p => p.GetCustomAttributes(typeof(Encrypted), true).Any(a => p.PropertyType == typeof(String)));
foreach (var property in encryptedProperties)
{
string value = property.GetValue(entity) as string;
if (!String.IsNullOrEmpty(value))
{
string encryptedValue = EncryptionService.Encrypt(value);
property.SetValue(entity, encryptedValue);
}
}
}
private void DecryptEntity(object entity)
{
//Get all the properties that are encryptable and decyrpt them
var encryptedProperties = entity.GetType().GetProperties()
.Where(p => p.GetCustomAttributes(typeof(Encrypted), true).Any(a => p.PropertyType == typeof(String)));
foreach (var property in encryptedProperties)
{
string encryptedValue = property.GetValue(entity) as string;
if (!String.IsNullOrEmpty(encryptedValue))
{
string value = EncryptionService.Decrypt(encryptedValue);
this.Entry(entity).Property(property.Name).OriginalValue = value;
this.Entry(entity).Property(property.Name).IsModified = false;
}
}
}
#endregion Encryption
}
@billymccafferty
Copy link

This is beautiful...so clean and simple...thank you!!!

@everythingEzra2
Copy link

How do you set custom attributes (such as "encrypted"), in a way that is persistent when the entity is regenerated?

@crazyboybert
Copy link

Persisting attributes on entities is best done by putting the attribute on a metadata type specified by the MetadataTypeAttribute on a partial of the entity. This is the same approach as used for setting validation attributes etc. The code above needs slight update to EncryptEntity and DecryptEntity to find the metadata type, search its properties for those that are encrypted, and then find the matching properties on the entity type to work with.

@mikeolgen
Copy link

I'm trying to find a solution to encrypt column data with Entity Framework using SQL Server 2012. It appears you have an EncryptionService class with Encrypt and Decrypt methods. Can you post code for that?

@mumairanwaar
Copy link

How do you set custom attributes (such as "encrypted"), in a way that is persistent when the entity is regenerated?
Will you please show us a sample code where you set the property attribute Encrypted.

@mumairanwaar
Copy link

Thanks I got it.
add a class
class Encrypted : Attribute
{

}

[Encrypted] // here setting up custom attribute for encrypted Property
public string EncryptedProperty { get; set; }

@nanowebcoder
Copy link

I'd like to use this, but I'm not sure what .NET version this requires (or EF version) and without "using" statements I am having a hard time to get code to compile as I seem to be missing a number of references (IObjectContextAdapter particularly).

@nigelbogle
Copy link

Looks like a good solution. Can you please clarify 'what' or 'where' EncryptionServices relates to?

i.e. in the following line[69]: string encryptedValue = EncryptionService.Encrypt(value);

Thanks & Best Wishes,
N.

@Daweczp
Copy link

Daweczp commented Jun 11, 2018

how does search in encrypted columns work in this solution?

@BarbeNoire
Copy link

BarbeNoire commented Apr 25, 2019

Hi,

Thank you for this solution, but it don't fit to my case as I'm using EF proxy and Custom Attributes on the POCO are not transfer to the proxy.
Could you, please, show a way to achieve this?

Thanks in advance.

Thank you

@Eastrall
Copy link

Eastrall commented Nov 7, 2019

I have developed an Entity Framework Core plugin that handles data encryption of a string field using a custom attribute. Actually there is only the AES encryption provider available, but you can easily implement new encryption providers. Check it out here: https://github.com/Eastrall/EntityFrameworkCore.DataEncryption

Also, it's compatible with EF Core 2 and 3.

Quick example:

public class UserEntity
{
	public int Id { get; set; }
	
	[Encrypted]
	public string Username { get; set; }
	
	[Encrypted]
	public string Password { get; set; }
	
	public int Age { get; set; }
}

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)
	{
		this._provider = new AesProvider(this._encryptionKey, this._encryptionIV);
	}
	
	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		modelBuilder.UseEncryption(this._provider);
	}
}

@deepanmuthukrishnan
Copy link

how does search (like search )in encrypted columns work in this solution?

@Eastrall
Copy link

When you define a string property as [Encrypted], the DataEncryption extension will save the encrypted data into the column instead of the raw data.
When you read the data from your code, it will appear as raw data instead of encrypted. So you can easily do your search through code.

@deepanmuthukrishnan
Copy link

Im using MyDB.cs code
Below is my code for search. But still not able to filter
transactions = db.OverseasAttractionTransactions.Where(t => t.CrOrderNumber.ToLower().Contains(searchKeyword) || t.CustomerEmail.ToLower().Contains(searchKeyword) ||
(t.CustomerFirstName).ToLower().Contains(searchKeyword) || t.CustomerLastName.ToLower().Contains(searchKeyword) ||
t.CustomerNricOrPassport.ToLower().Contains(searchKeyword) || t.CustomerPhoneNumber.ToLower().Contains(searchKeyword) ||
t.DiscountCode.ToLower().Contains(searchKeyword) || t.ReferenceNumber.ToLower().Contains(searchKeyword))
.OrderBy(t => t.PurchaseDateTime).ToList();

@dankodima1
Copy link

It's not working. I can't create provider and can't make migration.

@dankodima1
Copy link

Something wrong with this code and attribute.

@dankodima1
Copy link

modelBuilder.UseEncryption(this._provider); - this._provider always null while attempt to create migration.

@Eastrall
Copy link

Eastrall commented Apr 6, 2021

Version 3.0 is on the way. It will fix the issue : Eastrall/EntityFrameworkCore.DataEncryption#25

@dankodima1
Copy link

Thanks. I will try tomorrow :) This needs for me

@naveedtar
Copy link

Im using MyDB.cs code Below is my code for search. But still not able to filter transactions = db.OverseasAttractionTransactions.Where(t => t.CrOrderNumber.ToLower().Contains(searchKeyword) || t.CustomerEmail.ToLower().Contains(searchKeyword) || (t.CustomerFirstName).ToLower().Contains(searchKeyword) || t.CustomerLastName.ToLower().Contains(searchKeyword) || t.CustomerNricOrPassport.ToLower().Contains(searchKeyword) || t.CustomerPhoneNumber.ToLower().Contains(searchKeyword) || t.DiscountCode.ToLower().Contains(searchKeyword) || t.ReferenceNumber.ToLower().Contains(searchKeyword)) .OrderBy(t => t.PurchaseDateTime).ToList();

@Eastrall can you answer this question please i cant search on contains filter

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment