loading

Random values

RandomUuid / GetRandomValues

RandomUuid returns a cryptographically strong v4 UUID as a Guid. GetRandomValues fills up to 65,536 bytes per call with strong random data - ideal for keys, IVs and salts.

C#
Guid uuid = await crypto.RandomUuid();

byte[] iv = await crypto.GetRandomValues(16);
Live sample
random values output
Results will appear here when you interact with the samples.

Hashing

Digest

Digest computes a SHA-256, SHA-384 or SHA-512 hash of arbitrary bytes via SubtleCrypto.digest. The algorithm is selected with the CryptoKeyHash enum.

C#
byte[] data = Encoding.UTF8.GetBytes("Bit.Butil");

byte[] hash = await crypto.Digest(CryptoKeyHash.Sha256, data);
Live sample
Message
hashing output
Results will appear here when you interact with the samples.

AES encrypt & decrypt

Encrypt / Decrypt

The CryptoAlgorithm overload covers the common cases in one line: pick AES-GCM (authenticated, recommended), AES-CBC or AES-CTR and pass the key, data and IV. This demo generates a 256-bit key plus a random IV, encrypts your text, then decrypts the cipher back.

C#
byte[] key = await crypto.GenerateAesKey(256);
byte[] iv = await crypto.GetRandomValues(16);
byte[] data = Encoding.UTF8.GetBytes("Secret message");

byte[] cipher = await crypto.Encrypt(CryptoAlgorithm.AesGcm, key, data, iv: iv);
byte[] plain = await crypto.Decrypt(CryptoAlgorithm.AesGcm, key, cipher, iv: iv);
Live sample
Plain text
Algorithm
AES output
Results will appear here when you interact with the samples.

Algorithm parameter classes

Encrypt<T> / Decrypt<T>

For full control, pass a typed parameter object instead of the enum: AesGcmCryptoAlgorithmParams (Iv, AdditionalData, TagLength), AesCbcCryptoAlgorithmParams (Iv), AesCtrCryptoAlgorithmParams (Counter, Length) or RsaOaepCryptoAlgorithmParams (Label). The live demo generates an RSA-OAEP key pair, encrypts with the public key and decrypts with the private key.

C#
// AES-GCM with additional authenticated data and explicit tag length
byte[] cipher = await crypto.Encrypt(new AesGcmCryptoAlgorithmParams
{
    Iv = iv,
    AdditionalData = aad,
    TagLength = AesGcmTagLength.Sixteen,
}, key, data);

// RSA-OAEP: encrypt with the SPKI public key, decrypt with the PKCS8 private key
RsaKeyPair pair = await crypto.GenerateRsaKeyPair(2048, CryptoKeyHash.Sha256);

byte[] rsaCipher = await crypto.Encrypt(new RsaOaepCryptoAlgorithmParams(), pair.PublicKey, data);
byte[] rsaPlain = await crypto.Decrypt(new RsaOaepCryptoAlgorithmParams(), pair.PrivateKey, rsaCipher);
Live sample
Text for the RSA-OAEP round-trip
RSA-OAEP output
Results will appear here when you interact with the samples.

HMAC sign & verify

GenerateHmacKey / SignHmac / VerifyHmac

SignHmac produces a keyed authentication tag over your data; VerifyHmac checks a tag in constant time inside the browser engine. Keys can come from GenerateHmacKey or any shared secret.

C#
byte[] key = await crypto.GenerateHmacKey(CryptoKeyHash.Sha256);
byte[] data = Encoding.UTF8.GetBytes("message");

byte[] tag = await crypto.SignHmac(CryptoKeyHash.Sha256, key, data);
bool valid = await crypto.VerifyHmac(CryptoKeyHash.Sha256, key, tag, data);
Live sample
Message
HMAC output
Results will appear here when you interact with the samples.

Key generation

GenerateAesKey / GenerateHmacKey / GenerateRsaKeyPair / GenerateEcdsaKeyPair

