Use the library in Blazor
You want your Blazor app to use post-quantum algorithms, either on the server or in the browser.
Solution overview
Blazor Server and Blazor WebAssembly use the library differently, because a browser doesn't load native libraries:
| Blazor Server | Blazor WebAssembly | |
|---|---|---|
| Reference | WKL.System.Security.Cryptography.Extensions | WKL.System.Security.Cryptography.WASM |
| OpenSSL | libcrypto 3.5 or later on the server | OpenSSL as a WebAssembly module that ships with the project |
| API | the extension methods with CryptoMode, as in any .NET app | the asynchronous methods of WasmCryptoProvider: ML-DSA, ML-KEM, key derivation |
| Where the keys live | on the server | in the browser |
The extension methods call OpenSSL through platform invoke, so they don't run in Blazor WebAssembly. In
the browser, use WasmCryptoProvider directly.
Implementation
Blazor Server
Blazor Server uses the library like any other .NET app. When the app starts, set the path to OpenSSL and turn on PQ mode before the first cryptographic operation runs:
using WKL.System.Security.Cryptography; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorComponents().AddInteractiveServerComponents(); var app = builder.Build(); PostQuantumConfig.OpenSslPath = builder.Configuration["OpenSsl:Directory"]; PostQuantumConfig.EnablePostQuantum(CryptoMode.Hybrid); app.Run();
After that, every guide in this documentation applies unchanged, for example Create your first hybrid certificate chain.
Blazor WebAssembly
Reference the WebAssembly project:
<ItemGroup> <ProjectReference Include="..\sys-sec-crypto\WKL.System.Security.Cryptography.WASM\WKL.System.Security.Cryptography.WASM.csproj" /> </ItemGroup>
The project publishes
wkl-crypto.js,openssl.js, andopenssl.wasmas static web assets under_content/WKL.System.Security.Cryptography.WASM/.Load the JavaScript bridge in
wwwroot/index.html, before the Blazor script:<script src="_content/WKL.System.Security.Cryptography.WASM/wkl-crypto.js"></script>The bridge loads
openssl.jsandopenssl.wasmitself. If the files are somewhere else, setwindow.WKLCryptoConfig = { jsPath: "…", wasmPath: "…" }before the script.Register and start the provider in
Program.cs:using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.JSInterop; using WKL.System.Security.Cryptography; using WKL.System.Security.Cryptography.WASM; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); var host = builder.Build(); var provider = new WasmCryptoProvider(host.Services.GetRequiredService<IJSRuntime>()); CryptoProviderFactory.SetProvider(provider); await PostQuantumConfig.EnablePostQuantumAsync(CryptoMode.Hybrid); await host.RunAsync();
Sign and verify in the browser, for example in a component that receives the provider:
var (publicKey, privateKey) = await provider.GenerateMlDsaKeyPairAsync("ML-DSA-65"); byte[] data = "Order 2026-0815"u8.ToArray(); byte[] signature = await provider.SignMlDsaAsync(data, privateKey); bool valid = await provider.VerifyMlDsaAsync(data, signature, publicKey);
Keys are DER-encoded: SubjectPublicKeyInfo for public keys and PKCS #8 for private keys. ML-KEM works the same way with
GenerateMlKemKeyPairAsync,EncapsulateAsync, andDecapsulateAsync.
Remarks
- The app doesn't continue without PQ. If
EnablePostQuantumAsyncfails, it throws aCryptographicException. The app doesn't quietly keep running without PQ algorithms. - The policy applies in the browser, too.
GenerateMlDsaKeyPairAsync("ML-DSA-44")andGenerateMlKemKeyPairAsync("ML-KEM-512")throw aCryptographicExceptionwithoutPostQuantumExemptions.ReducedParameterSets.EncryptStreamWithPqKeyAsyncencrypts with ML-KEM alone and requiresPostQuantumExemptions.PurePostQuantum.DeriveHybridKeyAsynccombines with a method that BSI TR-02102-1 doesn't recommend and requiresPostQuantumExemptions.NonRecommendedCombiners. See Post-quantum algorithm policy. - An ML-DSA key alone isn't a hybrid signature.
SignMlDsaAsynccomputes ML-DSA only. For a signature that follows the BSI TR-02102-1 recommendation, combine it in your protocol with a classical signature, for example ECDSA through the browser's Web Crypto API. - Don't store private keys in browser storage. Any script on the page can read what's in
localStorageorsessionStorage. OpenSslPathdoesn't apply in the browser. The WebAssembly provider ignores the property.