-
-
Save albertbori/e95860644e69c1572441 to your computer and use it in GitHub Desktop.
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 | |
} |
How do you set custom attributes (such as "encrypted"), in a way that is persistent when the entity is regenerated?
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.
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?
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.
Thanks I got it.
add a class
class Encrypted : Attribute
{
}
[Encrypted] // here setting up custom attribute for encrypted Property
public string EncryptedProperty { get; set; }
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).
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.
how does search in encrypted columns work in this solution?
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
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);
}
}
how does search (like search )in encrypted columns work in this solution?
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.
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();
It's not working. I can't create provider and can't make migration.
Something wrong with this code and attribute.
modelBuilder.UseEncryption(this._provider); - this._provider always null while attempt to create migration.
Version 3.0 is on the way. It will fix the issue : SoftFluent/EntityFrameworkCore.DataEncryption#25
Thanks. I will try tomorrow :) This needs for me
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
This is beautiful...so clean and simple...thank you!!!