EntityFrameworkCore.Crypto.DataEncryption
10.7.0
dotnet add package EntityFrameworkCore.Crypto.DataEncryption --version 10.7.0
NuGet\Install-Package EntityFrameworkCore.Crypto.DataEncryption -Version 10.7.0
<PackageReference Include="EntityFrameworkCore.Crypto.DataEncryption" Version="10.7.0" />
<PackageVersion Include="EntityFrameworkCore.Crypto.DataEncryption" Version="10.7.0" />
<PackageReference Include="EntityFrameworkCore.Crypto.DataEncryption" />
paket add EntityFrameworkCore.Crypto.DataEncryption --version 10.7.0
#r "nuget: EntityFrameworkCore.Crypto.DataEncryption, 10.7.0"
#:package EntityFrameworkCore.Crypto.DataEncryption@10.7.0
#addin nuget:?package=EntityFrameworkCore.Crypto.DataEncryption&version=10.7.0
#tool nuget:?package=EntityFrameworkCore.Crypto.DataEncryption&version=10.7.0
EntityFrameworkCore.Crypto.DataEncryption
Transparent field-level encryption for Entity Framework Core 10 on .NET 10. Works from C#, F# and Visual Basic (examples). Data is encrypted in your application process before it reaches the database, so the database, its administrators and its backups only ever see ciphertext. Keys never leave your process.
Website & licensing • Complete guide • Roadmap • Security
Install
dotnet add package EntityFrameworkCore.Crypto.DataEncryption
Quick start
Mark the properties to encrypt, then turn encryption on in OnModelCreating:
public class Customer
{
public int Id { get; set; }
[CryptoEncrypted]
public string Ssn { get; set; } = string.Empty;
[CryptoEncrypted]
public string Email { get; set; } = string.Empty;
[CryptoEncrypted]
[Column(TypeName = "TEXT")]
public decimal AnnualSalary { get; set; }
}
public class AppDbContext : DbContext
{
private readonly IEncryptionCryptoProvider _provider = new AesGcmCryptoProvider(LoadKeyFromSecureStore()); // 32-byte key
public DbSet<Customer> Customers => Set<Customer>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
=> modelBuilder.UseEncryption(_provider); // call last
}
EF Core encrypts on SaveChanges and decrypts when rows are read. Supported types: string, byte[], long, decimal, double, Dictionary<string, string>, JSON classes and owned entity types.
What you get
| Capability | Notes |
|---|---|
| AES-256-GCM authenticated encryption | Random nonce per value, tamper detection, key IDs and key rings for rotation. |
| Cloud KMS | Azure Key Vault, AWS KMS, HashiCorp Vault. |
| Searchable columns | Blind index for equality lookups (HasBlindIndex, WhereBlindEquals). |
| Substring search and sorting | Exact Contains / StartsWith / EndsWith and in-application sorting, plus WhereBlindIn for IN lists and per-column normalization (WithNormalization). Optional accent-insensitive matching (HasSearchIndex(true, accentInsensitive: true): jose finds José). The key is never sent to the database. |
| Fuzzy (typo-tolerant) search | WhereSimilar(c => c.Name, "Jhon Smith", maxEdits: 1) finds "John Smith". Exact (no match missed, none added) and uses the same search index. |
| Tolerant decryption | Opt-in: with new EncryptionOptions { OnDecryptionFailure = DecryptionFailurePolicy.ReturnNull }, a string, byte[] or JSON column whose value cannot be decrypted reads as null and is logged, and the query carries on. Numeric columns still throw. |
| Query guard | Queries that cannot work on ciphertext throw instead of silently returning wrong results. |
| Safe under load | One provider instance can be shared by every thread. 10.6.6 fixes a defect in earlier versions that made AES-GCM fail under concurrent load on Linux: details. |
| Batch re-encryption | Verified, resumable AES-CBC to AES-GCM migration and key rotation (EncryptionRotator). |
| Legacy migration bridge | MigrationCryptoProvider reads old AES-CBC data and writes AES-GCM. |
| Auto-encryption | UseAutoEncryption encrypts sensitive columns by naming convention. |
| Observability | Health checks, OpenTelemetry metrics. |
| Enterprise | PII scanner with ML classifier, Crypto Sentinel anomaly detection, email/webhook alerts, decryption audit trail, DPDP / GDPR compliance reports. |
Searching encrypted columns
// Equality: a keyed hash in an indexed shadow column
modelBuilder.Entity<Customer>().Property(c => c.Email).HasBlindIndex();
var customer = await context.Customers.WhereBlindEquals(nameof(Customer.Email), email, hasher).FirstOrDefaultAsync();
var customers = await context.Customers.WhereBlindIn(context, nameof(Customer.Email), emails, hasher).ToListAsync(); // IN list
// Substring search and sorting
modelBuilder.Entity<Customer>().Property(c => c.Ssn).HasSearchIndex();
var rows = await context.Customers.AsEncrypted(context, hasher)
.WhereContains(c => c.Ssn, "123")
.OrderBy(c => c.Ssn)
.ToListAsync(maxRows: 5_000);
Results are exact: the index narrows candidates in SQL and each one is confirmed against the decrypted value. Sorting runs in the application and needs a maxRows cap, so it throws instead of truncating. Contains needs a term of at least 3 characters. Existing rows become searchable after a one-time SearchIndexBackfill.
Measured on 300,000 rows (single runs, default Docker containers on one desktop, no tuning, no PostgreSQL trigram index):
| PostgreSQL 16 | SQL Server 2022 | MySQL 8.4 | |
|---|---|---|---|
Contains("marnia") → 69 rows |
77 ms | 111 ms | 358 ms |
StartsWith("MARIA") → 1,169 rows |
53 ms | 158 ms | 305 ms |
| SQL filter + sort, 30,000 rows | 168 ms | 468 ms | 495 ms |
Verified on SQL Server 2022, PostgreSQL 16, MySQL 8.4 and SQLite with EF Core 10: results match the plaintext answer exactly (including %, _, accents and mixed case), sort order with nulls, upgrade of legacy CBC data, CBC to GCM and key rotation, and no plaintext or keys ever appear in the database server logs.
Setup, limits and method: search and re-encryption guide. Everything else, including KMS, scanning, alerts and compliance: full reference.
Licensing
Free for evaluation, open source and projects under $2,000/yr revenue (Community). Above that, every paid plan unlocks the full platform, priced per organization with no per-seat counting:
| Plan | Price | Support |
|---|---|---|
| Community | Free, under $2,000 annual revenue | Community |
| Enterprise Annual | $2,999 / year, unlimited developers and projects | Priority, under 1 business day |
| Enterprise 3-Year | $5,999 prepaid, one payment | Priority, 1-hour response |
| Enterprise 5-Year | $6,999 prepaid, one payment | Priority, 1-hour response |
Field encryption, key rotation, cloud KMS, blind indexing, search and auto-encryption are in Community. The ML classifier, Crypto Sentinel, alerts, audit trail and compliance reports need an Enterprise key. Plans and contact form: efcore-encryption.com/#plans.
Local development is never blocked or nagged. On a developer machine every feature works with or without a key, and no license message or error is shown. The license is checked only if you call AddDataEncryptionML(...); modelBuilder.UseEncryption(...), search and re-encryption on their own never check it. When you deploy with AddDataEncryptionML and no valid key, the host throws LicenseValidationException at startup in Kubernetes, managed cloud, a container (unless the environment is Development), or when the environment is Production or Staging. Set options.Enforcement.Mode = LicenseEnforcementMode.WarnOnly (or the environment variable EFCRYPTOML_LICENSE_ENFORCEMENT=WarnOnly) to log a warning instead of failing; paid features then stay off until a key is added.
Support
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- MailKit (>= 4.18.2)
- Microsoft.EntityFrameworkCore (>= 10.0.14)
- Microsoft.Extensions.Caching.Memory (>= 10.0.14)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Http (>= 10.0.14)
- Microsoft.Extensions.Options.DataAnnotations (>= 10.0.14)
- Microsoft.ML (>= 5.0.1)
- Microsoft.ML.TimeSeries (>= 5.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 10.7.0 | 69 | 11/12/2026 | |
| 10.6.6 | 75 | 11/11/2026 | |
| 10.6.5 | 182 | 10/5/2026 | |
| 10.6.0 | 257 | 9/10/2026 | |
| 10.5.0 | 146 | 9/8/2026 | |
| 10.4.0 | 86 | 11/5/2026 | |
| 10.3.0 | 85 | 11/5/2026 | |
| 10.2.0 | 90 | 11/5/2026 | |
| 10.1.0 | 84 | 11/5/2026 | |
| 10.0.1 | 4,967 | 11/12/2025 | |
| 10.0.0 | 5,006 | 11/12/2025 | |
| 9.0.0 | 1,130 | 6/23/2025 | |
| 8.0.3 | 69,428 | 5/29/2024 | |
| 8.0.2 | 456 | 5/27/2024 | |
| 8.0.1 | 432 | 5/27/2024 | |
| 8.0.0 | 453 | 5/24/2024 |
Version 10.7.0 for .NET 10 and EF Core 10
Field-level encryption for EF Core that your application, your team and your auditors can rely on: searchable, fast, safe under load, and now for every .NET language.
WHAT'S NEW
- F# and Visual Basic, first class. Use the library from C#, F# or Visual Basic, with examples for each in the documentation. Write your own encryption provider in any of them with the new EncryptionCryptoProviderBase class: override Encrypt and Decrypt and you are done.
- Accent-insensitive search. HasSearchIndex(caseInsensitive: true, accentInsensitive: true) and a search for "jose" finds "José", "nguyen" finds "Nguyễn". Your stored data keeps its accents.
- Typo-tolerant search. WhereSimilar(c => c.Name, "Jhon Smith", maxEdits: 1) finds "John Smith". Results are exact: nothing that matches is missed, nothing that doesn't is returned.
- AnyAsync and FirstOrDefaultAsync on encrypted searches. They stop at the first confirmed match instead of loading every row.
- SearchIndexBackfill.RebuildAsync. Re-index a table after changing the index key or search options, in batches, safely resumable, touching only the rows that change.
- A smarter query guard. It now also catches GroupBy, Join, Distinct, Equals and aggregates on encrypted columns, and the comparison operators Visual Basic generates. Queries that cannot work on ciphertext fail fast with a clear message instead of quietly returning the wrong answer.
FASTER
- Search indexing: 2x faster, 16x less memory per value.
- Search queries and fuzzy matching: 2x to 2.7x faster.
- Blind-index lookups: 35% faster, 60% less memory.
- Reading legacy AES-CBC data: 12x faster.
- One shared AES-GCM provider now handles 5.5 million encrypt-and-decrypt operations per second across 12 cores, up from 3.4 million.
- SaveChanges does less work per entity, and index backfill keeps the same speed from the first row to the last, however large the table.
BUILT FOR PRODUCTION LOAD
- One provider instance is safe to share across every thread, on Windows and on Linux and in containers. Register it once and use it everywhere.
- Important for Linux and container deployments: in earlier versions, a single AES-GCM provider used by many threads at the same moment on Linux could fail, and in rare cases return incorrect data. This release fixes it. If you run on Linux or in containers, upgrade.
- Clearer errors: the AES-CBC migration provider now says why a value cannot be decrypted, and a failed rollback during key rotation can no longer hide the original error.
- Search index keys are held more tightly: no second copy of the key in memory, and buffers that held plaintext are wiped after use.
ALSO INCLUDED
- Exact Contains, StartsWith and EndsWith search on encrypted columns, with the key never sent to the database.
- In-application sorting of encrypted columns with culture and null ordering, protected by a row limit that never silently truncates.
- Blind-index equality and IN-list lookups (WhereBlindEquals, WhereBlindIn) and per-column normalization.
- EncryptionRotator: verified, resumable re-encryption for key rotation and AES-CBC to AES-GCM migration, with no downtime.
- Tolerant decryption: optionally read an unreadable value as null, log it without any data, and keep the query running.
- Cloud key management (Azure Key Vault, AWS KMS, HashiCorp Vault), convention-based auto-encryption, health checks and OpenTelemetry metrics.
- Enterprise: ML-powered PII discovery, Crypto Sentinel exfiltration detection, email and webhook alerts, decryption audit trail, and GDPR / DPDP compliance reports.
UPGRADING
- Drop-in upgrade: no public API was removed or changed, and no data migration is needed. Everything already encrypted, and every existing search and blind index, keeps working as it is.
- The query guard is stricter. If a query now raises EncryptedQueryNotSupportedException, it was returning wrong results before; the message tells you which method to use instead.
- Visual Basic users: comparing an encrypted column with =, <> or Like in a query now raises a clear error instead of silently returning no rows. Switch those queries to WhereBlindEquals, WhereContains or the other search methods.
- Turning on accent-insensitive search for existing data: run SearchIndexBackfill.RebuildAsync once.
- Requires .NET 10 and Microsoft.EntityFrameworkCore 10.0.14 or later.
LICENSING
- Free Community edition for projects under $2,000 annual revenue: field encryption, key rotation, cloud KMS, blind indexing, search and auto-encryption.
- Enterprise adds ML PII discovery, Crypto Sentinel, alerting, the audit trail and compliance reports, with unlimited developers and projects: Annual $2,999, 3-Year $5,999, 5-Year $6,999.
- Develop freely: on a developer machine every feature works without a key and nothing nags you. Deployed environments validate the key at startup, fully offline. See https://efcore-encryption.com/#plans
Documentation: https://efcore-encryption.com/docs