Programming

Inject ICryptoService in Blazor WASM Non-Component Class

Learn to register Blazor.SubtleCrypto's ICryptoService in Program.cs and inject it via constructor into non-component classes in Blazor WebAssembly. Step-by-step guide for AES-GCM encryption/decryption with DI best practices.

1 answer 1 view

How to inject and use Blazor.SubtleCrypto (ICryptoService) in a non-component class in Blazor WebAssembly?

In a Blazor WASM project, injecting ICryptoService in components using @inject ICryptoService Crypto works fine. However, this approach does not work in non-component classes.

How can I register Blazor.SubtleCrypto and use it in a non-component class?

In Blazor WebAssembly, you register Blazor.SubtleCrypto’s ICryptoService in Program.cs with builder.Services.AddSubtleCrypto(), then inject it directly into non-component classes like services using constructor dependency injection—no @inject needed there. This taps into .NET’s built-in DI container, letting you call EncryptAsync or DecryptAsync for secure AES-GCM ops from anywhere in your app. Why does this matter? Components get the easy @inject shortcut, but plain classes demand the constructor pattern to stay decoupled and testable.


Contents


What is Blazor.SubtleCrypto and ICryptoService?

Blazor.SubtleCrypto wraps the browser’s Web Crypto API right into your Blazor WebAssembly apps, handling heavy lifting like AES-GCM encryption without raw JSInterop headaches. At its core sits ICryptoService—an interface exposing async methods for generateKey, encrypt, decrypt, and more. Think of it as your go-to for client-side crypto: secure key management, no server roundtrips.

But here’s the catch—it’s WASM-only. Server-side Blazor? Forget it; the underlying JSInterop demands a browser context. The library shines in standalone WASM hosted apps, pumping out CryptoResult objects packed with ciphertext, IVs, and secret keys. NuGet’s latest at version 9.0.0 keeps it fresh for .NET 8+.

Why bother? Raw IJSRuntime calls get messy fast. This library abstracts that, returning strongly-typed results you can await like any .NET service.


Installing Blazor.SubtleCrypto in Your Blazor WebAssembly Project

Fire up your terminal in the project root. Run this:

bash
dotnet add package Blazor.SubtleCrypto

That’s it—no fuss. Restore packages with dotnet restore if you’re picky. The package pulls in everything: ICryptoService interface, JSInterop glue, and those handy CryptoInput/CryptoResult types.

For components, you’d toss @using Blazor.SubtleCrypto at the top of your .razor file. But we’re not here for that. Non-components? Keep reading—they lean on DI registration instead.

Check the Blazor.SubtleCrypto NuGet page for changelogs. Recent updates fixed .NET 8 render-mode quirks.


Registering ICryptoService in Program.cs

Blazor WebAssembly’s DI container lives in Program.cs. Without registration, no injection—period. Here’s the magic line:

csharp
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");

// Key setup: dynamic (random per session) or fixed (your secret)
builder.Services.AddSubtleCrypto(options =>
{
 options.Mode = CryptoMode.DynamicKey; // Or FixedKey with options.Key = "your-base64-secret"
});

await builder.Build().RunAsync();

DynamicKey generates fresh AES-256-GCM keys on demand—perfect for ephemeral sessions. FixedKey? Base64-encode your secret ahead of time for persistent use. The official GitHub repo spells out options like key length and algorithm tweaks.

Scoped by default, it fits WASM’s single-page nature. Boom—ICryptoService is now resolvable anywhere DI flows.


Dependency Injection: Components vs. Non-Component Classes

Blazor’s @inject directive? Component-exclusive. Slap it in a .razor file:

razor
@inject ICryptoService Crypto
@code {
 var result = await Crypto.EncryptAsync("hello");
}

Sweet for UI logic. But non-components—say, a DataService or Utility class? No Razor context, no @inject. Enter constructor injection, straight from .NET’s playbook.

Microsoft’s docs nail it: register services in Program.cs, then constructors pull them in. Blazor DI fundamentals confirm Scoped works across the app lifetime in WASM.

What if you’re in a static method or factory? Grab IServiceProvider from the host. But constructors keep things clean.


Step-by-Step: Injecting ICryptoService in a Non-Component

Ready to wire it up? Let’s build a simple AuthService.

  1. Create your non-component class (Services/AuthService.cs):
csharp
using Blazor.SubtleCrypto;

public class AuthService
{
 private readonly ICryptoService _crypto;

 // Constructor injection—DI magic happens here
 public AuthService(ICryptoService crypto)
 {
 _crypto = crypto;
 }

 public async Task<string> EncryptTokenAsync(string token)
 {
 var result = await _crypto.EncryptAsync(token);
 return Convert.ToBase64String(result.Value); // Ciphertext ready to store/send
 }
}
  1. Register it in Program.cs (after AddSubtleCrypto):
csharp
builder.Services.AddScoped<AuthService>();
  1. Inject AuthService into a component (or chain further):
razor
@inject AuthService Auth
@code {
 var encrypted = await Auth.EncryptTokenAsync("my-jwt");
}

See? No hacks. A StackOverflow thread details this exact pattern, proving it works beyond components.

Test it—hit F5, encrypt something. Smooth.


Full Example: Encrypt and Decrypt in a Custom Service

