Codebelt
v10.5.5

Cuemon.Security.Cryptography

Factory-based HMAC, digest, and AES APIs that extend System.Security.Cryptography.

.NET 10.0 / .NET 9.0 / .NET Standard 2.0 MIT 206,014 downloads

Overview

Cuemon.Security.Cryptography extends System.Security.Cryptography with two families of types: factory-driven hash computation and an AES symmetric-encryption helper. Both families surface a consistent API built on the Hash and HashResult abstractions from Cuemon.Core. UnkeyedHashFactory covers the standard digest algorithms (MD5, SHA-1, SHA-256, SHA-384, SHA-512, and SHA-512/256), while KeyedHashFactory covers the corresponding HMAC variants. Each factory method returns a Hash whose ComputeHash overloads accept byte arrays, strings, streams, and many primitive types.

The AesCryptor class provides a self-contained AES implementation with CBC/PKCS7 defaults. It exposes static key and IV generation methods and an encrypt/decrypt byte-array API configured through the AesCryptorOptions options object. The package also ships SHA512256, a pure .NET implementation of the SHA-512/256 truncation defined in NIST FIPS PUB 180-4, used internally by SecureHashAlgorithm512256.

Key APIs

UnkeyedHashFactory is a static factory that creates instances of the non-keyed hash implementations. CreateCrypto(UnkeyedCryptoAlgorithm algorithm = default) dispatches to the algorithm-specific overloads; calling it without arguments returns a SHA-256 hasher. Named overloads such as CreateCryptoSha256(), CreateCryptoSha512(), and CreateCryptoSha512Slash256() are available for each algorithm.

KeyedHashFactory is the keyed counterpart that produces HMAC implementations. CreateHmacCrypto(byte[] secret, KeyedCryptoAlgorithm algorithm = default) defaults to HMAC-SHA256 and accepts an optional Action<ConvertibleOptions> to configure byte-order and encoding. Named overloads (CreateHmacCryptoSha256, CreateHmacCryptoSha512, and so on) are available for direct selection.

UnkeyedCryptoHash<TAlgorithm> is the abstract base class for all non-keyed hash wrappers. It accepts a Func<TAlgorithm> initializer and delegates ComputeHash(byte[]) to the algorithm instance. Subclass it with a HashAlgorithm-derived type constraint to add custom algorithm support without re-implementing the Hash<ConvertibleOptions> lifecycle.

KeyedCryptoHash<TAlgorithm> extends UnkeyedCryptoHash<TAlgorithm> and constrains TAlgorithm to KeyedHashAlgorithm. It passes the secret key to the algorithm via Activator.CreateInstance, so the concrete types (HmacSecureHashAlgorithm256, HmacSecureHashAlgorithm512, and so on) are thin wrappers around the standard HMAC classes with no additional logic beyond constructor forwarding.

AesCryptor wraps System.Security.Cryptography.Aes with a fixed 128-bit block size. The constructor validates that the key is exactly 128, 192, or 256 bits and that the IV is exactly 128 bits, throwing CryptographicException on violations. Encrypt(byte[] value, Action<AesCryptorOptions> setup = null) and Decrypt(byte[] value, Action<AesCryptorOptions> setup = null) perform the transform using the configured padding and cipher mode. The static GenerateKey(Action<AesKeyOptions> setup = null) and GenerateInitializationVector() methods produce cryptographically suitable random key material; the key defaults to 256 bits via AesSize.Aes256.

AesCryptorOptions configures the cipher parameters passed to the underlying Aes instance. The default values are CipherMode.CBC and PaddingMode.PKCS7. Both properties are mutable, allowing callers to switch to alternative modes using the Action<AesCryptorOptions> delegate pattern used throughout Cuemon.

SecureHashAlgorithm512256 and SHA512256 provide the SHA-512/256 digest. SHA512256 derives from HashAlgorithm and implements the full NIST FIPS PUB 180-4 Section 5.3.6.2 algorithm with the correct IV and compression function. SecureHashAlgorithm512256 wraps it inside UnkeyedCryptoHash<SHA512256> and exposes the Hash API; UnkeyedHashFactory.CreateCryptoSha512Slash256() is the preferred entry point.

Basic usage

Use this pattern when you need to sign or verify a payload with a shared secret, for example in webhook signature checks or API request authentication. The Hash abstraction returned by KeyedHashFactory is algorithm-agnostic, so switching from HMAC-SHA256 to HMAC-SHA512 only changes the selected factory overload.

using System.Text;
using Codebelt.Extensions.Xunit;
using Cuemon.Security.Cryptography;
using Xunit;

namespace Acme.Webhooks.Tests;

public class WebhookSignatureTest : Test
{
    public WebhookSignatureTest(ITestOutputHelper output) : base(output)
    {
    }

    [Fact]
    public void SignAndVerify_ShouldProduceConsistentHmacSha256Signature()
    {
        var secret = Encoding.UTF8.GetBytes("super-secret-key");
        var payload = Encoding.UTF8.GetBytes("{\"event\":\"order.placed\",\"id\":42}");
        var signature = KeyedHashFactory.CreateHmacCrypto(secret).ComputeHash(payload).ToHexadecimalString();

        TestOutput.WriteLine($"HMAC-SHA256 signature: {signature}");

        Assert.Equal(
            signature,
            KeyedHashFactory.CreateHmacCrypto(secret).ComputeHash(payload).ToHexadecimalString());

        var tampered = Encoding.UTF8.GetBytes("{\"event\":\"order.placed\",\"id\":99}");
        Assert.NotEqual(
            signature,
            KeyedHashFactory.CreateHmacCrypto(secret).ComputeHash(tampered).ToHexadecimalString());
    }
}

Installation

dotnet add package Cuemon.Security.Cryptography

Usage guidance

This package is the right choice when you need a shared-secret HMAC or a content digest with a uniform API across multiple algorithms, or when you want AES encryption without assembling the System.Security.Cryptography.Aes setup manually. The Hash.ComputeHash overloads accept byte arrays, strings, streams, and most primitive types directly, which avoids explicit encoding before hashing in many scenarios. If you need only a single, well-known algorithm such as SHA256.HashData and do not require the HashResult abstraction or the multi-overload convenience, the BCL static methods are sufficient and carry no additional dependency.

Family packages