Les deux certificats sortent du même eIDAS TSP, portent le même numéro d'agrément et les mêmes rôles DSP2, et arrivent souvent sur le même bon de commande. La différence n'est pas dans ce qu'ils contiennent. Elle est dans la couche de la pile qui les lit. Le QWAC est lu par la couche TLS, une fois, au handshake. Le QSealC est lu par la couche applicative, à chaque requête, dans un en-tête HTTP.
Un ticket qui dit « erreur de certificat » sans dire à quelle couche n'est pas diagnosticable. Cet article ne redéfinit pas les deux objets — les fiches QWAC, QSealC et mTLS le font. Il place chacun dans la pile, dans l'infrastructure et dans les logs, avec le texte de STET 1.6.3 en regard. Pour le panorama complet de la stack, lisez d'abord Architecture technique d'une API DSP2.
En une image : deux couches, deux certificats
Une requête DSP2 descend la pile. Chaque certificat n'intervient qu'à un étage.
| QWAC | QSealC | |
|---|---|---|
| Base eIDAS | certificat qualifié d'authentification de site web, art. 3(39) | certificat qualifié de cachet électronique, art. 3(30) |
| Couche | transport (TLS) | message (HTTP) |
| Lu par | la pile TLS des deux côtés, au handshake | le vérificateur de signature de l'ASPSP, à chaque requête |
| Fréquence | une fois par connexion | une fois par requête — et par réponse si l'ASPSP signe |
| Survit à un proxy qui termine TLS | non | oui |
| Valeur de preuve après coup | aucune : rien n'est conservé | la signature reste dans la requête, vérifiable plus tard |
| Clé privée, côté TPP | là où la connexion sortante est ouverte | là où la requête est construite |
| Symptôme quand ça casse | handshake rompu, aucun code HTTP | HTTP 400 (STET §3.5) |
| Ce que STET en dit | §3.1, §3.2, §3.9 | §3.5 |
La RTS (UE) 2018/389, art. 34, autorise l'un ou l'autre pour l'identification. Les standards d'API ont tranché en les empilant : STET et Berlin Group exigent le QWAC pour le canal et le QSealC pour la signature. Deux certificats, donc deux clés privées, deux dates d'expiration et deux procédures de rotation.
Ce que STET exige, texte à l'appui
Trois passages du Framework 1.6.3 (Part 1) suffisent. Les pages sont celles du PDF officiel, que le manuel affiche en marge de chaque paragraphe.
§3.1 et §3.2 — le QWAC, pour TLS et rien d'autre
« Each actor must be provided with at least one eIDAS certificate (QWAC), for TLS 1.2 purpose » (§3.1, p. 17). Le TPP vérifie le QWAC serveur de l'ASPSP et présente le sien ; les deux respectent ETSI TS 119 495 ; « in case of authentication failure, on one side or the other, the connection must be closed » ; et « no additional encrypting or authenticating feature is required » (§3.2, p. 17–18). Le QWAC ne remonte jamais dans la requête : tout ce qu'il a à dire, il le dit au handshake.
Le même paragraphe fixe le format de l'organizationIdentifier du sujet du certificat : PSD, le code pays de la NCA, un tiret, l'identifiant de la NCA, un tiret, le numéro d'agrément — PSDFR-ACPR-12345 pour un acteur agréé par l'ACPR. C'est ce champ, avec les rôles du QC statement ETSI, que l'ASPSP lit dans le certificat client pour savoir qui appelle, et en quelle qualité.
§3.5 — le QSealC, pour signer chaque requête
« 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).
Le mécanisme historique est HTTP Signature dans sa version draft-cavage : un Digest SHA-256 du corps, puis une signature RSA-SHA256 faite avec le QSealC sur (request-target), Date, Content-Type, Content-Length, X-Request-Id, les en-têtes PSU-* et le Digest lui-même, transportée dans l'en-tête Signature avec keyId, algorithm et la liste des en-têtes signés (§3.5.1.1, p. 53–55). Le keyId est soit l'identifiant attribué lors de l'enregistrement OAuth2, soit l'URL du QSealC suffixée d'un _ et de son empreinte SHA-256 (§3.5.1.2, p. 55).
Depuis la 1.6.3, le Framework conseille de remplacer ce mécanisme par le profil JSON Web Signature d'Open Banking Europe : un JWS détaché (RFC 7515) dans l'en-tête x-jws-signature, dont l'en-tête protégé porte x5t#S256, l'empreinte du QSealC signataire, et sigD la liste des en-têtes couverts (§3.5.2, p. 55–56). Le draft-cavage, lui, a été remplacé côté IETF par la RFC 9421 — que STET ne cite pas comme cible.
§3.9 — le QWAC revient une troisième fois, dans OAuth2
« Based on MTLS, the identity of the TPP is provided by its eIDAS certificate during OAuth2 procedures », avec renvoi à la RFC 8705 (§3.9, p. 59). Le serveur d'autorisation authentifie donc le client par tls_client_auth — le QWAC présenté au handshake du POST /token — et peut lier le token émis à ce certificat, par sa claim cnf et l'empreinte x5t#S256. Un token lié à une empreinte qui n'est plus celle présentée est refusé, même s'il n'a pas expiré.
Une requête, trois vérifications
Trois vérifications, trois couches, trois symptômes qui ne se ressemblent pas. L'ordre des deux vérifications applicatives est propre à chaque banque ; le symptôme, lui, ne change pas.
Où vivent les clés privées : l'erreur de couche
C'est ici qu'on se trompe. Les deux clés privées ne vivent pas au même endroit, parce que les deux certificats ne sont pas lus par le même composant.
Côté TPP, en sortie
La clé du QSealC appartient au composant qui construit la requête, parce que la signature couvre des en-têtes — Date, Content-Length, X-Request-Id — qui doivent être figés avant l'envoi. La clé du QWAC appartient au composant qui ouvre la connexion. Si un proxy sortant, un NAT applicatif ou un service mesh ouvre les connexions à la place de l'application, c'est lui qui présente le QWAC, et l'application n'a pas à en tenir la clé. Mettre la clé du QWAC dans l'application alors que le mesh termine et rouvre TLS donne un handshake sans certificat client : une alerte TLS, et aucun code HTTP.
Côté ASPSP, en entrée
Côté banque, le QWAC client s'arrête au composant qui termine TLS. Ce qui passe ensuite est une identité extraite — organizationIdentifier, rôles, empreinte — transmise dans un en-tête interne que le reste de la chaîne doit pouvoir croire, d'où l'intérêt de le signer ou de ne l'accepter que du terminateur. La signature QSealC, elle, traverse tout : un composant situé n'importe où derrière peut la vérifier, à condition de voir les octets d'origine. Une gateway qui re-sérialise le JSON, recalcule Content-Length ou normalise Date avant le vérificateur produit des 400 sur des requêtes valides.
Les six confusions de couche
| # | Confusion | Ce qui se passe | Symptôme |
|---|---|---|---|
| 1 | Clé du QWAC sur un composant qui n'ouvre pas la connexion | handshake sans certificat client | alerte TLS, pas de HTTP |
| 2 | QSealC présenté comme certificat client TLS, ou QWAC utilisé pour signer | le QcType ETSI n'est pas le bon (web pour l'un, eseal pour l'autre) ; un vérificateur qui le contrôle refuse | alerte TLS, ou 400 |
| 3 | Signature calculée avant un proxy qui réécrit Date, Content-Length ou le corps | la signature porte sur d'autres octets que ceux reçus | 400, signature invalide |
| 4 | Vérification faite après une transformation de la requête, côté ASPSP | même cause, en miroir | 400 sur des requêtes valides |
| 5 | QWAC renouvelé sans réémettre les tokens liés | cnf.x5t#S256 ne correspond plus au certificat présenté | 401 sur un token non expiré |
| 6 | AC du QTSP de la banque absente du magasin de confiance du TPP | le TPP refuse le QWAC serveur de l'ASPSP | unknown_ca, connexion fermée par le TPP lui-même |
La confusion 2 est la plus coûteuse, parce qu'elle est invisible tant que la banque ne contrôle pas le type de certificat — et visible d'un coup le jour où elle le fait. Les deux certificats sont souvent vendus ensemble ; ils ne sont pas interchangeables.
Matrice de diagnostic
| Symptôme | Couche | Certificat en cause | Où regarder |
|---|---|---|---|
Connexion fermée pendant le handshake, aucun code HTTP ; handshake_failure, bad_certificate, certificate_expired, unknown_ca | TLS | QWAC — le vôtre, ou celui de la banque que vous ne reconnaissez pas | logs du terminateur TLS ; openssl s_client -cert -key |
| 401 | token | QWAC, via le binding RFC 8705 ; ou simplement un token expiré | réponse du serveur d'autorisation ; cnf du token contre l'empreinte du certificat présenté |
| 400 mentionnant la signature (STET §3.5) | message HTTP | QSealC | Digest recalculé ; liste des en-têtes signés ; keyId résoluble ; octets réellement envoyés |
| 403, ou code métier de la banque | autorisation | aucun des deux | rôles PSD2 du certificat contre l'endpoint appelé ; état du consentement |
Deux horloges
Les deux certificats expirent chacun à leur date, sont révocables chacun de leur côté, et se renouvellent séparément. Trois conséquences :
- Deux alertes d'expiration, pas une. Un QWAC valide ne dit rien de l'état du QSealC, et réciproquement.
- La rotation du QWAC touche les tokens. L'enregistrement OAuth2 survit si le DN du sujet ne change pas — c'est lui que
tls_client_auth_subject_dndéclare — mais les access tokens liés à l'ancienne empreinte (RFC 8705) ne passent plus. Réémettre les tokens fait partie de la rotation. - La rotation du QSealC touche le
keyId— l'URL suffixée de l'empreinte change, ou lex5t#S256du profil JWS — et elle ne doit pas retirer l'ancien certificat : les requêtes déjà signées doivent rester vérifiables. C'est tout l'objet de la non-répudiation. On retire la clé privée, on laisse le certificat publié.
Pour résumer
- QWAC = couche TLS. Lu au handshake, par le composant qui ouvre ou termine la connexion. Sa clé vit là, nulle part ailleurs.
- QSealC = couche message. Lu à chaque requête, par un vérificateur qui doit voir les octets d'origine. Sa clé vit là où la requête est construite.
- STET : QWAC pour TLS 1.2 (§3.1–3.2), chaque requête signée avec le QSealC et 400 sinon (§3.5), profil JWS recommandé depuis la 1.6.3 (§3.5.2), identité OAuth2 portée par le QWAC (§3.9, RFC 8705).
- Pas de code HTTP → QWAC. 400 → QSealC. 401 → binding du token. 403 → ni l'un ni l'autre.
- Deux horloges : deux expirations, deux rotations, et l'ancien QSealC reste publié pour vérifier le passé.
Pour le reste de la stack — standards, flux SCA, consentement — voir Architecture technique d'une API DSP2. Les deux mécanismes de signature, octet par octet, et les pannes de handshake les plus fréquentes font l'objet des deux articles suivants. Le texte intégral de la section citée est dans le manuel STET 1.6.3, avec le PDF original en regard.