Generate symmetric keys as raw bytes, or RSA / ECDSA pairs as SPKI (public) and PKCS8 (private) DER bytes ready for Encrypt, SignRsaPss and SignEcdsa - or for export to a server.

C#
byte[] aesKey = await crypto.GenerateAesKey(256);
byte[] hmacKey = await crypto.GenerateHmacKey(CryptoKeyHash.Sha512);

RsaKeyPair rsa = await crypto.GenerateRsaKeyPair(2048, CryptoKeyHash.Sha256);
EcKeyPair ec = await crypto.GenerateEcdsaKeyPair("P-256");
Live sample
key generation output
Results will appear here when you interact with the samples.

Password-based derivation

DerivePbkdf2

DerivePbkdf2 stretches a password into key material using PBKDF2 with a salt, an iteration count and the requested output size in bits. Use a random per-user salt and a six-figure iteration count.

C#
byte[] salt = await crypto.GetRandomValues(16);

byte[] derived = await crypto.DerivePbkdf2(
    Encoding.UTF8.GetBytes(password),
    salt,
    iterations: 100_000,
    outputLengthBits: 256);
Live sample
Password
PBKDF2 output
Results will appear here when you interact with the samples.

Digital signatures

SignRsaPss / VerifyRsaPss / SignEcdsa / VerifyEcdsa

Sign with the PKCS8 private key, verify with the SPKI public key. RSA-PSS takes a salt length; ECDSA takes the named curve (P-256, P-384 or P-521). Both default the hash to SHA-256.

C#
byte[] data = Encoding.UTF8.GetBytes("signed payload");

RsaKeyPair rsa = await crypto.GenerateRsaKeyPair();
byte[] pssSig = await crypto.SignRsaPss(rsa.PrivateKey, data);
bool pssOk = await crypto.VerifyRsaPss(rsa.PublicKey, pssSig, data);

EcKeyPair ec = await crypto.GenerateEcdsaKeyPair("P-256");
byte[] ecSig = await crypto.SignEcdsa(ec.PrivateKey, data, "P-256");
bool ecOk = await crypto.VerifyEcdsa(ec.PublicKey, ecSig, data, "P-256");
Live sample
Message to sign
signatures output
Results will appear here when you interact with the samples.

Key import & export

ExportKey / ExportJsonWebKey / ImportJsonWebKey

Re-expresses key material between the four WebCrypto formats, which is how a key that came from a server becomes bytes the methods above accept. CryptoKeyAlgorithm says what the key is - the browser rejects a key imported under the wrong algorithm even when its bytes are right. The demo generates an AES key, publishes it as a JWK, and imports the JWK back to raw bytes.

C#
byte[] key = await crypto.GenerateAesKey(256);

// raw bytes → the JWK a server publishes
CryptoJsonWebKey? jwk = await crypto.ExportJsonWebKey(key, CryptoKeyFormat.Raw, CryptoKeyAlgorithm.AesGcm(256));

// and back again
byte[] roundTripped = await crypto.ImportJsonWebKey(jwk, CryptoKeyAlgorithm.AesGcm(256), CryptoKeyFormat.Raw);

// the same conversion between two byte formats - here a public key from SPKI to JWK's cousin
RsaKeyPair rsa = await crypto.GenerateRsaKeyPair();
CryptoJsonWebKey? publicJwk = await crypto.ExportJsonWebKey(rsa.PublicKey, CryptoKeyFormat.Spki, CryptoKeyAlgorithm.RsaOaep());
Live sample
import/export output
Results will appear here when you interact with the samples.

Key agreement (ECDH)

GenerateEcdhKeyPair / DeriveEcdhBits / DeriveEcdhKey

Two parties each generate a key pair, exchange public keys, and arrive at the same secret without ever sending it. DeriveEcdhBits gives the raw agreement - the x coordinate of a point, not a uniform key - while DeriveEcdhKey runs it through to a usable AES key in one step. The demo plays both sides and checks that they agree.

