Developer instructions

Integratiegids voor Service Providers (SP’s).

1) Overzicht (SP ↔ beID Hermes Platform)

Als SP praat je enkel met het beID Hermes Platform.

  1. Maak een JSON‑payload met de gevraagde actie.
  2. Base64‑encode de JSON → payload_b64.
  3. Sign payload_b64 met jouw private key.
  4. POST‑redirect naar /hosted/start.php.
  5. Het platform POST terug naar jouw return_url met result_token.

2) Start request (SP → Platform)

Endpoint:

https://beid.hermesplatform.be/hosted/start.php

POST‑velden:

veldbeschrijving
payload_b64base64(JSON) met de start‑payload (zie hieronder)
sp_sig_algES256 of RS256
sp_signaturebase64( signature over payload_b64 )

Payload JSON (base64‑encoded in payload_b64):

{
  "sp_id": "partner1",
  "op": "read:identity",
  "return_url": "https://sp.example/return",
  "state": "opaque123",
  "ts": 1700000000,
  "nonce": "random-hex",
  "tbs_b64": "..." // enkel voor auth/sign
}

Veld uitleg:

veldvereistomschrijving
sp_idjajouw SP‑identifier
opjaread:identity, read:address, read:photo, read:certs, auth, sign
return_urljamoet op allowlist staan bij HP
stateneeopaque string, krijg je terug in de response
tsjaUnix timestamp (seconden)
noncejaunieke waarde per request (anti‑replay)
tbs_b64ja bij auth/signbase64 van de te‑signen bytes

Signature regels:

3) Response (Platform → SP)

Het platform POST naar jouw return_url met:

POST https://sp.example/return
result_token=...&state=opaque123

Uitleg:

De SP valideert result_token met de public key van het platform en decodeert payload_b64.

Public key (download): /keys/challenge_pub.pem

Result‑token claims (voorbeeld):

{
  "iss": "beid.hermesplatform.be",
  "aud": "sp",
  "sp_id": "partner1",
  "op": "read:identity",
  "status": "ok",
  "payload_b64": "...",
  "result_hash": "...",
  "iat": 1700000000,
  "exp": 1700000300
}

4) tbs_b64 (auth/sign)

5) Voorbeelden (SP‑zijde)

5.1 PHP – start (signed POST‑redirect)

<?php
$payload = [
  'sp_id' => 'partner1',
  'op' => 'read:identity',
  'return_url' => 'https://sp.example/return',
  'state' => 'opaque123',
  'ts' => time(),
  'nonce' => bin2hex(random_bytes(16)),
];
$payloadB64 = base64_encode(json_encode($payload, JSON_UNESCAPED_SLASHES));
$privPem = file_get_contents('/path/to/sp_priv.pem');
$privKey = openssl_pkey_get_private($privPem);
openssl_sign($payloadB64, $sigDer, $privKey, OPENSSL_ALGO_SHA256);
$sigB64 = base64_encode($sigDer);
?>
<form method="POST" action="https://beid.hermesplatform.be/hosted/start.php">
  <input name="payload_b64" value="<?php echo htmlspecialchars($payloadB64); ?>" />
  <input name="sp_sig_alg" value="ES256" />
  <input name="sp_signature" value="<?php echo htmlspecialchars($sigB64); ?>" />
</form>
<script>document.forms[0].submit()</script>

5.2 PHP – return (verifieer result_token)

<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

$token = $_POST['result_token'] ?? '';
$pubPem = file_get_contents('/path/to/platform_pub.pem');
$claims = JWT::decode($token, new Key($pubPem, 'ES256'));
$payload = json_decode(base64_decode($claims->payload_b64), true);
?>

5.3 ASP.NET (C#) – start (signed POST‑redirect)

var payload = new {
  sp_id = "partner1",
  op = "read:identity",
  return_url = "https://sp.example/return",
  state = "opaque123",
  ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
  nonce = Guid.NewGuid().ToString("N")
};
var payloadJson = System.Text.Json.JsonSerializer.Serialize(payload);
var payloadB64 = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(payloadJson));
var privPem = System.IO.File.ReadAllText("C:\\path\\sp_priv.pem");
var ecdsa = System.Security.Cryptography.ECDsa.Create();
ecdsa.ImportFromPem(privPem);
var sigDer = ecdsa.SignData(System.Text.Encoding.ASCII.GetBytes(payloadB64),
  System.Security.Cryptography.HashAlgorithmName.SHA256);
var sigB64 = Convert.ToBase64String(sigDer);

5.4 ASP.NET (C#) – return (verifieer result_token)

var token = Request.Form["result_token"];
var pubPem = System.IO.File.ReadAllText("C:\\path\\platform_pub.pem");
var handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();
var key = new Microsoft.IdentityModel.Tokens.ECDsaSecurityKey(
  System.Security.Cryptography.ECDsa.Create(System.Security.Cryptography.ECCurve.NamedCurves.nistP256));
key.Ecdsa.ImportFromPem(pubPem);
var parms = new Microsoft.IdentityModel.Tokens.TokenValidationParameters {
  ValidateIssuer = true, ValidIssuer = "beid.hermesplatform.be",
  ValidateAudience = true, ValidAudience = "sp",
  IssuerSigningKey = key,
  ValidateLifetime = true
};
var principal = handler.ValidateToken(token, parms, out _);
var payloadB64 = principal.Claims.First(c => c.Type == "payload_b64").Value;
var payloadJson = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(payloadB64));

5.5 Java – start (signed POST‑redirect)

String payloadJson = "{...}"; // bouw JSON met sp_id/op/return_url/ts/nonce
String payloadB64 = Base64.getEncoder().encodeToString(payloadJson.getBytes(StandardCharsets.UTF_8));
PrivateKey privKey = loadPrivateKey("/path/sp_priv.pem");
Signature sig = Signature.getInstance("SHA256withECDSA");
sig.initSign(privKey);
sig.update(payloadB64.getBytes(StandardCharsets.US_ASCII));
byte[] sigDer = sig.sign();
String sigB64 = Base64.getEncoder().encodeToString(sigDer);

5.6 Java – return (verifieer result_token)

// bv. met Nimbus JOSE + JWT
SignedJWT jwt = SignedJWT.parse(resultToken);
JWK jwk = JWK.parseFromPEMEncodedObjects(pubPem);
JWSVerifier verifier = new ECDSAVerifier(jwk.toECKey());
if (!jwt.verify(verifier)) throw new Exception("invalid signature");
JWTClaimsSet claims = jwt.getJWTClaimsSet();
String payloadB64 = claims.getStringClaim("payload_b64");
String payloadJson = new String(Base64.getDecoder().decode(payloadB64), StandardCharsets.UTF_8);