I've been trying and failing to figure out a good approach to encrypting SQL data with the Entity Framework Code First. I must preface this with that I am hosting in Azure and do not have access to native SQL encryption.
Taking a page from SecurEntity, I have fully implemented an approach that utilizes SaveChanges and ObjectMaterialized to handle the encrypting/decrypting of the entities, but in testing I have found this has been far too unreliable to use.
Here is a sample of some of the implementation:
public override int SaveChanges()
{
var pendingEntities = ((IObjectContextAdapter)this).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;
}
void ObjectMaterialized(object sender, ObjectMaterializedEventArgs e)
{
DecryptEntity(e.Entity);
}
I've seen other posts that manually encrypt/decrypt via secondary properties, like so:
public Value { get; set; }
[NotMapped]
public DecryptedValue
{
get { return Decrypt(this.Value); }
set { this.Value = Encrypt(value); }
}
This will most definitely work, but I find this approach to be... less than ideal. When using this approach, all the developers have to wade through all the encrypted properties to find which ones they can use.
The most ideal solution would be for me to be able to override the getting/setting of each value at the data-access level. Is there a way to do this? If not, how can I implement data encryption with Entity Framework - Code First so that it will be easy to maintain and work with?
You need to use attribute as below for the columns you want to keep the data encrypted on your database. The “EncryptColumn” parameter in the “EntityFrameworkCore. EncryptColumn. Attribute” class will be used.
Encrypt your connection to protect sensitive data.The Entity Framework does not directly handle data encryption. If users access data over a public network, your application should establish an encrypted connection to the data source to increase security.
To create an encrypted data set, a key label must be supplied on new data set allocation. The key label must point to an AES-256 bit encryption DATA key within the ICSF key repository (CKDS) to be used to encrypt or decrypt the data. For each encrypted data set, its key label is stored in the catalog.
I have good news. The instability I was experiencing with the SaveChanges/ObjectMaterialized approach was due to the fact that DetectChanges()
isn't called until the DbContext actually performs the save.
I was able to fix this by calling DetectChanges()
before I pulled the Added/Modified records from the ObjectStateManager
. This cleared up any object state oddities that were causing inconsistent encryption behavior.
The resulting code being:
public override int SaveChanges()
{
var contextAdapter = ((IObjectContextAdapter)this);
contextAdapter.ObjectContext.DetectChanges();
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;
}
EDIT - Added a sample DataContext to see my end-to-end solution for encrypting all entities. Note: You don't have to use a custom attribute for encrypting properties. Source
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With