C#
EcKeyPair alice = await crypto.GenerateEcdhKeyPair("P-256");
EcKeyPair bob = await crypto.GenerateEcdhKeyPair("P-256");

// each side derives from its own private key and the other's public key
byte[] aliceKey = await crypto.DeriveEcdhKey(alice.PrivateKey, bob.PublicKey, CryptoKeyAlgorithm.AesGcm(256));
byte[] bobKey = await crypto.DeriveEcdhKey(bob.PrivateKey, alice.PublicKey, CryptoKeyAlgorithm.AesGcm(256));
// aliceKey and bobKey are the same 32 bytes

// or take the raw agreement and derive from it yourself
byte[] shared = await crypto.DeriveEcdhBits(alice.PrivateKey, bob.PublicKey, 256);
Live sample
ECDH output
Results will appear here when you interact with the samples.

Deriving keys

DeriveHkdfBits / DeriveHkdfKey / DerivePbkdf2Key

HKDF turns one high-entropy secret into as many purpose-bound keys as you need - the info parameter is what keeps the encryption key and the signing key different. PBKDF2 is the one for passwords: it stretches, HKDF does not. Both have a Key form that hands back a key of a stated algorithm rather than loose bits.

C#
byte[] secret = await crypto.GetRandomValues(32);
byte[] salt = await crypto.GetRandomValues(16);

// two keys from one secret, kept apart by their info string
byte[] encryptionKey = await crypto.DeriveHkdfKey(secret, salt, Encoding.UTF8.GetBytes("encryption"), CryptoKeyAlgorithm.AesGcm(256));
byte[] signingKey = await crypto.DeriveHkdfKey(secret, salt, Encoding.UTF8.GetBytes("signing"), CryptoKeyAlgorithm.Hmac());

// passwords go through PBKDF2, never HKDF
byte[] fromPassword = await crypto.DerivePbkdf2Key(
    Encoding.UTF8.GetBytes(password), salt, iterations: 100_000, CryptoKeyAlgorithm.AesGcm(256));
Live sample
Password
derivation output
Results will appear here when you interact with the samples.

Key wrapping

GenerateAesKwKey / WrapKey / UnwrapKey

Encrypts one key with another, so key material can be stored or sent without ever being in the clear. AES-KW exists only for this and needs no IV; AES-GCM works too and takes one. The demo wraps a freshly generated AES key, then unwraps it and checks the bytes came back identical.

C#
byte[] keyToProtect = await crypto.GenerateAesKey(256);
byte[] wrappingKey = await crypto.GenerateAesKwKey(256);

byte[] wrapped = await crypto.WrapKey(
    keyToProtect, CryptoKeyFormat.Raw, CryptoKeyAlgorithm.AesGcm(256),
    wrappingKey, new AesKwCryptoAlgorithmParams());

// wrapped is safe to store; only the wrapping key opens it
byte[] recovered = await crypto.UnwrapKey(
    wrapped, CryptoKeyFormat.Raw, CryptoKeyAlgorithm.AesGcm(256),
    wrappingKey, new AesKwCryptoAlgorithmParams());
Live sample
key wrapping output
Results will appear here when you interact with the samples.
Warning:
Secure context and key handling SubtleCrypto only exists in secure contexts (HTTPS or localhost). Also note that Butil's key helpers return extractable key bytes to .NET across the interop channel - convenient, but the material may surface in interop logs or memory dumps. Avoid logging keys, and prefer server-side key custody when keys must never leave a hardware or secure boundary.

API reference