Let’s go end-to-end. Say SecureStorageService handles user data.

csharp
using Blazor.SubtleCrypto;

public class SecureStorageService
{
 private readonly ICryptoService _crypto;

 public SecureStorageService(ICryptoService crypto)
 {
 _crypto = crypto;
 }

 public async Task<byte[]> EncryptAsync(string plaintext)
 {
 var encryptResult = await _crypto.EncryptAsync(plaintext);
 return encryptResult.Value; // byte[] ciphertext
 }

 public async Task<string> DecryptAsync(byte[] ciphertext, CryptoResult encryptResult)
 {
 var input = new CryptoInput
 {
 Data = ciphertext,
 Secret = encryptResult.Secret // Reuse key/IV from encrypt
 };
 var decryptResult = await _crypto.DecryptAsync(input);
 return decryptResult.ValueAsString; // Back to plaintext
 }
}

Register: builder.Services.AddScoped<SecureStorageService>();

Usage in a page:

razor
@page "/secure"
@inject SecureStorageService Storage
@inject IJSRuntime JS // Optional, for localStorage

<h3>Secure Storage Demo</h3>

<button @onclick="Demo">Encrypt/Decrypt</button>
<p>@message</p>

@code {
 private string message = "";

 private async Task Demo()
 {
 var plaintext = "super-secret-data";
 var encryptRes = await _crypto.EncryptAsync(plaintext); // Wait, need to store encryptRes.Secret!
 
 // Save encryptRes to localStorage via JS, or pass it along
 var ciphertext = encryptRes.Value;
 await JS.InvokeVoidAsync("localStorage.setItem", "cipher", Convert.ToBase64String(ciphertext));
 await JS.InvokeVoidAsync("localStorage.setItem", "secret", JsonSerializer.Serialize(encryptRes.Secret));

 // Later: decrypt
 var ctBytes = Convert.FromBase64String(await JS.InvokeAsync<string>("localStorage.getItem", "cipher"));
 var secretJson = await JS.InvokeAsync<string>("localStorage.getItem", "secret");
 var secret = JsonSerializer.Deserialize<CryptoSecret>(secretJson);
 var input = new CryptoInput { Data = ctBytes, Secret = secret };
 var decrypted = await _crypto.DecryptAsync(input);
 message = decrypted.ValueAsString;
 }
}

Pro tip: Serialize CryptoResult.Secret for roundtrips—it’s got Key and IV as byte[].


Common Pitfalls and Fixes

Hit a wall? PlatformNotSupportedException screams “not in browser!”—double-check WASM hosting. .NET 8+ interactive render modes? Add @rendermode InteractiveWebAssembly or tweak JSInterop timing, per the README.

Decrypt failing? Mismatch CryptoInput.Secret from the original encrypt. Another SO post covers decrypt woes—always pair key/IV.

Singleton vs Scoped? Stick to Scoped; WASM reloads nuke Singletons anyway.

No crypto post-SSR? WASM-only—patience for JSInterop warmup.


Best Practices for Blazor WebAssembly Crypto

Dynamic keys rock for sessions, but fixed for app-wide needs. Always await fully—JSInterop’s async under the hood.

Chain services: ICryptoService into DataService into Pages. Lifetimes matter—Scoped for user sessions, Transient for one-offs.

Alternatives? Direct IJSRuntime for subtle.encrypt() if you crave control, but why reinvent? Or libs like Blazored.LocalStorage with crypto wrappers.

Store secrets client-side? Risky—pair with secure tokens. Test in incognito; clear storage often.

Reddit threads echo this: inject services properly, no inheritance hacks needed.


Sources

  1. Blazor.SubtleCrypto GitHub — Official repo with registration, examples, and non-component DI: https://github.com/memd24/Blazor.SubtleCrypto
  2. Blazor.SubtleCrypto NuGet — Package details, API reference, and constructor injection samples: https://www.nuget.org/packages/Blazor.SubtleCrypto
  3. Stack Overflow: Using Blazor.SubtleCrypto in Non-Component — Constructor injection walkthrough for services: https://stackoverflow.com/questions/79867084/how-to-use-blazor-subtlecrypto-in-a-non-component-related-class
  4. Microsoft Blazor DI Docs — Fundamentals of dependency injection in Blazor WASM: https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/dependency-injection?view=aspnetcore-8.0
  5. Blazor.SubtleCrypto README — .NET 8+ render mode fixes and advanced config: https://github.com/memd24/Blazor.SubtleCrypto/blob/master/README.md
  6. Stack Overflow: Decrypt with Blazor.SubtleCrypto — Troubleshooting JSInterop and CryptoInput issues: https://stackoverflow.com/questions/72018234/how-decrypt-data-from-blazor-subtlecrypto

Conclusion

Mastering Blazor.SubtleCrypto in non-components boils down to Program.cs registration and constructor injection—simple, powerful, and fully DI-native for Blazor WebAssembly. You’ll encrypt, decrypt, and secure data seamlessly across services, dodging @inject limits entirely. Experiment with the full example; tweak keys as needed. Secure coding awaits—what’s your first payload?

Authors
Verified by moderation
NeuroAnswers
Moderation
Inject ICryptoService in Blazor WASM Non-Component Class