Inhaltsverzeichnis

WasmCryptoProvider

Namespace: WvdS.System.Security.Cryptography.Providers

JavaScript Interop-basierter Krypto-Provider für Blazor WebAssembly. Kommuniziert über IJSRuntime mit openssl.wasm.

Übersicht

Der WasmCryptoProvider ermöglicht Post-Quantum Kryptographie in Blazor WebAssembly Anwendungen durch:

Architektur

Blazor WebAssembly
      │
      ▼
┌─────────────────┐
│ WasmCrypto-     │
│ Provider        │
│ (C#)            │
└────────┬────────┘
         │ IJSRuntime.InvokeAsync
         ▼
┌─────────────────┐
│ wvds-crypto.js  │
│ (JavaScript)    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ openssl.wasm    │
│ (WebAssembly)   │
└─────────────────┘

Eigenschaften

Eigenschaft Typ Beschreibung
Name string "WASM (JS Interop)"
IsAvailable bool true wenn initialisiert

Dependency Injection

// Program.cs (Blazor WebAssembly)
builder.Services.AddScoped<ICryptoProvider>(sp =>
    new WasmCryptoProvider(sp.GetRequiredService<IJSRuntime>()));

Initialisierung

@inject ICryptoProvider CryptoProvider
 
@code {
    protected override async Task OnInitializedAsync()
    {
        await CryptoProvider.InitializeAsync();
 
        if (CryptoProvider.IsAvailable)
        {
            var version = CryptoProvider.GetOpenSslVersion();
            Console.WriteLine($"OpenSSL WASM: {version}");
        }
    }
}

Erforderliche JS/WASM Dateien

In wwwroot/index.html:

<head>
    <!-- OpenSSL WASM Module -->
    <script src="_content/WvdS.Crypto/openssl.js"></script>
 
    <!-- WvdS Crypto Wrapper -->
    <script src="_content/WvdS.Crypto/wvds-crypto.js"></script>
</head>

ML-DSA Operationen

GenerateMlDsaKeyPairAsync

var (publicKey, privateKey) = await provider.GenerateMlDsaKeyPairAsync("ML-DSA-65");

SignMlDsaAsync

byte[] data = Encoding.UTF8.GetBytes("Browser-signierte Daten");
byte[] signature = await provider.SignMlDsaAsync(data, privateKey);

VerifyMlDsaAsync

bool isValid = await provider.VerifyMlDsaAsync(data, signature, publicKey);

ML-KEM Operationen

GenerateMlKemKeyPairAsync

var (publicKey, privateKey) = await provider.GenerateMlKemKeyPairAsync("ML-KEM-768");

EncapsulateAsync

var (sharedSecret, ciphertext) = await provider.EncapsulateAsync(recipientPublicKey);

DecapsulateAsync

byte[] sharedSecret = await provider.DecapsulateAsync(ciphertext, privateKey);

Key Derivation Operationen

Pbkdf2Async

PBKDF2 über Web Crypto API.

byte[] salt = await provider.RandomBytesAsync(32);
 
byte[] derivedKey = await provider.Pbkdf2Async(
    password: "UserPassword",
    salt: salt,
    iterations: 100000,
    outputLength: 32,
    hash: "SHA-256");

Pbkdf2WithPqSaltAsync

PBKDF2 mit PQ-verstärktem Salt.

byte[] key = await provider.Pbkdf2WithPqSaltAsync(
    password: "UserPassword",
    baseSalt: baseSalt,
    pqPublicKey: recipientPqPublicKey,
    iterations: 100000,
    outputLength: 32);

Argon2idAsync

Memory-hard KDF via OpenSSL WASM.

byte[] key = await provider.Argon2idAsync(
    password: passwordBytes,
    salt: salt,
    outputLength: 32,
    iterations: 3,
    memoryKiB: 65536,
    parallelism: 4);

Stream/Chunked Encryption

EncryptChunkedAsync / DecryptChunkedAsync

Für große Datenmengen mit Chunk-Verarbeitung.

byte[] key = await provider.RandomBytesAsync(32);
 
// Verschlüsseln
byte[] encrypted = await provider.EncryptChunkedAsync(
    plaintext,
    key,
    chunkSize: 65536);  // 64 KB Chunks
 
// Entschlüsseln
byte[] decrypted = await provider.DecryptChunkedAsync(encrypted, key);

EncryptStreamWithPqKeyAsync / DecryptStreamWithPqKeyAsync

Kombiniert ML-KEM Key Exchange mit chunked Encryption.

// Verschlüsseln für Empfänger
var (kemCiphertext, encryptedData) = await provider.EncryptStreamWithPqKeyAsync(
    plaintext,
    recipientPublicKey,
    chunkSize: 65536);
 
// Entschlüsseln
byte[] decrypted = await provider.DecryptStreamWithPqKeyAsync(
    kemCiphertext,
    encryptedData,
    privateKey);

HKDF Operationen

HkdfExtractAsync

byte[] prk = await provider.HkdfExtractAsync(salt, inputKeyMaterial);

HkdfExpandAsync

byte[] okm = await provider.HkdfExpandAsync(prk, info, outputLength: 32);

HkdfDeriveKeyAsync

Extract + Expand in einem Schritt.

byte[] key = await provider.HkdfDeriveKeyAsync(
    ikm: sharedSecret,
    length: 32,
    salt: optionalSalt,
    info: contextInfo);

DeriveHybridKeyAsync

Kombiniert klassisches und PQ Secret.

byte[] hybridKey = await provider.DeriveHybridKeyAsync(
    classicSecret: ecdhSecret,
    pqSecret: mlKemSecret,
    outputLength: 32);

TLS 1.3 Key Derivation

DeriveTls13KeysAsync

Tls13KeyMaterial keys = await provider.DeriveTls13KeysAsync(
    sharedSecret,
    clientHello,
    serverHello);
 
// Zugriff auf abgeleitete Secrets
var clientKey = keys.ClientHandshakeTrafficSecret;
var serverKey = keys.ServerHandshakeTrafficSecret;

Hybrid Signatures

CreateHybridSignatureAsync

Erstellt hybride Signatur aus klassischer Signatur und ML-DSA.

// Klassische Signatur erstellen (z.B. ECDSA)
byte[] classicSig = CreateEcdsaSignature(data);
 
// Hybrid-Signatur erstellen
byte[] hybridSig = await provider.CreateHybridSignatureAsync(
    data,
    classicSig,
    mlDsaPrivateKey);

Utility Methoden

RandomBytesAsync

Kryptographisch sichere Zufallszahlen via Web Crypto API.

byte[] random = await provider.RandomBytesAsync(32);

Zertifikat-Operationen

CreateEphemeralCertificateAsync

byte[] certBytes = await provider.CreateEphemeralCertificateAsync(
    "CN=Browser Certificate",
    TimeSpan.FromHours(1),
    mlDsaPrivateKey);

SignCertificateAsync

byte[] signedCert = await provider.SignCertificateAsync(tbsCertificate, privateKey);

Methoden-Übersicht

ML-DSA

Methode Parameter Rückgabe
GenerateMlDsaKeyPairAsync string algorithm Task<(byte[], byte[])>
SignMlDsaAsync byte[] data, byte[] privateKey Task<byte[]>
VerifyMlDsaAsync byte[] data, byte[] signature, byte[] publicKey Task<bool>

ML-KEM

Methode Parameter Rückgabe
GenerateMlKemKeyPairAsync string algorithm Task<(byte[], byte[])>
EncapsulateAsync byte[] publicKey Task<(byte[], byte[])>
DecapsulateAsync byte[] ciphertext, byte[] privateKey Task<byte[]>

Key Derivation

Methode Parameter Rückgabe
Pbkdf2Async string password, byte[] salt, int iterations, int outputLength, string hash Task<byte[]>
Pbkdf2WithPqSaltAsync string password, byte[] baseSalt, byte[] pqPublicKey, int iterations, int outputLength Task<byte[]>
Argon2idAsync byte[]/string password, byte[] salt, int outputLength, int iterations, int memoryKiB, int parallelism Task<byte[]>

HKDF

Methode Parameter Rückgabe
HkdfExtractAsync byte[] salt, byte[] ikm Task<byte[]>
HkdfExpandAsync byte[] prk, byte[] info, int length Task<byte[]>
HkdfDeriveKeyAsync byte[] ikm, int length, byte[]? salt, byte[]? info Task<byte[]>
DeriveHybridKeyAsync byte[] classicSecret, byte[] pqSecret, int outputLength Task<byte[]>

Encryption

Methode Parameter Rückgabe
EncryptChunkedAsync byte[] plaintext, byte[] key, int chunkSize Task<byte[]>
DecryptChunkedAsync byte[] ciphertext, byte[] key Task<byte[]>
EncryptStreamWithPqKeyAsync byte[] plaintext, byte[] publicKey, int chunkSize Task<(byte[], byte[])>
DecryptStreamWithPqKeyAsync byte[] kemCiphertext, byte[] encryptedData, byte[] privateKey Task<byte[]>

Utility

Methode Parameter Rückgabe
RandomBytesAsync int length Task<byte[]>
CreateHybridSignatureAsync byte[] data, byte[] classicSig, byte[] pqPrivKey Task<byte[]>
DeriveTls13KeysAsync byte[] sharedSecret, byte[] clientHello, byte[] serverHello Task<Tls13KeyMaterial>

Vollständiges Beispiel

// Blazor Component
@page "/crypto-demo"
@inject ICryptoProvider CryptoProvider
 
<h3>PQ Crypto Demo</h3>
<p>Status: @_status</p>
 
@code {
    private string _status = "Initializing...";
 
    protected override async Task OnInitializedAsync()
    {
        try
        {
            await CryptoProvider.InitializeAsync();
 
            // Key Exchange Demo
            var (alicePublic, alicePrivate) =
                await CryptoProvider.GenerateMlKemKeyPairAsync();
 
            var (sharedSecret, ciphertext) =
                await CryptoProvider.EncapsulateAsync(alicePublic);
 
            var decapsulated =
                await CryptoProvider.DecapsulateAsync(ciphertext, alicePrivate);
 
            bool keysMatch = sharedSecret.SequenceEqual(decapsulated);
 
            // Signatur Demo
            var (sigPub, sigPriv) =
                await CryptoProvider.GenerateMlDsaKeyPairAsync();
 
            byte[] message = Encoding.UTF8.GetBytes("Test Message");
            byte[] signature = await CryptoProvider.SignMlDsaAsync(message, sigPriv);
            bool isValid = await CryptoProvider.VerifyMlDsaAsync(message, signature, sigPub);
 
            _status = $"Keys match: {keysMatch}, Signature valid: {isValid}";
        }
        catch (Exception ex)
        {
            _status = $"Error: {ex.Message}";
        }
    }
}

Sicherheitshinweise

  • Erfordert .NET 8.0+ mit Blazor WebAssembly
  • Browser-Speicher ist weniger sicher als Server-Speicher
  • Private Keys sollten nicht langfristig im Browser gespeichert werden
  • Für sensible Operationen: Server-seitige Verarbeitung bevorzugen
  • openssl.wasm und wvds-crypto.js müssen korrekt geladen sein
Best Practices für Browser-Krypto:
  • Ephemere Keys für Session-basierte Verschlüsselung verwenden
  • Sensible Private Keys auf Server belassen
  • IndexedDB/localStorage nicht für unverschlüsselte Keys verwenden
  • CSP-Header korrekt konfigurieren für WASM

Siehe auch


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional