====== 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: * JavaScript Interop zu WebAssembly-kompiliertem OpenSSL * Vollständig asynchrone API (erforderlich für JS Interop) * Identische Funktionalität wie ''NativeCryptoProvider'' ===== 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(sp => new WasmCryptoProvider(sp.GetRequiredService())); ===== 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'': ===== 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 | | ''VerifyMlDsaAsync'' | byte[] data, byte[] signature, byte[] publicKey | Task | ==== ML-KEM ==== ^ Methode ^ Parameter ^ Rückgabe ^ | ''GenerateMlKemKeyPairAsync'' | string algorithm | Task<(byte[], byte[])> | | ''EncapsulateAsync'' | byte[] publicKey | Task<(byte[], byte[])> | | ''DecapsulateAsync'' | byte[] ciphertext, byte[] privateKey | Task | ==== Key Derivation ==== ^ Methode ^ Parameter ^ Rückgabe ^ | ''Pbkdf2Async'' | string password, byte[] salt, int iterations, int outputLength, string hash | Task | | ''Pbkdf2WithPqSaltAsync'' | string password, byte[] baseSalt, byte[] pqPublicKey, int iterations, int outputLength | Task | | ''Argon2idAsync'' | byte[]/string password, byte[] salt, int outputLength, int iterations, int memoryKiB, int parallelism | Task | ==== HKDF ==== ^ Methode ^ Parameter ^ Rückgabe ^ | ''HkdfExtractAsync'' | byte[] salt, byte[] ikm | Task | | ''HkdfExpandAsync'' | byte[] prk, byte[] info, int length | Task | | ''HkdfDeriveKeyAsync'' | byte[] ikm, int length, byte[]? salt, byte[]? info | Task | | ''DeriveHybridKeyAsync'' | byte[] classicSecret, byte[] pqSecret, int outputLength | Task | ==== Encryption ==== ^ Methode ^ Parameter ^ Rückgabe ^ | ''EncryptChunkedAsync'' | byte[] plaintext, byte[] key, int chunkSize | Task | | ''DecryptChunkedAsync'' | byte[] ciphertext, byte[] key | Task | | ''EncryptStreamWithPqKeyAsync'' | byte[] plaintext, byte[] publicKey, int chunkSize | Task<(byte[], byte[])> | | ''DecryptStreamWithPqKeyAsync'' | byte[] kemCiphertext, byte[] encryptedData, byte[] privateKey | Task | ==== Utility ==== ^ Methode ^ Parameter ^ Rückgabe ^ | ''RandomBytesAsync'' | int length | Task | | ''CreateHybridSignatureAsync'' | byte[] data, byte[] classicSig, byte[] pqPrivKey | Task | | ''DeriveTls13KeysAsync'' | byte[] sharedSecret, byte[] clientHello, byte[] serverHello | Task | ===== Vollständiges Beispiel ===== // Blazor Component @page "/crypto-demo" @inject ICryptoProvider CryptoProvider

PQ Crypto Demo

Status: @_status

@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 ===== * [[.:start|Providers Namespace]] * [[.:icryptoprovider|ICryptoProvider]] * [[.:nativecryptoprovider|NativeCryptoProvider]] * [[.:cryptoproviderfactory|CryptoProviderFactory]] * [[de:int:pqcrypt:developer:integration:blazor-wasm|Blazor WASM Integration]] ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//