Mit dem folgenden Skript kann man OpenSSL für Webassembly compilieren.

Voraussetzungen

  • Emscripten (https://developer.mozilla.org/en-US/docs/WebAssembly/C_to_wasm)
  • Das Skript wurde bisher nur mit Linux getestet
  • Das Skript mk-openssl-webassemby.sh:
#!/bin/bash

#######################################################################################
# prerequisites
# * Emscripten
#######################################################################################

GIT=0

# ==> Select latest version for security reasons
OPENSSL_VERSION="1.1.0h"
#OPENSSL_VERSION="1.1.1-pre8"

if [ "$GIT" -eq 1 ]; then
if [ ! -d openssl ]; then
git clone --depth 10 https://github.com/openssl/openssl.git
fi
cd openssl || exit 1
git pull
else
if [ ! -f openssl-${OPENSSL_VERSION}.tar.gz ]; then
wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz
fi

rm -rf "openssl"
mkdir openssl
tar xvf "openssl-${OPENSSL_VERSION}.tar.gz" --strip-components=1 --directory=openssl
cd openssl
fi

export CC=emcc
export CXX=emcc
export LINK=${CXX}
export ARCH_FLAGS=""
export ARCH_LINK=""
export CPPFLAGS=" ${ARCH_FLAGS} "
export CXXFLAGS=" ${ARCH_FLAGS} "
export CFLAGS=" ${ARCH_FLAGS} "
export LDFLAGS=" ${ARCH_LINK} "
echo $OSTYPE | grep -i darwin > /dev/null 2> /dev/null
if [ $? -eq 0 ]; then
OLD=$(pwd)
HOMEBREW_PREFIX="/usr/local/Cellar/binutils"
cd $HOMEBREW_PREFIX || exit 1
TARGET=$(find . -name ar)
export AR=${HOMEBREW_PREFIX}/${TARGET}
cd "$OLD"
fi

./Configure \
purify \
--openssldir=/tmp \
--api=1.1.0 \
no-engine no-dso no-dgram no-sock no-srtp no-stdio no-ui no-err no-ocsp no-psk no-stdio no-ts

PATH=$TOOLCHAIN_PATH:$PATH make


Die generierten Files in dem Verzeichnis kann man nun für die eigene Webassembly Anwendung verwenden.

Anwendung
hello.cpp
#include <stdio.h>
#include <string.h>
#include <string>
#include <stdlib.h>
#include <time.h>

#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>

#include "ssl.h"


using namespace std;


void sha256(char *string, char outputBuffer[SHA256_DIGEST_LENGTH+1]) {
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, string, strlen(string));
SHA256_Final(hash, &sha256);
int i = 0;
for(i = 0; i < SHA256_DIGEST_LENGTH; i++) {
sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
}
outputBuffer[SHA256_DIGEST_LENGTH] = 0;
}


string getTime() {
static char buf[256];
char fmt[] = "%a %b %d %H:%M:%S %Z %Y";
time_t now = time(NULL);
struct tm *tp;
tp = localtime(&now);
strftime(buf, sizeof(buf), fmt, tp);
return buf;
}

int main(int argc, char **argv) {
static string hello="Hello OpenSSL world "+getTime();
static unsigned char buffer[65];

char *chello = new char [hello.length()+1];
std::strcpy (chello, hello.c_str());
printf("%s\n", chello);
sha256(chello, (char *)buffer);
printf("sha256: %s\n", buffer);

doTest();

return 0;
}
ssl.cpp
#include <cstdio>
#include <iostream>
#include <openssl/pem.h>
#include <openssl/x509.h>

/* Generates a 2048-bit RSA key. */
EVP_PKEY * generate_key(int keySize)
{
/* Allocate memory for the EVP_PKEY structure. */
EVP_PKEY * pkey = EVP_PKEY_new();
if(!pkey)
{
return NULL;
}

/* Generate the RSA key and assign it to pkey. */
RSA * rsa = RSA_generate_key(keySize, RSA_F4, NULL, NULL);
if(!EVP_PKEY_assign_RSA(pkey, rsa))
{
EVP_PKEY_free(pkey);
return NULL;
}

/* The key has been generated, return it. */
return pkey;
}

/* Generates a self-signed x509 certificate. */
X509 * generate_x509(EVP_PKEY * pkey)
{
/* Allocate memory for the X509 structure. */
X509 * x509 = X509_new();
if(!x509)
{
return NULL;
}

/* Set the serial number. */
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);

/* This certificate is valid from now until exactly one year from now. */
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);

/* Set the public key for our certificate. */
X509_set_pubkey(x509, pkey);

/* We want to copy the subject name to the issuer name. */
X509_NAME * name = X509_get_subject_name(x509);

/* Set the country code and common name. */
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)"CA", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)"MyCompany", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"localhost", -1, -1, 0);

/* Now set the issuer name. */
X509_set_issuer_name(x509, name);

/* Actually sign the certificate with our key. */
if(!X509_sign(x509, pkey, EVP_sha256()))
{
X509_free(x509);
return NULL;
}

return x509;
}

bool write_to_stdout(EVP_PKEY * pkey, X509 * x509)
{
/* Write the key to disk. */
bool ret = PEM_write_PrivateKey(stdout, pkey, NULL, NULL, 0, NULL, NULL);

if(!ret)
{
return false;
}

/* Write the certificate to disk. */
ret = PEM_write_X509(stdout, x509);

if(!ret)
{
return false;
}

return true;
}


int doTest(void)
{
/* Generate the key. */
std::cout << "Generating RSA key..." << std::endl;

EVP_PKEY * pkey = generate_key(2048);
if(!pkey)
return 1;

/* Generate the certificate. */
std::cout << "Generating x509 certificate..." << std::endl;

X509 * x509 = generate_x509(pkey);
if(!x509)
{
EVP_PKEY_free(pkey);
return 1;
}

/* Write the private key and certificate out to disk. */
std::cout << "Writing key and certificate to stdout..." << std::endl;

bool ret = write_to_stdout(pkey, x509);
EVP_PKEY_free(pkey);
X509_free(x509);

if(ret)
{
std::cout << "Success!" << std::endl;
return 0;
}
else
return 1;
}


Makefile
all: hello.html

hello.html: hello.cpp ssl.cpp
emcc -L./openssl-1.1.0h -I./openssl-1.1.0h/include hello.cpp ssl.cpp -s WASM=1 -o hello.html -lssl -lcrypto --emrun

clean:
rm hello.html hello.wasm hello.js

Mit make lässt sich das compilieren, dabei entsteht unter Anderem eine Datei hello.html, die man mit einem aktuellen Browser ansehen kann.

In Kürze erscheint auf Github eine komplette Anwendung, die Democode für CMS Verschlüsselung und Signaturen enthält.