| name | dotnet-cryptography |
| description | Selects crypto algorithms and usage. Hashing, AES-GCM, RSA, ECDSA, PQC key derivation. |
| allowed-tools | ["Read","Grep","Glob","Bash","Write","Edit"] |
dotnet-cryptography
Modern .NET cryptography covering hashing (SHA-256/384/512), symmetric encryption (AES-GCM), asymmetric cryptography
(RSA, ECDSA), key derivation (PBKDF2, Argon2), and post-quantum algorithms (ML-KEM, ML-DSA, SLH-DSA) for .NET 10+.
Includes TFM-aware guidance: what's available on net10.0 vs fallback strategies for net8.0/net9.0.
Scope
- Algorithm selection and correct usage of System.Security.Cryptography APIs
- Hashing for integrity (SHA-256/384/512)
- Symmetric encryption (AES-GCM)
- Asymmetric cryptography (RSA, ECDSA)
- Key derivation (PBKDF2, Argon2)
- Post-quantum cryptography (ML-KEM, ML-DSA, SLH-DSA) for .NET 10+
- Deprecated algorithm warnings
Out of scope
- Secrets management and configuration binding -- see [skill:dotnet-secrets-management]
- OWASP vulnerability categories and deprecated security patterns -- see [skill:dotnet-security-owasp]
- Authentication/authorization implementation (JWT, OAuth, Identity) -- see [skill:dotnet-api-security] and
[skill:dotnet-blazor-auth]
- Cloud-specific key management (Azure Key Vault, AWS KMS) -- see [skill:dotnet-advisor]
- TLS/HTTPS configuration -- see [skill:dotnet-advisor]
Cross-references: [skill:dotnet-security-owasp] for OWASP A02 (Cryptographic Failures) and deprecated pattern warnings,
[skill:dotnet-secrets-management] for storing keys and secrets securely.
Prerequisites
- .NET 8.0+ (LTS baseline for classical algorithms)
- .NET 10.0+ for post-quantum algorithms (ML-KEM, ML-DSA, SLH-DSA)
- Platform support for PQC: Windows 11 (November 2025+) or OpenSSL 3.5+ on Linux/macOS
Hashing (SHA-2 Family)
Use SHA-256/384/512 for integrity verification, checksums, and content-addressable storage. Never use hashing alone for
passwords (see Key Derivation below).
using System.Security.Cryptography;
byte[] data = "Hello, world"u8.ToArray();
byte[] hash = SHA256.HashData(data);
await using var stream = File.OpenRead("largefile.bin");
byte[] fileHash = await SHA256.HashDataAsync(stream);
bool isEqual = CryptographicOperations.FixedTimeEquals(hash1, hash2);
```text
```csharp
byte[] key = RandomNumberGenerator.GetBytes(32);
byte[] mac = HMACSHA256.HashData(key, data);
byte[] computedMac = HMACSHA256.HashData(key, receivedData);
if (!CryptographicOperations.FixedTimeEquals(mac, computedMac))
{
throw new CryptographicException("Message authentication failed");
}
```text
---
## Symmetric Encryption (AES-GCM)
AES-GCM is the recommended symmetric encryption for .NET. It provides both confidentiality and authenticity
(authenticated encryption with associated data -- AEAD).
```csharp
using System.Security.Cryptography;
public static class AesGcmEncryptor
{
private const int NonceSize = 12;
TagSize = ;
{
nonce = RandomNumberGenerator.GetBytes(NonceSize);
ciphertext = [plaintext.Length];
tag = [TagSize];
aes = AesGcm(key, TagSize);
aes.Encrypt(nonce, plaintext, ciphertext, tag);
result = [NonceSize + ciphertext.Length + TagSize];
nonce.CopyTo(result, );
ciphertext.CopyTo(result, NonceSize);
tag.CopyTo(result, NonceSize + ciphertext.Length);
result;
}
{
nonce = encryptedData.AsSpan(, NonceSize);
ciphertext = encryptedData.AsSpan(NonceSize, encryptedData.Length - NonceSize - TagSize);
tag = encryptedData.AsSpan(encryptedData.Length - TagSize);
plaintext = [ciphertext.Length];
aes = AesGcm(key, TagSize);
aes.Decrypt(nonce, ciphertext, tag, plaintext);
plaintext;
}
}
```text
```csharp
Microsoft.AspNetCore.DataProtection;
{
IDataProtector _protector =
provider.CreateProtector();
=> _protector.Protect(plaintext);
=> _protector.Unprotect(ciphertext);
}
builder.Services.AddDataProtection()
.SetApplicationName()
.PersistKeysToFileSystem( DirectoryInfo());
```text
---
; prefer -bit
systems.
```csharp
System.Security.Cryptography;
rsa = RSA.Create();
[] signature = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
[] publicKeyBytes = rsa.ExportRSAPublicKey();
rsaPublic = RSA.Create();
rsaPublic.ImportRSAPublicKey(publicKeyBytes, _);
valid = rsaPublic.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
[] encrypted = rsaPublic.Encrypt(smallPayload, RSAEncryptionPadding.OaepSHA256);
[] decrypted = rsa.Decrypt(encrypted, RSAEncryptionPadding.OaepSHA256);
```text
Prefer ECDSA over RSA digital signatures projects -- smaller keys equivalent security.
```csharp
System.Security.Cryptography;
ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
[] signature = ecdsa.SignData(data, HashAlgorithmName.SHA256);
[] publicKey = ecdsa.ExportSubjectPublicKeyInfo();
ecdsaPublic = ECDsa.Create();
ecdsaPublic.ImportSubjectPublicKeyInfo(publicKey, _);
valid = ecdsaPublic.VerifyData(data, signature, HashAlgorithmName.SHA256);
```text
---
PBKDF2 built .NET acceptable password hashing. Use at least , iterations SHA (OWASP
recommendation).
```csharp
System.Buffers.Binary;
System.Security.Cryptography;
{
SaltSize = ;
HashSize = ;
Iterations = _000;
PayloadSize = + SaltSize + HashSize;
{
[] salt = RandomNumberGenerator.GetBytes(SaltSize);
[] hash = Rfc2898DeriveBytes.Pbkdf2(
password,
salt,
Iterations,
HashAlgorithmName.SHA256,
HashSize);
[] result = [PayloadSize];
BinaryPrimitives.WriteInt32LittleEndian(result, Iterations);
salt.CopyTo(result.AsSpan());
hash.CopyTo(result.AsSpan( + SaltSize));
Convert.ToBase64String(result);
}
{
Span<> decoded = [PayloadSize];
(!Convert.TryFromBase64String(stored, decoded, bytesWritten)
|| bytesWritten != PayloadSize)
{
;
}
iterations = BinaryPrimitives.ReadInt32LittleEndian(decoded);
(iterations <= )
;
salt = decoded.Slice(, SaltSize);
expectedHash = decoded.Slice( + SaltSize, HashSize);
[] actualHash = Rfc2898DeriveBytes.Pbkdf2(
password,
salt,
iterations,
HashAlgorithmName.SHA256,
HashSize);
CryptographicOperations.FixedTimeEquals(expectedHash, actualHash);
}
}
```text
Argon2id the recommended algorithm password hashing a NuGet dependency acceptable. It memory-hard,
resisting GPU/ASIC attacks better than PBKDF2.
```csharp
Konscious.Security.Cryptography;
{
argon2 = Argon2id(Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = ,
MemorySize = ,
Iterations =
};
argon2.GetBytes();
}
```text
> Prefer ASP.NET Core Identitys `Aes.Create()` defaults to CBC, but prefer AES-GCM authenticated encryption.
**Never compare hashes `==`** -- use `CryptographicOperations.FixedTimeEquals` to prevent timing side-channel
attacks.
**Never use MD5 SHA security purposes** -- they are broken. SHA acceptable only non-