Diese Anleitung erklärt, wie Sie OpenSSL für Blazor WebAssembly kompilieren.
WebAssembly (WASM) ist ein binäres Format, das im Browser läuft. Es ermöglicht:
Wann brauche ich WASM?
| Anwendungstyp | WASM nötig? |
| ————– | ————- |
| Blazor WebAssembly | Ja |
| Blazor Server | Nein (nutzt Windows/Linux Build) |
| ASP.NET Core API | Nein |
| Desktop App (.NET) | Nein |
Zusätzlich zu den Standard-Tools benötigen Sie:
WASM-Builds sind nur unter Linux/WSL möglich, nicht direkt unter Windows!
Der WASM-Build erstellt:
openssl.js - JavaScript-Loaderopenssl.wasm - WebAssembly-Modul# WSL Terminal öffnen
wsl
# Emscripten Environment laden source /opt/emsdk/emsdk_env.sh # Prüfen emcc --version # Sollte zeigen: emcc (Emscripten gcc/clang-like replacement) 3.x.x
# Verzeichnisse erstellen mkdir -p /mnt/d/Projects/openssl-3.6.0/wasm-build mkdir -p /mnt/d/Projects/openssl-3.6.0/wasm-install cd /mnt/d/Projects/openssl-3.6.0/wasm-build
emconfigure /mnt/d/Projects/openssl-3.6.0/src/Configure \ linux-generic32 \ --prefix=/mnt/d/Projects/openssl-3.6.0/wasm-install \ --openssldir=/mnt/d/Projects/openssl-3.6.0/wasm-install/ssl \ no-asm \ no-threads \ no-shared \ no-dso \ no-engine \ no-hw \ no-async \ no-sock \ no-dgram \ no-tests \ no-apps \ -DOPENSSL_NO_SECURE_MEMORY \ CC=emcc \ AR=emar \ RANLIB=emranlib
Erklärung der Optionen:
| Option | Bedeutung |
| ——– | ———– |
linux-generic32 | Generische 32-bit Plattform |
no-asm | Keine Assembly (WASM kann kein x86 Assembly) |
no-threads | Keine Threads (Web Workers separat) |
no-shared | Nur statische Library |
no-sock | Keine Socket-Unterstützung |
no-tests | Keine Tests bauen |
no-apps | Kein openssl CLI Tool |
emmake make -j$(nproc) build_libs
emmake make install_sw
Für die Integration in Blazor brauchen wir einen Wrapper:
// WvdS Crypto WASM Wrapper #include <emscripten.h> #include <openssl/evp.h> #include <openssl/x509.h> #include <openssl/err.h> #include <string.h> #include <stdlib.h> // Initialisierung EMSCRIPTEN_KEEPALIVE int wvds_init(void) { OPENSSL_init_crypto( OPENSSL_INIT_LOAD_CRYPTO_STRINGS | OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL); return 1; } // OpenSSL Version EMSCRIPTEN_KEEPALIVE const char* wvds_get_version(void) { return OPENSSL_VERSION_TEXT; } // ML-DSA Schlüsselgenerierung EMSCRIPTEN_KEEPALIVE int wvds_mldsa_keygen(const char* algorithm, unsigned char** pub_key, int* pub_len, unsigned char** priv_key, int* priv_len) { EVP_PKEY_CTX *ctx = NULL; EVP_PKEY *pkey = NULL; int ret = 0; // Algorithmus-Name für OpenSSL const char* ossl_alg = algorithm; if (strcmp(algorithm, "ML-DSA-44") == 0) ossl_alg = "mldsa44"; else if (strcmp(algorithm, "ML-DSA-65") == 0) ossl_alg = "mldsa65"; else if (strcmp(algorithm, "ML-DSA-87") == 0) ossl_alg = "mldsa87"; ctx = EVP_PKEY_CTX_new_from_name(NULL, ossl_alg, NULL); if (!ctx) goto err; if (EVP_PKEY_keygen_init(ctx) <= 0) goto err; if (EVP_PKEY_keygen(ctx, &pkey) <= 0) goto err; // Public Key exportieren *pub_len = i2d_PUBKEY(pkey, NULL); *pub_key = malloc(*pub_len); unsigned char *p = *pub_key; i2d_PUBKEY(pkey, &p); // Private Key exportieren *priv_len = i2d_PrivateKey(pkey, NULL); *priv_key = malloc(*priv_len); p = *priv_key; i2d_PrivateKey(pkey, &p); ret = 1; err: EVP_PKEY_free(pkey); EVP_PKEY_CTX_free(ctx); return ret; } // ML-DSA Signieren EMSCRIPTEN_KEEPALIVE int wvds_mldsa_sign(const unsigned char* data, int data_len, const unsigned char* priv_key, int priv_len, unsigned char** sig, int* sig_len) { // ... (vollständige Implementierung im Build-Skript) return 1; } // ML-DSA Verifizieren EMSCRIPTEN_KEEPALIVE int wvds_mldsa_verify(const unsigned char* data, int data_len, const unsigned char* sig, int sig_len, const unsigned char* pub_key, int pub_len) { // ... (vollständige Implementierung im Build-Skript) return 1; } // Speicher freigeben EMSCRIPTEN_KEEPALIVE void wvds_free(void* ptr) { free(ptr); }
INSTALL_DIR=/mnt/d/Projects/openssl-3.6.0/wasm-install OUTPUT=/mnt/d/Projects/openssl-3.6.0/wasm-build emcc $OUTPUT/wvds_crypto_wrapper.c \ -I"$INSTALL_DIR/include" \ -L"$INSTALL_DIR/lib" \ -lcrypto \ -Os \ -s WASM=1 \ -s MODULARIZE=1 \ -s EXPORT_NAME="OpenSSLModule" \ -s EXPORTED_FUNCTIONS='["_wvds_init","_wvds_get_version","_wvds_mldsa_keygen","_wvds_mldsa_sign","_wvds_mldsa_verify","_wvds_free","_malloc","_free"]' \ -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap","getValue","setValue","UTF8ToString"]' \ -s ALLOW_MEMORY_GROWTH=1 \ -o "$OUTPUT/openssl.js"
Nach dem Build:
wasm-build/ ├── openssl.js # JavaScript Loader (~150 KB) └── openssl.wasm # WebAssembly Modul (~2 MB)
cp openssl.js openssl.wasm /mnt/d/MeinProjekt/wwwroot/
<script src="openssl.js"></script> <script> var cryptoModule = null; OpenSSLModule().then(function(module) { cryptoModule = module; module._wvds_init(); console.log("OpenSSL Version:", module.ccall('wvds_get_version', 'string', [], [])); }); </script>
// In Ihrer Blazor-Komponente @inject IJSRuntime JS public async Task<string> GetOpenSslVersion() { return await JS.InvokeAsync<string>("eval", "cryptoModule.ccall('wvds_get_version', 'string', [], [])"); }
Für den kompletten Build mit ML-DSA und ML-KEM:
#!/bin/bash # build-wasm.sh - OpenSSL 3.6.0 WASM Build set -e OPENSSL_SRC="/mnt/d/Projects/openssl-3.6.0/src" OUTPUT_DIR="/mnt/d/Projects/openssl-3.6.0/wasm-build" INSTALL_DIR="/mnt/d/Projects/openssl-3.6.0/wasm-install" echo "=== OpenSSL WASM Build ===" # Emscripten aktivieren source /opt/emsdk/emsdk_env.sh # Verzeichnisse erstellen mkdir -p "$OUTPUT_DIR" "$INSTALL_DIR" cd "$OUTPUT_DIR" # Konfigurieren emconfigure "$OPENSSL_SRC/Configure" \ linux-generic32 \ --prefix="$INSTALL_DIR" \ no-asm no-threads no-shared no-dso no-engine \ no-async no-sock no-dgram no-tests no-apps \ CC=emcc AR=emar RANLIB=emranlib # Bauen emmake make -j$(nproc) build_libs # Installieren emmake make install_sw echo "=== Build abgeschlossen ==="
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional