EntityFrameworkCore.Crypto.DataEncryption 10.3.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package EntityFrameworkCore.Crypto.DataEncryption --version 10.3.0
                    
NuGet\Install-Package EntityFrameworkCore.Crypto.DataEncryption -Version 10.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EntityFrameworkCore.Crypto.DataEncryption" Version="10.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EntityFrameworkCore.Crypto.DataEncryption" Version="10.3.0" />
                    
Directory.Packages.props
<PackageReference Include="EntityFrameworkCore.Crypto.DataEncryption" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EntityFrameworkCore.Crypto.DataEncryption --version 10.3.0
                    
#r "nuget: EntityFrameworkCore.Crypto.DataEncryption, 10.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EntityFrameworkCore.Crypto.DataEncryption@10.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EntityFrameworkCore.Crypto.DataEncryption&version=10.3.0
                    
Install as a Cake Addin
#tool nuget:?package=EntityFrameworkCore.Crypto.DataEncryption&version=10.3.0
                    
Install as a Cake Tool

EntityFrameworkCore.Crypto.DataEncryption

.NET NuGet Downloads License: Commercial / Community Cryptography: AES-256-GCM AEAD In-Process EF Core GDPR Art. 32 DPDP 2023 §8 PCI-DSS

Transparent field-level encryption for Entity Framework Core 8, 9 and 10. 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). The key is never sent to the database.
Query guard Queries that cannot work on ciphertext throw instead of silently returning wrong results.
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

Tested on all three databases and SQLite: 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 in the PostgreSQL and MySQL server logs. Not measured: concurrent load and tables over 300,000 rows.

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

Website • Documentation • Security policy • Issues

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.6.5 162 10/5/2026
10.6.0 242 9/10/2026
10.5.0 145 9/8/2026
10.3.0 0 11/5/2026
10.2.0 0 11/5/2026
10.1.0 0 11/5/2026
10.0.1 4,889 11/12/2025
10.0.0 4,697 11/12/2025 10.0.0 is deprecated because it has critical bugs.
9.0.0 1,015 6/23/2025
8.0.3 69,384 5/29/2024
8.0.2 456 5/27/2024 8.0.2 is deprecated because it is no longer maintained and has critical bugs.
8.0.1 430 5/27/2024 8.0.1 is deprecated because it is no longer maintained and has critical bugs.
8.0.0 453 5/24/2024 8.0.0 is deprecated because it is no longer maintained.

Version 10.3.0

WHAT CHANGED SINCE 10.2.0
- Licensing validation is back. 10.2.0 had it switched off, so it granted every feature with no key. 10.3.0 enforces it again, with one change from 10.1.0 and earlier: on a local development machine nothing is checked or logged, and every feature works with no key (before, a developer machine got a startup warning and the paid features were switched off). See LICENSE BEHAVIOR below.

CORRECTION: CHANGES IN 10.2.0 THAT ITS NOTES DID NOT LIST (the 10.2.0 notes repeated the 10.1.0 notes)
- New WhereBlindIn method for IN-list lookups on a blind-indexed column.
- Stricter query guard: with UseEncryptedQueryGuard, equality (==, !=) and IN-list lookups (list.Contains) on a randomized (AES-GCM) encrypted column now throw EncryptedQueryNotSupportedException, because they silently matched no rows. Null checks, and equality on a deterministic provider such as the legacy fixed-IV AesCryptoProvider, are still allowed.
- EncryptedQueryNotSupportedException constructor gained an optional fourth parameter, hint. Code that only catches the exception is unaffected. Code that constructs it must be recompiled (binary-incompatible change, source-compatible).
- Fixed: MigrationCryptoProvider could fail to read about 1 in 256 legacy AES-CBC values whose first bytes looked like a GCM envelope with an unregistered key ID. It now tries the legacy decryptor first, and still reports a genuinely missing key.
- Dependencies updated (see DEPENDENCIES).

BREAKING CHANGES
- Since 10.2.0: licensing enforcement returns (see LICENSE BEHAVIOR). An app that calls AddDataEncryptionML without a valid key will now fail at startup in production-like environments, as it did before 10.2.0.
- Since 10.1.0: LicenseEnforcer.Enforce returns a new value, Outcome.AllowedInDevelopment, for an unlicensed local development machine under Auto mode (it used to return AllowedWithWarning). Code that switched on that result needs a new case.
- Since 10.1.0: the guard change and the exception constructor change above. The net10.0 package requires Microsoft.EntityFrameworkCore 10.0.14 or later.