Member
Signature
Description
RandomUuid
ValueTask<Guid> RandomUuid()
Returns a cryptographically strong random Guid (v4 UUID).
GetRandomValues
ValueTask<byte[]> GetRandomValues(int length)
Fills length bytes (max 65,536 per call) with cryptographically strong random values.
Digest
ValueTask<byte[]> Digest(CryptoKeyHash algorithm, byte[] data)
Computes a SHA-256 / SHA-384 / SHA-512 digest of the data.
SignHmac
ValueTask<byte[]> SignHmac(CryptoKeyHash algorithm, byte[] key, byte[] data)
Produces an HMAC tag for the data using the given symmetric key.
VerifyHmac
ValueTask<bool> VerifyHmac(CryptoKeyHash algorithm, byte[] key, byte[] signature, byte[] data)
Verifies an HMAC tag previously produced by SignHmac or any compatible producer.
GenerateAesKey
ValueTask<byte[]> GenerateAesKey(int bits = 256)
Generates a fresh AES key (128, 192 or 256 bits) as raw bytes.
GenerateHmacKey
ValueTask<byte[]> GenerateHmacKey(CryptoKeyHash algorithm = CryptoKeyHash.Sha256, int? lengthBits = null)
Generates an HMAC key of the requested length and hash as raw bytes.
GenerateRsaKeyPair
ValueTask<RsaKeyPair> GenerateRsaKeyPair(int modulusLengthBits = 2048, CryptoKeyHash algorithm = CryptoKeyHash.Sha256)
Generates an RSA-OAEP key pair; returns SPKI public and PKCS8 private DER bytes.
GenerateEcdsaKeyPair
ValueTask<EcKeyPair> GenerateEcdsaKeyPair(string curve = "P-256")
Generates an ECDSA key pair on the named curve (P-256, P-384, P-521).
DerivePbkdf2
ValueTask<byte[]> DerivePbkdf2(byte[] password, byte[] salt, int iterations, int outputLengthBits, CryptoKeyHash algorithm = CryptoKeyHash.Sha256)
Derives raw bytes from a password using PBKDF2.
SignRsaPss
ValueTask<byte[]> SignRsaPss(byte[] privateKey, byte[] data, int saltLength = 32, CryptoKeyHash algorithm = CryptoKeyHash.Sha256)
Produces an RSA-PSS signature using a PKCS8 private key.
VerifyRsaPss
ValueTask<bool> VerifyRsaPss(byte[] publicKey, byte[] signature, byte[] data, int saltLength = 32, CryptoKeyHash algorithm = CryptoKeyHash.Sha256)
Verifies an RSA-PSS signature using an SPKI public key.
SignEcdsa
ValueTask<byte[]> SignEcdsa(byte[] privateKey, byte[] data, string curve = "P-256", CryptoKeyHash algorithm = CryptoKeyHash.Sha256)
Produces an ECDSA signature using a PKCS8 private key.
VerifyEcdsa
ValueTask<bool> VerifyEcdsa(byte[] publicKey, byte[] signature, byte[] data, string curve = "P-256", CryptoKeyHash algorithm = CryptoKeyHash.Sha256)
Verifies an ECDSA signature using an SPKI public key.
Encrypt<T>
ValueTask<byte[]> Encrypt<T>(T algorithm, byte[] key, byte[] data, CryptoKeyHash? keyHash = null) where T : ICryptoAlgorithmParams
Encrypts data with a typed algorithm parameter object (AES-GCM / AES-CBC / AES-CTR / RSA-OAEP). keyHash applies to RSA-OAEP key import only.
Encrypt
ValueTask<byte[]> Encrypt(CryptoAlgorithm algorithm, byte[] key, byte[] data, byte[]? iv = null, CryptoKeyHash? keyHash = null)
Convenience overload selecting the algorithm via the CryptoAlgorithm enum; iv feeds the AES IV/counter.
Decrypt<T>
ValueTask<byte[]> Decrypt<T>(T algorithm, byte[] key, byte[] data, CryptoKeyHash? keyHash = null) where T : ICryptoAlgorithmParams
Decrypts data with a typed algorithm parameter object.
Decrypt
ValueTask<byte[]> Decrypt(CryptoAlgorithm algorithm, byte[] key, byte[] data, byte[]? iv = null, CryptoKeyHash? keyHash = null)
Convenience overload selecting the algorithm via the CryptoAlgorithm enum.
ExportKey
ValueTask<byte[]> ExportKey(byte[] key, CryptoKeyFormat sourceFormat, CryptoKeyFormat targetFormat, CryptoKeyAlgorithm algorithm)
Re-expresses key material between the raw, pkcs8 and spki formats.
ExportJsonWebKey
ValueTask<CryptoJsonWebKey?> ExportJsonWebKey(byte[] key, CryptoKeyFormat sourceFormat, CryptoKeyAlgorithm algorithm)
Exports key material as a JSON Web Key - the format a server publishes and consumes.
ImportJsonWebKey
ValueTask<byte[]> ImportJsonWebKey(CryptoJsonWebKey jwk, CryptoKeyAlgorithm algorithm, CryptoKeyFormat targetFormat = CryptoKeyFormat.Raw)
Imports a JWK and hands back its bytes, ready for the encrypt, sign and derive methods.
GenerateEcdhKeyPair
ValueTask<EcKeyPair> GenerateEcdhKeyPair(string curve = "P-256")
Generates an ECDH key pair for deriving a shared secret with someone else's public key.
DeriveEcdhBits
ValueTask<byte[]> DeriveEcdhBits(byte[] privateKey, byte[] publicKey, int outputLengthBits, string curve = "P-256")
Derives the raw ECDH agreement. Not a uniform key - run it through HKDF.
DeriveEcdhKey
ValueTask<byte[]> DeriveEcdhKey(byte[] privateKey, byte[] publicKey, CryptoKeyAlgorithm derivedKeyAlgorithm, string curve = "P-256")
Derives a usable key from an ECDH agreement in one step.
DeriveHkdfBits
ValueTask<byte[]> DeriveHkdfBits(byte[] keyMaterial, byte[]? salt, byte[]? info, int outputLengthBits, CryptoKeyHash algorithm = CryptoKeyHash.Sha256)
Derives raw bytes from existing high-entropy key material using HKDF. Not for passwords.
DeriveHkdfKey
ValueTask<byte[]> DeriveHkdfKey(byte[] keyMaterial, byte[]? salt, byte[]? info, CryptoKeyAlgorithm derivedKeyAlgorithm, CryptoKeyHash algorithm = CryptoKeyHash.Sha256)
Derives a usable key from existing key material using HKDF; info binds it to a purpose.
DerivePbkdf2Key
ValueTask<byte[]> DerivePbkdf2Key(byte[] password, byte[] salt, int iterations, CryptoKeyAlgorithm derivedKeyAlgorithm, CryptoKeyHash algorithm = CryptoKeyHash.Sha256)
Stretches a password into a key of a stated algorithm rather than into loose bits.
GenerateAesKwKey
ValueTask<byte[]> GenerateAesKwKey(int bits = 256)
Generates an AES-KW key - the algorithm whose only job is encrypting other keys.
WrapKey<T>
ValueTask<byte[]> WrapKey<T>(byte[] key, CryptoKeyFormat format, CryptoKeyAlgorithm keyAlgorithm, byte[] wrappingKey, T wrapAlgorithm, CryptoKeyHash? wrappingKeyHash = null) where T : ICryptoAlgorithmParams
Encrypts key material with another key so it can be stored or sent without appearing in the clear.
UnwrapKey<T>
ValueTask<byte[]> UnwrapKey<T>(byte[] wrappedKey, CryptoKeyFormat format, CryptoKeyAlgorithm unwrappedKeyAlgorithm, byte[] unwrappingKey, T unwrapAlgorithm, CryptoKeyHash? unwrappingKeyHash = null) where T : ICryptoAlgorithmParams
Reverses WrapKey: decrypts wrapped key material and hands back its bytes.
An unhandled error has occurred. Reload 🗙