Make OpenSSL an optional crypto backend (closes #297)
OpenSSL was mandatory; it is now one of three interchangeable backends.
libjwt builds and passes its test suite with any non-empty subset of
{OpenSSL, GnuTLS, MbedTLS} (default WITH_OPENSSL=ON).
Core decoupling:
- Replace the OPENSSL_cleanse-based jwt_scrub_and_free (used in ~60 sites
across the core and every backend) with a portable jwt_cleanse; guard the
<openssl/crypto.h> include under HAVE_OPENSSL and include <limits.h>
explicitly so non-OpenSSL builds need no OpenSSL header.
Native key2jwk:
- Split openssl/jwk-export.c into a crypto-free common jwk-export.c (the new
jwk_export_t struct + key2jwk_params ops member, JWK assembly, base64url
encoding, kid via the backend RNG, and the HMAC fallback) plus native
per-backend extractors for OpenSSL, GnuTLS, and MbedTLS. Drops the
cross-backend dependency on openssl_key2jwk.
RSA-OAEP (SHA-1):
- The GnuTLS -> OpenSSL fallback for plain RSA-OAEP (which GnuTLS/nettle
cannot perform), is now gated on HAVE_OPENSSL; without OpenSSL it fails
cleanly instead of referencing an undefined symbol.
Build:
- Add WITH_OPENSSL (default ON); move OpenSSL detection, sources, links, and
HAVE_CRYPTO inside if(WITH_OPENSSL); stop linking OpenSSL into the tools;
make the pkg-config Requires conditional; require GnuTLS >= 3.8.4 for an
OpenSSL-less GnuTLS build (older GnuTLS has no native JWK/JWE path).
CI:
- Add a build-linux-combos matrix exercising all seven backend combinations
(build + full test suite) in a debian:forky container.
Tests cover each backend's native key2jwk, with skips for genuinely
unsupported cases (MbedTLS+EdDSA/RSA-PSS, native GnuTLS+secp256k1); the CLI
golden-JWKS test skips on backends that cannot convert every key type.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Ben Collins <bcollins@libjwt.io>
| Standard | RFC | Description |
|---|---|---|
JWS | :page_facing_up: RFC-7515 | JSON Web Signature |
JWE | :page_facing_up: RFC-7516 | JSON Web Encryption |
JWK | :page_facing_up: RFC-7517 | JSON Web Keys and Sets |
JWA | :page_facing_up: RFC-7518 | JSON Web Algorithms |
JWT | :page_facing_up: RFC-7519 | JSON Web Token |
[!NOTE] Throughout this documentation you will see links such as the ones above to RFC documents. These are relevant to that particular part of the library and are helpful to understand some of the specific standards that shaped the development of LibJWT.
[!NOTE] At least one crypto backend is required, but any non-empty combination works. OpenSSL is enabled by default and can be disabled with
-DWITH_OPENSSL=OFF. Each backend parses and converts JWK(S) natively. A GnuTLS-only build (no OpenSSL) requires GnuTLS >= 3.8.4.
JWS Algorithm alg | OpenSSL | GnuTLS | MbedTLS |
|---|---|---|---|
HS256 HS384 HS512 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
ES256 ES384 ES512 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
RS256 RS384 RS512 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
EdDSA using ED25519 | :white_check_mark: | :white_check_mark: | :x: |
EdDSA using ED448 | :white_check_mark: | :white_check_mark: >= 3.8.8 | :x: |
PS256 PS384 PS512 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
ES256K | :white_check_mark: | :x: | :white_check_mark: |
LibJWT supports JWE (RFC 7516) in both the Compact Serialization and the JSON Serialization (the Flattened form and the General form with one or more recipients). A JWE uses two algorithms: a key management algorithm (alg) and a content encryption algorithm (enc).
| JWE serialization | Recipients | Supported |
|---|---|---|
| Compact (RFC 7516 §7.1) | one | :white_check_mark: |
| JSON Flattened (RFC 7516 §7.2.2) | one | :white_check_mark: |
| JSON General (RFC 7516 §7.2.1) | one or more | :white_check_mark: |
With the JSON serializations the plaintext is encrypted once with a single CEK; each recipient wraps that CEK independently, so any recipient's key can decrypt the token. They also carry an optional shared unprotected header, per-recipient headers, and an application AAD member.
Legend: :white_check_mark: native implementation · :large_blue_circle: supported, using OpenSSL as a fallback · :x: not supported
JWE key management alg | OpenSSL | GnuTLS | MbedTLS |
|---|---|---|---|
dir (Direct Encryption) | :white_check_mark: | :white_check_mark: | :white_check_mark: |
A128KW A192KW A256KW | :white_check_mark: | :white_check_mark: | :white_check_mark: |
RSA-OAEP (SHA-1) | :white_check_mark: | :large_blue_circle: | :white_check_mark: |
RSA-OAEP-256 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
ECDH-ES (+ +A128KW/+A192KW/+A256KW) | :white_check_mark: | :white_check_mark: | :white_check_mark: |
JWE content encryption enc | OpenSSL | GnuTLS | MbedTLS |
|---|---|---|---|
A128GCM A192GCM A256GCM | :white_check_mark: | :white_check_mark: | :white_check_mark: |
A128CBC-HS256 A192CBC-HS384 A256CBC-HS512 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
[!NOTE]
ECDH-ESsupports both Direct Key Agreement and the+A*KWkey wrapping modes, on the EC curves P-256/384/521 and the OKP curves X25519/X448, with optionalapu/apvPartyInfo.RSA1_5andzip(compression) are intentionally not supported. Each backend implements JWE natively, with one exception: GnuTLS/Nettle cannot perform RSA-OAEP with SHA-1, so under the GnuTLS backend plainRSA-OAEPfalls back to OpenSSL when it is compiled in, and is otherwise unsupported (RSA-OAEP-256is native).
:link: Current Docs
:link: Legacy Docs v2.1.1
:link: GitHub Repo
LibJWT is available in most Linux distributions as well as through Homebrew for Linux, macOS, and Windows.
$ mkdir build $ cd build $ cmake .. $ make