3.3 WASM Build for Blazor

This guide explains how to compile OpenSSL for Blazor WebAssembly.


What is WebAssembly?

WebAssembly (WASM) is a binary format that runs in the browser. It enables:

  • High-performance code in the browser
  • Using languages like C/C++ on the web
  • Blazor WebAssembly applications

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

Prerequisites

In addition to standard tools you need:

WASM builds are only possible under Linux/WSL, not directly under Windows!


Build Overview

The WASM build creates:

  • openssl.js - JavaScript loader
  • openssl.wasm - WebAssembly module
  • C wrapper for Post-Quantum functions

Build Steps

Step 1: Open WSL

# Open WSL terminal
wsl

Step 2: Activate Emscripten

# Load Emscripten environment
source /opt/emsdk/emsdk_env.sh
 
# Verify
emcc --version
# Should show: emcc (Emscripten gcc/clang-like replacement) 3.x.x

Step 3: Prepare Build Directory

# 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

Step 4: Configure OpenSSL for WASM

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

Step 5: Compile

emmake make -j$(nproc) build_libs

Step 6: Install

emmake make install_sw

Create JavaScript Wrapper

For integration into Blazor we need a wrapper:

wvds_crypto_wrapper.c

// 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);
}

Compile Wrapper

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"

Result

After the build:

wasm-build/
├── openssl.js    # JavaScript Loader (~150 KB)
└── openssl.wasm  # WebAssembly Module (~2 MB)

Integrate into Blazor

1. Copy Files

cp openssl.js openssl.wasm /mnt/d/MyProject/wwwroot/

2. Load in index.html

<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>

3. Call from Blazor

// In your Blazor component
@inject IJSRuntime JS
 
public async Task<string> GetOpenSslVersion()
{
    return await JS.InvokeAsync<string>("eval",
        "cryptoModule.ccall('wvds_get_version', 'string', [], [])");
}

Continue to


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

Zuletzt geändert: on 2026/01/29 at 09:19 PM