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

  1. 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.
  2. Disable BLOB encryption for the container when running on netstandard2.0 (do not call UseEncryption, or gate it on the target framework).
  3. Move encryption to a net6.0+ service/host that consumes the netstandard2.0 storage library and stores plaintext-free blobs.
  4. 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

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


AI-assisted analysis of abpframework/abp@7ed43b1931 (2026-08-13). Data as JSON: /api/errors/36d9e9c3b7b74d88. Report an issue: GitHub.