abpframework/abp · critical · PlatformNotSupportedException
AES-GCM is not available on .NET Standard 2.0!
Error message
AES-GCM is not available on .NET Standard 2.0!
What it means
BLOB encryption is built on AES-GCM in chunk mode, and AES-GCM has no implementation on .NET Standard 2.0. CreateChunkCipher is the single entry point that constructs the per-BLOB AesGcm instance; on netstandard2.0 it throws PlatformNotSupportedException before any crypto runs. The method is typed as IDisposable so the calling streams still compile on netstandard2.0.
Source
Thrown at framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobEncryptionCodec.cs:366
#else
using var password = new Rfc2898DeriveBytes(passwordBytes, salt, iterations, HashAlgorithmName.SHA256);
return password.GetBytes(32);
#endif
}
finally
{
CryptographicOperations.ZeroMemory(passwordBytes);
}
#endif
}
// One AES-GCM instance is bound to the per-BLOB key and reused for every chunk, so a
// stream sets up the key schedule once instead of per chunk. Typed as IDisposable so the
// streams that hold it still compile on netstandard2.0 (where creation throws first).
internal static IDisposable CreateChunkCipher(byte[] keyBytes)
{
#if NETSTANDARD2_0
throw new PlatformNotSupportedException("AES-GCM is not available on .NET Standard 2.0!");
#else
return CreateAesGcm(keyBytes);
#endif
}
internal static byte[] EncryptChunk(byte[] keyBytes, byte[] associatedDataPrefix, byte[] baseNonce, int chunkIndex, byte[] plainChunk, int plainChunkLength)
{
using (var cipher = CreateChunkCipher(keyBytes))
{
return EncryptChunkCore(cipher, CreateChunkAssociatedData(associatedDataPrefix, chunkIndex), CreateChunkNonce(baseNonce, chunkIndex), plainChunk, plainChunkLength);
}
}
// The cipher, associated data and nonce are passed in fully built so the streams can reuse
// one of each and only rewrite the trailing chunk index, instead of reconstructing the
// AES-GCM key schedule and reallocating the whole identity (which grows with the
// container/BLOB name) for every chunk
internal static byte[] EncryptChunkCore(IDisposable cipher, byte[] associatedData, byte[] nonce, byte[] plainChunk, int plainChunkLength)View on GitHub (pinned to 7ed43b1931)
Solutions
- Retarget the project (or add a net6.0/net8.0 TFM in a multi-target) so the AES-GCM code path is selected at runtime.
- Disable BLOB encryption for the container when running on netstandard2.0 (do not call UseEncryption, or gate it on the target framework).
- Move encryption to a net6.0+ service/host that consumes the netstandard2.0 storage library and stores plaintext-free blobs.
- Keep the storage library on netstandard2.0 but put a net6.0+ companion package in charge of encryption.
Example fix
// before — encryption enabled unconditionally
Configure<AbpBlobStoringOptions>(o =>
o.Containers.Configure<UserContainer>(c =>
c.UseEncryption()));
// after — gate on a TFM where AES-GCM exists
#if !NETSTANDARD2_0
Configure<AbpBlobStoringOptions>(o =>
o.Containers.Configure<UserContainer>(c =>
c.UseEncryption()));
#endif Defensive patterns
Strategy: validation
Validate before calling
// Detect netstandard2.0 at startup and refuse to enable encryption there.
static bool AesGcmAvailable =>
#if NETSTANDARD2_0
false;
#else
true;
#endif
if (!AesGcmAvailable)
throw new PlatformNotSupportedException(
"BLOB encryption requires a TFM with AES-GCM (net6.0+); this build is netstandard2.0."); Type guard
// Compile-time guard so the encryption call sites simply do not exist on netstandard2.0.
#if !NETSTANDARD2_0
public static void EnableEncryption(BlobContainerConfiguration c) => c.UseEncryption();
#else
public static void EnableEncryption(BlobContainerConfiguration c) =>
throw new PlatformNotSupportedException("Re-target to net6.0+ for BLOB encryption.");
#endif Try / catch
try
{
EnableEncryption(containerConfig);
}
catch (PlatformNotSupportedException ex) when (ex.Message.Contains(".NET Standard 2.0"))
{
logger.LogCritical(ex, "Cannot enable encryption on this TFM; deploy on net6.0+.");
throw;
} Prevention
- Multi-target shared libraries and run crypto on a net6.0+ TFM.
- Add a startup check that fails fast if encryption is requested on a build without AES-GCM.
- Keep encryption configuration out of netstandard2.0-only projects.
- Document the AES-GCM TFM requirement wherever encryption is enabled.
When it happens
Trigger: Running or unit-testing code that calls BlobEncryptionCodec.CreateChunkCipher (directly, or via the encrypting/decrypting streams that construct it in their ctor) while the executing assembly resolves to the netstandard2.0 TFM.
Common situations: A shared/legacy library targets only netstandard2.0 and enables BLOB encryption; an ABP upgrade pulled in encryption defaults on a project that still multi-targets down to netstandard2.0; tests run against the netstandard2.0 build.
Related errors
- The data is too large: the maximum chunk count has been exce
- The BLOB encryption passphrase contains invalid characters (
- The encrypted BLOB is corrupted or has an invalid format: mi
- The encrypted BLOB is corrupted or has an invalid format: tr
- The encrypted BLOB is corrupted or has an invalid format: in
AI-assisted analysis of abpframework/abp@7ed43b1931 (2026-08-13).
Data as JSON: /api/errors/36d9e9c3b7b74d88.
Report an issue: GitHub.