This guide explains how to compile OpenSSL for Blazor WebAssembly.
WebAssembly (WASM) is a binary format that runs in the browser. It enables:
When do I need WASM?
| Application Type | WASM needed? |
| —————— | ————– |
| Blazor WebAssembly | Yes |
| Blazor Server | No (uses Windows/Linux build) |
| ASP.NET Core API | No |
| Desktop App (.NET) | No |
In addition to standard tools you need:
WASM builds are only possible under Linux/WSL, not directly under Windows!
The WASM build creates:
openssl.js - JavaScript loaderopenssl.wasm - WebAssembly module# Open WSL terminal
wsl
# Load Emscripten environment source /opt/emsdk/emsdk_env.sh # Verify emcc --version # Should show: emcc (Emscripten gcc/clang-like replacement) 3.x.x
# Create directories 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
Explanation of options:
| Option | Meaning |
| ——– | ——— |
linux-generic32 | Generic 32-bit platform |
no-asm | No assembly (WASM can't use x86 assembly) |
no-threads | No threads (Web Workers separate) |
no-shared | Static library only |
no-sock | No socket support |
no-tests | Don't build tests |
no-apps | No openssl CLI tool |
emmake make -j$(nproc) build_libs
emmake make install_sw
For integration into Blazor we need a 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> // Initialization 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 Key Generation 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; // Algorithm name for 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; // Export public key *pub_len = i2d_PUBKEY(pkey, NULL); *pub_key = malloc(*pub_len); unsigned char *p = *pub_key; i2d_PUBKEY(pkey, &p); // Export private key *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; } // Free memory 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_free","_malloc","_free"]' \ -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap","getValue","setValue","UTF8ToString"]' \ -s ALLOW_MEMORY_GROWTH=1 \ -o "$OUTPUT/openssl.js"
After the build:
wasm-build/ ├── openssl.js # JavaScript Loader (~150 KB) └── openssl.wasm # WebAssembly Module (~2 MB)
cp openssl.js openssl.wasm /mnt/d/MyProject/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 your Blazor component @inject IJSRuntime JS public async Task<string> GetOpenSslVersion() { return await JS.InvokeAsync<string>("eval", "cryptoModule.ccall('wvds_get_version', 'string', [], [])"); }
Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional