Become a member!

JOSE/JWT Asymmetric Signing in DelphiMVCFramework: RS256, ECDSA, EdDSA and Modern OpenSSL

🌐
This article is also available in other languages:
🇮🇹 Italiano  •  🇪🇸 Español

JOSE, JWT and Industry Standards

JOSE (Javascript Object Signing and Encryption) is the IETF family of standards that defines how to sign and encrypt JSON-formatted data. It includes:

  • JWT (RFC 7519) — the token format (header, payload, signature)
  • JWS (RFC 7515) — digital signatures for tokens
  • JWA (RFC 7518) — the supported cryptographic algorithms
  • JWK (RFC 7517) — JSON representation of cryptographic keys

Until now, DelphiMVCFramework supported JWT with symmetric HMAC algorithms (HS256/384/512) — the most common choice, perfectly adequate for the majority of applications. However, as architectures grow toward microservices, the symmetric model forces a trade-off: either every server knows the same secret (increasing the attack surface), or every service must validate tokens by calling a single centralized auth server (creating a bottleneck and a single point of failure). Asymmetric signing eliminates both problems.

Asymmetric JWT Signing in Delphi: the Missing Piece

With asymmetric signing, the paradigm shifts: the auth server signs with a private key that never leaves that machine, while all other services verify with the public key, which can be freely distributed. This is the standard for OpenID Connect, OAuth 2.0, and any modern microservice architecture.

DelphiMVCFramework now implements full coverage of the JWA algorithms for JWS:

Family Algorithms Notes
HMAC HS256, HS384, HS512 Shared secret (already supported)
RSA PKCS#1 RS256, RS384, RS512 Maximum compatibility, OIDC
RSA-PSS PS256, PS384, PS512 More secure than RS*, same RSA keys
ECDSA ES256, ES384, ES512 Much smaller keys than RSA
EdDSA Ed25519 Fastest, 32-byte keys

Sixteen algorithms. Four cryptographic families. All accessible through the same IJWTSigner interface, which integrates with the existing JWT middleware without changing a single line in your application logic.

This means a DMVCFramework application can now natively interoperate with identity providers like Auth0, Keycloak, Azure AD and Google Identity, which issue RS256 or ES256 tokens.

OpenSSL 3.x via TaurusTLS

A deliberate architectural choice: the new signers do not use the OpenSSL 1.0.x libraries shipped with Indy (End of Life since late 2019). They use TaurusTLS to access OpenSSL 1.1.1+ and 3.x — modern algorithms, active security patches, superior performance.

Optional Dependency: Zero Impact on Existing Code

If you use HS256, you don’t need to change anything. TaurusTLS is required only when using asymmetric algorithms.

The IJWTSigner interface is defined in the framework core with no external dependencies. The RSA/ECDSA/EdDSA implementations live in a separate unit (MVCFramework.JWT.RSA) that you include only when you need it.

How to Switch from HS256 to RS256 in DelphiMVCFramework

// BEFORE: HMAC (shared secret)
Engine.AddMiddleware(UseJWTMiddleware(AuthHandler, ClaimsSetup,
  'my_shared_secret', '/login', [...], 300));

// AFTER: RSA (asymmetric key pair)
uses MVCFramework.JWT.RSA;

var lSigner := TRSAJWTSigner.CreateFromFiles(JWT_RS256,
  'keys/private.pem', 'keys/public.pem');

Engine.AddMiddleware(UseJWTMiddleware(AuthHandler, ClaimsSetup,
  lSigner, '/login', [...], 300));

One extra unit in the uses clause, one line to create the signer, one different parameter in the middleware. Controllers, auth handler, claims: all identical.

And if a microservice only needs to verify tokens without signing them:

var lSigner := TRSAJWTSigner.CreateVerifyOnly(JWT_RS256,
  TFile.ReadAllText('public.pem'));

The private key doesn’t even exist on that machine.

Full Example: Create and Verify an RS256 Token

uses
  MVCFramework.JWT,
  MVCFramework.JWT.RSA;  // requires TaurusTLS + OpenSSL 1.1.1+

// --- 1. CREATE A TOKEN (auth server) ---

var lSigner := TRSAJWTSigner.CreateFromFiles(
  JWT_RS256, 'keys/private.pem', 'keys/public.pem');

var lJWT := TJWT.Create(lSigner);
try
  lJWT.Claims.Issuer := 'my-auth-server';
  lJWT.Claims.Subject := 'user42';
  lJWT.Claims.ExpirationTime := Now + OneHour;
  lJWT.Claims.IssuedAt := Now;
  lJWT.CustomClaims['role'] := 'admin';

  var lToken := lJWT.GetToken;
  // eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOi...
finally
  lJWT.Free;
end;

// --- 2. VERIFY THE TOKEN (microservice, public key only) ---

var lVerifier := TRSAJWTSigner.CreateVerifyOnly(
  JWT_RS256, TFile.ReadAllText('keys/public.pem'));

var lVerifyJWT := TJWT.Create(lVerifier);
try
  var lError: string;
  if lVerifyJWT.LoadToken(lToken, lError) then
  begin
    WriteLn('Issuer: ', lVerifyJWT.Claims.Issuer);       // my-auth-server
    WriteLn('Subject: ', lVerifyJWT.Claims.Subject);      // user42
    WriteLn('Role: ', lVerifyJWT.CustomClaims['role']);    // admin
  end;
finally
  lVerifyJWT.Free;
end;

All the Complexity Lives in the Middleware

In DelphiMVCFramework you don’t need to write code like the above for every request. The JWT middleware handles the entire cycle automatically: it intercepts requests, extracts the token from the Authorization: Bearer header, verifies it with the configured signer, populates Context.LoggedUser with claims and roles, and rejects invalid requests with the appropriate HTTP status code. Your controller only receives the request if the token is valid — and at that point the authenticated user and their roles are already available, without a single line of JWT code.

Built-in JWT Security

Beyond the algorithms, the implementation includes concrete protections:

  • Algorithm Confusion Prevention: the token cannot declare a different algorithm from the one configured in the signer — prevents the well-known attack where an RS256 token is re-signed as HS256 using the public key
  • Constant-Time Comparison: HMAC verification uses constant-time byte comparison to prevent timing attacks
  • Sign-Only / Verify-Only: a microservice that only needs to verify tokens can be configured with just the public key — the private key is never deployed on that machine, eliminating the risk of it being compromised

Quality: No Feature Ships Without Unit Tests

Like every feature in DelphiMVCFramework, JOSE/JWS support is backed by a comprehensive suite of automated unit tests. No feature is ever released without tests that verify correct behavior across all supported platforms. Tests cover all 16 algorithms on Win32 and Win64, including tamper detection, wrong keys, algorithm mismatch, and backward compatibility with existing code.

What’s Next? JWE and JWK

Full JWS support is the first step. The IJWTSigner-based architecture paves the way for JWE (JSON Web Encryption, RFC 7516) for encrypted tokens and JWK (RFC 7517) for JSON-formatted key management — including JWKS endpoints for automatic public key distribution. Stay tuned.

At ITDevCon 2026 Spring Edition

This is just one of the new features I’ll be presenting at ITDevCon 2026 Spring Edition. We’ll see live demos of microservice architectures with asymmetric signing, integration with modern identity providers, and much more.

Everything described in this article is already implemented and working in version 3.5.0-silicon, currently in beta on GitHub. The stable release is planned before ITDevCon 2026 Spring Edition.

Comments

comments powered by Disqus