NEW AND REMOVED APIS
- 10.3.0 added: LicenseEnforcer.Outcome.AllowedInDevelopment, LicenseGate.IsDevelopmentMode, and an optional developmentMode parameter on the LicenseGate constructor. Removed: none.
- Added in 10.2.0: WhereBlindIn; optional hint parameter on EncryptedQueryNotSupportedException. Removed: none.
- Also present in the 10.1.0 and 10.2.0 line: HasSearchIndex, AsEncrypted (WhereContains, WhereStartsWith, WhereEndsWith, OrderBy, ThenBy), EncryptedSortOptions, SearchIndexBackfill, UseEncryptedQueryGuard, WithNormalization and NormalizeForQuery, EncryptionRotator with RotationOptions and checkpoint stores.

DEPENDENCIES (unchanged in 10.3.0; changed in 10.2.0 compared with 10.1.0)
- Microsoft.EntityFrameworkCore and Microsoft.Extensions.* (net10.0): 10.0.11 to 10.0.14. The net8.0 and net9.0 builds use the newest 8.0.* and 9.0.* patch releases.
- MailKit 4.17.0 to 4.18.2. Microsoft.ML and Microsoft.ML.TimeSeries 5.0.0 to 5.0.1.
- Security: MailKit 4.18.2 brings in System.Security.Cryptography.Pkcs 10.0.13, which fixes advisories GHSA-846f-f764-76mx (high severity) and GHSA-2fmv-rv2c-59x7 (moderate) that affected the Pkcs version used by earlier releases.

STORED DATA FORMAT
- Unchanged. Values written by earlier versions, AES-GCM or legacy AES-CBC, read exactly as before. Nothing needs re-encrypting to upgrade.
- HasSearchIndex adds an optional nullable column named after the property plus SearchIndex. It needs a database migration and a one-time SearchIndexBackfill for existing rows. WhereBlindIn uses the existing blind-index column and needs no schema change.

LICENSE BEHAVIOR
- The license is checked only when AddDataEncryptionML is called. UseEncryption, blind index, search, sorting and EncryptionRotator never check it.
- Community needs no key: field encryption, key rotation, cloud KMS, blind indexing, search and auto-encryption. Enterprise needs a key: ML classifier, Crypto Sentinel, email and webhook alerts, custom alert channels, decryption audit trail, compliance reports.
- Local development (default mode Auto): everything works with or without a key, every feature is available, and no license warning or error is shown.
- Deployed (Auto): without a valid key it throws LicenseValidationException at startup in Kubernetes, managed cloud, a container (unless the environment is Development), or when the environment is Production or Staging.
- To log a warning instead of failing, set Enforcement.Mode to WarnOnly in the options, or set the environment variable EFCRYPTOML_LICENSE_ENFORCEMENT to WarnOnly. Paid features then stay off until a key is added. Strict mode fails everywhere, including a developer machine.
- Plans: Community (free, under $2,000 annual revenue), Enterprise Annual ($2,999/yr), Enterprise 3-Year ($5,999 prepaid), Enterprise 5-Year ($6,999 prepaid), all Enterprise plans with unlimited developers and projects. See https://efcore-encryption.com/#plans

KNOWN LIMITS
- Sorting an encrypted column runs in the application and needs a maxRows cap. If more rows match, it throws and never truncates.
- Contains search needs a term of at least 3 characters. StartsWith and EndsWith accept shorter terms.
- The search index reveals which rows share short fragments of text, not the text itself. Use a dedicated blind-index key.
- Existing rows are not searchable until SearchIndexBackfill has run. On PostgreSQL a trigram GIN index on the search column is recommended; the published timings were measured without one.
- EncryptionRotator needs a single-column primary key and a concurrency token (or AllowWithoutConcurrencyToken), and stops on a row that cannot be decrypted.
- Unicode normalization of search terms is not performed. Not tested: concurrent load, tables over 300,000 rows, MariaDB, managed cloud databases. The real-database tests ran on .NET 10 against PostgreSQL 16, MySQL 8.4 and SQL Server 2022.

Roadmap: https://github.com/efcore-encryption/EntityFrameworkCore.Crypto.DataEncryption/blob/main/ROADMAP.md