Both certificates come from the same eIDAS TSP, carry the same authorisation number and the same DSP2 roles, and often arrive on the same purchase order. The difference is not in what they contain. It is in which layer of the stack reads them. The QWAC is read by the TLS layer, once, at the handshake. The QSealC is read by the application layer, on every request, in an HTTP header.
A ticket that says "certificate error" without saying which layer cannot be diagnosed. This article does not redefine the two objects — the QWAC, QSealC and mTLS glossary entries do that. It places each one in the stack, in the infrastructure and in the logs, with the STET 1.6.3 text alongside. For the full picture of the stack, read Technical architecture of a PSD2 API first.
In one picture: two layers, two certificates
A PSD2 request goes down the stack. Each certificate comes into play at one level only.
| QWAC | QSealC | |
|---|---|---|
| eIDAS basis | qualified website authentication certificate, art. 3(39) | qualified electronic seal certificate, art. 3(30) |
| Layer | transport (TLS) | message (HTTP) |
| Read by | the TLS stack on both sides, at the handshake | the signature verifier of the ASPSP, on every request |
| Frequency | once per connection | once per request — and per response if the ASPSP signs |
| Survives a proxy that terminates TLS | no | yes |
| Evidential value after the fact | none: nothing is kept | the signature stays in the request, verifiable later |
| Private key, on the TPP side | where the outbound connection is opened | where the request is built |
| Symptom when it breaks | broken handshake, no HTTP status code | HTTP 400 (STET §3.5) |
| What STET says about it | §3.1, §3.2, §3.9 | §3.5 |
RTS (EU) 2018/389, art. 34, allows either one for identification. The API standards settled the question by stacking them: STET and the Berlin Group require the QWAC for the channel and the QSealC for the signature. Two certificates, hence two private keys, two expiry dates and two rotation procedures.
What STET requires, text in hand
Three passages of the 1.6.3 Framework (Part 1) are enough. Page numbers are those of the official PDF, which the handbook displays in the margin of each paragraph.
§3.1 and §3.2 — the QWAC, for TLS and nothing else
"Each actor must be provided with at least one eIDAS certificate (QWAC), for TLS 1.2 purpose" (§3.1, p. 17). The TPP verifies the ASPSP's server QWAC and presents its own; both comply with ETSI TS 119 495; "in case of authentication failure, on one side or the other, the connection must be closed"; and "no additional encrypting or authenticating feature is required" (§3.2, p. 17–18). The QWAC never surfaces in the request: everything it has to say, it says at the handshake.
The same paragraph fixes the format of the certificate subject's organizationIdentifier: PSD, the country code of the NCA, a hyphen, the NCA identifier, a hyphen, the authorisation number — PSDFR-ACPR-12345 for an actor authorised by the ACPR. This field, together with the roles in the ETSI QC statement, is what the ASPSP reads in the client certificate to know who is calling, and in what capacity.
§3.5 — the QSealC, to sign every request
"Each request sent by the TPP has to be signed and ASPSP might also sign their responses. If the ASPSP notes that the signature is either absent or invalid for a given request, it shall reject this request with HTTP400" (§3.5, p. 53).
The historical mechanism is HTTP Signature in its draft-cavage version: a SHA-256 Digest of the body, then an RSA-SHA256 signature computed with the QSealC over (request-target), Date, Content-Type, Content-Length, X-Request-Id, the PSU-* headers and the Digest itself, carried in the Signature header along with keyId, algorithm and the list of signed headers (§3.5.1.1, p. 53–55). The keyId is either the identifier assigned at OAuth2 registration, or the URL of the QSealC suffixed with a _ and its SHA-256 fingerprint (§3.5.1.2, p. 55).
Since 1.6.3, the Framework recommends replacing this mechanism with the Open Banking Europe JSON Web Signature profile: a detached JWS (RFC 7515) in the x-jws-signature header, whose protected header carries x5t#S256, the fingerprint of the signing QSealC, and sigD, the list of covered headers (§3.5.2, p. 55–56). As for draft-cavage, the IETF has superseded it with RFC 9421 — which STET does not cite as a target.
§3.9 — the QWAC comes back a third time, in OAuth2
"Based on MTLS, the identity of the TPP is provided by its eIDAS certificate during OAuth2 procedures", with a reference to RFC 8705 (§3.9, p. 59). The authorisation server therefore authenticates the client through tls_client_auth — the QWAC presented at the handshake of the POST /token — and can bind the issued token to that certificate, via its cnf claim and the x5t#S256 fingerprint. A token bound to a fingerprint that is no longer the one presented is rejected, even if it has not expired.
One request, three checks
Three checks, three layers, three symptoms that look nothing alike. The order of the two application-level checks varies from bank to bank; the symptom does not.
Where the private keys live: the layer mistake
This is where the mistake happens. The two private keys do not live in the same place, because the two certificates are not read by the same component.
On the TPP side, outbound
The QSealC key belongs to the component that builds the request, because the signature covers headers — Date, Content-Length, X-Request-Id — that must be frozen before sending. The QWAC key belongs to the component that opens the connection. If an egress proxy, an application-level NAT or a service mesh opens the connections on the application's behalf, it is that component that presents the QWAC, and the application has no business holding the key. Putting the QWAC key in the application while the mesh terminates and re-opens TLS yields a handshake with no client certificate: a TLS alert, and no HTTP status code.
On the ASPSP side, inbound
On the bank side, the client QWAC stops at the component that terminates TLS. What travels onward is an extracted identity — organizationIdentifier, roles, fingerprint — carried in an internal header that the rest of the chain must be able to trust, hence the point of signing it, or of accepting it only from the terminator. The QSealC signature, on the other hand, goes all the way through: a component anywhere behind can verify it, provided it sees the original bytes. A gateway that re-serialises the JSON, recomputes Content-Length or normalises Date before the verifier produces 400s on valid requests.
The six layer confusions
| # | Confusion | What happens | Symptom |
|---|---|---|---|
| 1 | QWAC key on a component that does not open the connection | handshake with no client certificate | TLS alert, no HTTP |
| 2 | QSealC presented as the TLS client certificate, or QWAC used for signing | the ETSI QcType is the wrong one (web for one, eseal for the other); a verifier that checks it refuses | TLS alert, or 400 |
| 3 | Signature computed before a proxy that rewrites Date, Content-Length or the body | the signature covers different bytes from those received | 400, invalid signature |
| 4 | Verification done after a transformation of the request, on the ASPSP side | same cause, mirrored | 400 on valid requests |
| 5 | QWAC renewed without reissuing the bound tokens | cnf.x5t#S256 no longer matches the certificate presented | 401 on an unexpired token |
| 6 | The bank's QTSP CA missing from the TPP's trust store | the TPP refuses the ASPSP's server QWAC | unknown_ca, connection closed by the TPP itself |
Confusion 2 is the most expensive, because it is invisible as long as the bank does not check the certificate type — and visible all at once the day it does. The two certificates are often sold together; they are not interchangeable.
Diagnostic matrix
| Symptom | Layer | Certificate involved | Where to look |
|---|---|---|---|
Connection closed during the handshake, no HTTP status code; handshake_failure, bad_certificate, certificate_expired, unknown_ca | TLS | QWAC — yours, or the bank's that you do not recognise | TLS terminator logs; openssl s_client -cert -key |
| 401 | token | QWAC, via the RFC 8705 binding; or simply an expired token | authorisation server response; the token's cnf against the fingerprint of the certificate presented |
| 400 mentioning the signature (STET §3.5) | HTTP message | QSealC | recomputed Digest; list of signed headers; resolvable keyId; bytes actually sent |
| 403, or the bank's business error code | authorisation | neither | the certificate's PSD2 roles against the endpoint called; consent state |
Two clocks
The two certificates each expire on their own date, can each be revoked on their own, and are renewed separately. Three consequences:
- Two expiry alerts, not one. A valid QWAC says nothing about the state of the QSealC, and vice versa.
- Rotating the QWAC affects the tokens. The OAuth2 registration survives if the subject DN does not change — that is what
tls_client_auth_subject_dndeclares — but the access tokens bound to the old fingerprint (RFC 8705) no longer go through. Reissuing the tokens is part of the rotation. - Rotating the QSealC affects the
keyId— the fingerprint-suffixed URL changes, or thex5t#S256of the JWS profile — and it must not withdraw the old certificate: requests already signed must remain verifiable. That is the whole point of non-repudiation. The private key is withdrawn; the certificate stays published.
To sum up
- QWAC = TLS layer. Read at the handshake, by the component that opens or terminates the connection. Its key lives there, nowhere else.
- QSealC = message layer. Read on every request, by a verifier that must see the original bytes. Its key lives where the request is built.
- STET: QWAC for TLS 1.2 (§3.1–3.2), every request signed with the QSealC and 400 otherwise (§3.5), JWS profile recommended since 1.6.3 (§3.5.2), OAuth2 identity carried by the QWAC (§3.9, RFC 8705).
- No HTTP status code → QWAC. 400 → QSealC. 401 → token binding. 403 → neither.
- Two clocks: two expiries, two rotations, and the old QSealC stays published to verify the past.
For the rest of the stack — standards, SCA flows, consent — see Technical architecture of a PSD2 API. The two signature mechanisms, byte by byte, and the most frequent handshake failures are the subject of the next two articles. The full text of the section quoted is in the STET 1.6.3 handbook, with the original PDF alongside.