# QWAC vs QSealC: quem faz o quê, e por que você está olhando para a camada errada

> O QWAC é lido pela camada TLS, uma vez, no handshake. O QSealC é lido pela camada aplicativa, a cada requisição. Onde vive cada chave privada, o que o STET exige, e como ler um “erro de certificado”.

_Publicado em: 2026-08-24 · Category: segurança · Tags: #dsp2 #psd2 #open-banking #segurança #eidas #mtls #stet_

---

Os dois certificados saem do mesmo [eIDAS TSP](https://openfinanceguide.com/pt-br/glossary/eidas-tsp), carregam o mesmo número de autorização e os mesmos papéis [DSP2](https://openfinanceguide.com/pt-br/glossary/dsp2), e muitas vezes chegam no mesmo pedido de compra. A diferença não está no que eles contêm. Está na **camada da pilha que os lê**. O [QWAC](https://openfinanceguide.com/pt-br/glossary/qwac) é lido pela camada TLS, uma vez, no handshake. O [QSealC](https://openfinanceguide.com/pt-br/glossary/qsealc) é lido pela camada aplicativa, a cada requisição, em um cabeçalho HTTP.

Um ticket que diz “erro de certificado” sem dizer em qual camada não é diagnosticável. Este artigo não redefine os dois objetos — os verbetes QWAC, QSealC e [mTLS](https://openfinanceguide.com/pt-br/glossary/mtls) fazem isso. Ele coloca cada um na pilha, na infraestrutura e nos logs, com o texto do [STET](https://openfinanceguide.com/pt-br/glossary/stet) 1.6.3 lado a lado. Para o panorama completo do stack, leia antes [Arquitetura técnica de uma API DSP2](/pt-br/blog/architecture-api-dsp2).

## Em uma imagem: duas camadas, dois certificados

Uma requisição DSP2 desce a pilha. Cada certificado só intervém em um único nível.

```mermaid
flowchart LR
    subgraph APP["Camada aplicativa"]
        BIZ["Lógica de negócio do TPP\nGET /accounts\nPOST /payment-requests"]
    end
    subgraph MSG["Camada de mensagem HTTP — a cada requisição"]
        SIG["Digest + Signature\nou x-jws-signature\n🔏 chave privada do QSealC"]
        TOK["Authorization: Bearer …\ntoken vinculado ao certificado\nde cliente (RFC 8705)"]
    end
    subgraph TLS["Camada de transporte — por conexão"]
        MT["Handshake mTLS, TLS 1.2\n🪪 QWAC do TPP\n⇄ QWAC do ASPSP"]
    end
    APP --> MSG --> TLS -->|"Internet"| ASPSP["ASPSP"]
```

| | QWAC | QSealC |
|---|---|---|
| Base eIDAS | certificado qualificado de autenticação de site, art. 3(39) | certificado qualificado de selo eletrônico, art. 3(30) |
| Camada | transporte (TLS) | mensagem (HTTP) |
| Lido por | a pilha TLS dos dois lados, no handshake | o verificador de assinatura do [ASPSP](https://openfinanceguide.com/pt-br/glossary/aspsp), a cada requisição |
| Frequência | uma vez por conexão | uma vez por requisição — e por resposta, se o ASPSP assinar |
| Sobrevive a um proxy que termina o TLS | não | sim |
| Valor probatório a posteriori | nenhum: nada é conservado | a assinatura fica na requisição, verificável depois |
| Chave privada, do lado do [TPP](https://openfinanceguide.com/pt-br/glossary/tpp) | onde a conexão de saída é aberta | onde a requisição é construída |
| Sintoma quando quebra | handshake rompido, nenhum código HTTP | HTTP 400 (STET §3.5) |
| O que o STET diz a respeito | §3.1, §3.2, §3.9 | §3.5 |

> **Info:**
> A RTS (UE) 2018/389, art. 34, autoriza um ou outro para a identificação. Os
>   padrões de API resolveram a questão empilhando os dois: STET e Berlin Group exigem
>   o QWAC para o canal e o QSealC para a assinatura. Dois certificados, portanto duas
>   chaves privadas, duas datas de expiração e dois procedimentos de rotação.

## O que o STET exige, com o texto na mão

Três trechos do Framework 1.6.3 (Part 1) bastam. As páginas são as do PDF oficial, que o manual exibe na margem de cada parágrafo.

### §3.1 e §3.2 — o QWAC, para o TLS e nada mais

"Each actor must be provided with at least one eIDAS certificate (QWAC), for TLS 1.2 purpose" ([§3.1, p. 17](/pt-br/stet/1.6.3/framework/3-prerequisites-and-technical-details#s-3.1)). O TPP verifica o QWAC de servidor do ASPSP e apresenta o seu; os dois respeitam a ETSI TS 119 495; "in case of authentication failure, on one side or the other, the connection must be closed"; e "no additional encrypting or authenticating feature is required" ([§3.2, p. 17–18](/pt-br/stet/1.6.3/framework/3-prerequisites-and-technical-details#s-3.2)). O QWAC nunca chega à requisição: tudo o que tem a dizer, ele diz no handshake.

O mesmo parágrafo fixa o formato do `organizationIdentifier` do sujeito do certificado: `PSD`, o código do país da [NCA](https://openfinanceguide.com/pt-br/glossary/nca), um hífen, o identificador da NCA, um hífen, o número de autorização — `PSDFR-ACPR-12345` para um ator autorizado pela [ACPR](https://openfinanceguide.com/pt-br/glossary/acpr). É esse campo, junto com os papéis do QC statement ETSI, que o ASPSP lê no certificado de cliente para saber quem chama, e em que qualidade.

### §3.5 — o QSealC, para assinar cada requisição

"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](/pt-br/stet/1.6.3/framework/3-prerequisites-and-technical-details#s-3.5)).

O mecanismo histórico é o [HTTP Signature](https://openfinanceguide.com/pt-br/glossary/http-signature) na versão draft-cavage: um `Digest` SHA-256 do corpo, depois uma assinatura RSA-SHA256 feita com o QSealC sobre `(request-target)`, `Date`, `Content-Type`, `Content-Length`, `X-Request-Id`, os cabeçalhos `PSU-*` e o próprio `Digest`, transportada no cabeçalho `Signature` com `keyId`, `algorithm` e a lista dos cabeçalhos assinados ([§3.5.1.1, p. 53–55](/pt-br/stet/1.6.3/framework/3-prerequisites-and-technical-details#s-3.5.1.1)). O `keyId` é ou o identificador atribuído no registro [OAuth2](https://openfinanceguide.com/pt-br/glossary/oauth2), ou a URL do QSealC com o sufixo `_` seguido da sua impressão digital SHA-256 ([§3.5.1.2, p. 55](/pt-br/stet/1.6.3/framework/3-prerequisites-and-technical-details#s-3.5.1.2)).

Desde a 1.6.3, o Framework recomenda substituir esse mecanismo pelo perfil JSON Web Signature do Open Banking Europe: um JWS destacado (RFC 7515) no cabeçalho `x-jws-signature`, cujo cabeçalho protegido carrega em `x5t#S256` a impressão digital do QSealC signatário e em `sigD` a lista dos cabeçalhos cobertos ([§3.5.2, p. 55–56](/pt-br/stet/1.6.3/framework/3-prerequisites-and-technical-details#s-3.5.2)). O draft-cavage, por sua vez, foi substituído no IETF pela RFC 9421 — que o STET não cita como alvo.

### §3.9 — o QWAC volta uma terceira vez, no OAuth2

"Based on MTLS, the identity of the TPP is provided by its eIDAS certificate during OAuth2 procedures", com remissão à RFC 8705 ([§3.9, p. 59](/pt-br/stet/1.6.3/framework/3-prerequisites-and-technical-details#s-3.9)). O servidor de autorização autentica, portanto, o cliente por `tls_client_auth` — o QWAC apresentado no handshake do `POST /token` — e pode vincular o token emitido a esse certificado, pela claim `cnf` e pela impressão digital `x5t#S256`. Um token vinculado a uma impressão digital que já não é a apresentada é recusado, mesmo que não tenha expirado.

## Uma requisição, três verificações

```mermaid
sequenceDiagram
    autonumber
    participant TPP as TPP (cliente HTTP)
    participant TLS as ASPSP — terminação TLS
    participant AS as ASPSP — servidor de autorização
    participant API as ASPSP — API e verificador de assinatura

    TPP->>TLS: ClientHello
    TLS-->>TPP: Certificate (QWAC do ASPSP) + CertificateRequest
    TPP->>TLS: Certificate (QWAC do TPP) + CertificateVerify
    alt QWAC desconhecido, expirado, revogado, sem o papel
        TLS-->>TPP: alert TLS — conexão fechada, nenhum código HTTP
    end
    TLS-->>TPP: Finished — canal mTLS estabelecido

    TPP->>AS: POST /token (tls_client_auth, no canal mTLS)
    AS-->>TPP: access_token vinculado ao QWAC (cnf.x5t#35;S256)

    TPP->>API: GET /accounts<br/>Authorization: Bearer …<br/>Digest + Signature (QSealC)
    API->>API: token válido, vinculado a este certificado de cliente?
    alt token ausente, expirado, vinculado a outro certificado
        API-->>TPP: 401
    end
    API->>API: Digest recalculado, Signature verificada com o QSealC do keyId?
    alt assinatura ausente ou inválida
        API-->>TPP: 400 (STET §3.5)
    end
    API-->>TPP: 200 + corpo, assinado pelo ASPSP quando for o caso
```

Três verificações, três camadas, três sintomas que não se parecem. A ordem das duas verificações aplicativas é própria de cada banco; o sintoma, esse, não muda.

## Onde vivem as chaves privadas: o erro de camada

É aqui que se erra. As duas chaves privadas não vivem no mesmo lugar, porque os dois certificados não são lidos pelo mesmo componente.

### Do lado do TPP, na saída

```mermaid
flowchart LR
    subgraph TPP["Infraestrutura do TPP"]
        APP["Serviço aplicativo\nconstrói a requisição"]
        SIGN["Serviço de assinatura\n🔏 chave privada do QSealC (HSM / KMS)"]
        EGRESS["Cliente HTTP ou proxy de saída\n🪪 chave privada do QWAC\nabre a conexão mTLS"]
        APP -->|"requisição + Digest"| SIGN
        SIGN -->|"requisição + Signature"| EGRESS
    end
    EGRESS -->|"mTLS"| BANK["ASPSP"]
```

A chave do QSealC pertence ao componente que **constrói a requisição**, porque a assinatura cobre cabeçalhos — `Date`, `Content-Length`, `X-Request-Id` — que precisam estar congelados antes do envio. A chave do QWAC pertence ao componente que **abre a conexão**. Se um proxy de saída, um NAT aplicativo ou um service mesh abre as conexões no lugar da aplicação, é ele que apresenta o QWAC, e a aplicação não tem por que guardar a chave. Colocar a chave do QWAC na aplicação quando o mesh termina e reabre o TLS resulta em um handshake sem certificado de cliente: um alerta TLS, e nenhum código HTTP.

### Do lado do ASPSP, na entrada

```mermaid
flowchart LR
    TPP["TPP"] -->|"mTLS"| LB
    subgraph ASPSP["Infraestrutura do ASPSP"]
        LB["Terminação TLS — LB, WAF, gateway\nsolicita e verifica o QWAC de cliente\n🪪 chave privada do QWAC de servidor"]
        GW["Verificador de assinatura\nrecalcula o Digest, resolve o keyId,\nverifica com o QSealC do TPP"]
        AUTHZ["Autorização\ntoken, consentimento, papel"]
        BE["Backend de negócio"]
        LB -->|"requisição + identidade extraída do QWAC\n(cabeçalho interno, assinado)"| GW
        GW --> AUTHZ --> BE
    end
```

Do lado do banco, o QWAC de cliente para no componente que termina o TLS. O que passa dali em diante é uma **identidade extraída** — `organizationIdentifier`, papéis, impressão digital — transmitida em um cabeçalho interno no qual o resto da cadeia precisa poder confiar, daí o interesse em assiná-lo ou em aceitá-lo apenas do terminador. A assinatura QSealC, por sua vez, atravessa tudo: um componente situado em qualquer ponto atrás pode verificá-la, **desde que veja os bytes originais**. Um gateway que reserializa o JSON, recalcula o `Content-Length` ou normaliza o `Date` antes do verificador produz 400 em requisições válidas.

### As seis confusões de camada

| # | Confusão | O que acontece | Sintoma |
|---|---|---|---|
| 1 | Chave do QWAC em um componente que não abre a conexão | handshake sem certificado de cliente | alerta TLS, sem HTTP |
| 2 | QSealC apresentado como certificado de cliente TLS, ou QWAC usado para assinar | o `QcType` ETSI não é o correto (`web` para um, `eseal` para o outro); um verificador que o confere recusa | alerta TLS, ou 400 |
| 3 | Assinatura calculada antes de um proxy que reescreve `Date`, `Content-Length` ou o corpo | a assinatura cobre bytes diferentes dos recebidos | 400, assinatura inválida |
| 4 | Verificação feita depois de uma transformação da requisição, do lado do ASPSP | mesma causa, em espelho | 400 em requisições válidas |
| 5 | QWAC renovado sem reemitir os tokens vinculados | `cnf.x5t#S256` já não corresponde ao certificado apresentado | 401 em um token não expirado |
| 6 | AC do QTSP do banco ausente do trust store do TPP | o TPP recusa o QWAC de servidor do ASPSP | `unknown_ca`, conexão fechada pelo próprio TPP |

> **Warning:**
> A confusão 2 é a mais cara, porque é invisível enquanto o banco não confere o tipo
>   de certificado — e visível de uma vez no dia em que ele passa a conferir. Os dois
>   certificados costumam ser vendidos juntos; não são intercambiáveis.

## Matriz de diagnóstico

| Sintoma | Camada | Certificado em questão | Onde olhar |
|---|---|---|---|
| Conexão fechada durante o handshake, nenhum código HTTP; `handshake_failure`, `bad_certificate`, `certificate_expired`, `unknown_ca` | TLS | QWAC — o seu, ou o do banco que você não reconhece | logs do terminador TLS; `openssl s_client -cert -key` |
| 401 | token | QWAC, pelo binding RFC 8705; ou simplesmente um token expirado | resposta do servidor de autorização; `cnf` do token contra a impressão digital do certificado apresentado |
| 400 mencionando a assinatura (STET §3.5) | mensagem HTTP | QSealC | `Digest` recalculado; lista dos cabeçalhos assinados; `keyId` resolvível; bytes realmente enviados |
| 403, ou código de negócio do banco | autorização | nenhum dos dois | papéis PSD2 do certificado contra o endpoint chamado; estado do consentimento |

```mermaid
flowchart TD
    Q0["“Erro de certificado”"] --> Q1{"Voltou algum\ncódigo HTTP?"}
    Q1 -->|"Não"| TLSX["Camada TLS → QWAC\ncadeia, revogação, papel,\ntrust store, quem abre a conexão"]
    Q1 -->|"Sim"| Q2{"Qual?"}
    Q2 -->|"400"| SIGX["Camada de mensagem → QSealC\nDigest, cabeçalhos assinados, keyId,\nbytes reescritos no caminho"]
    Q2 -->|"401"| TOKX["Camada de token → binding RFC 8705\no token está vinculado ao certificado apresentado?"]
    Q2 -->|"403 ou outro"| ROLEX["Autorização → nem um nem outro\npapel PSD2, consentimento, escopo"]
```

## Dois relógios

Os dois certificados expiram cada um na sua data, são revogáveis cada um do seu lado e se renovam separadamente. Três consequências:

- **Dois alertas de expiração**, não um. Um QWAC válido não diz nada sobre o estado do QSealC, e vice-versa.
- **A rotação do QWAC afeta os tokens.** O registro OAuth2 sobrevive se o DN do sujeito não mudar — é ele que `tls_client_auth_subject_dn` declara — mas os access tokens vinculados à impressão digital antiga (RFC 8705) deixam de passar. Reemitir os tokens faz parte da rotação.
- **A rotação do QSealC afeta o `keyId`** — a URL com o sufixo da impressão digital muda, ou o `x5t#S256` do perfil JWS — e ela não deve retirar o certificado antigo: as requisições já assinadas precisam continuar verificáveis. É todo o sentido do não repúdio. Retira-se a chave privada, deixa-se o certificado publicado.

```mermaid
gantt
    title Dois certificados, dois calendários
    dateFormat YYYY-MM-DD
    axisFormat %m/%Y
    section QWAC
    QWAC n                                  :done, q1, 2026-01-01, 2027-01-01
    QWAC n+1                                :active, q2, 2026-11-15, 2027-11-15
    Reemissão dos tokens vinculados         :crit, milestone, m1, 2026-11-15, 0d
    section QSealC
    QSealC n                                :done, s1, 2026-04-01, 2027-04-01
    QSealC n+1                              :active, s2, 2027-02-15, 2028-02-15
    QSealC antigo ainda publicado           :s3, 2027-04-01, 2027-10-01
```

## Para resumir

1. **QWAC = camada TLS.** Lido no handshake, pelo componente que abre ou termina a conexão. Sua chave vive ali, e em nenhum outro lugar.
2. **QSealC = camada de mensagem.** Lido a cada requisição, por um verificador que precisa ver os bytes originais. Sua chave vive onde a requisição é construída.
3. **STET**: QWAC para o TLS 1.2 (§3.1–3.2), cada requisição assinada com o QSealC e 400 caso contrário (§3.5), perfil JWS recomendado desde a 1.6.3 (§3.5.2), identidade OAuth2 carregada pelo QWAC (§3.9, RFC 8705).
4. **Sem código HTTP → QWAC. 400 → QSealC. 401 → binding do token. 403 → nem um nem outro.**
5. **Dois relógios**: duas expirações, duas rotações, e o QSealC antigo continua publicado para verificar o passado.

Para o resto do stack — padrões, fluxos [SCA](https://openfinanceguide.com/pt-br/glossary/sca), consentimento — veja [Arquitetura técnica de uma API DSP2](/pt-br/blog/architecture-api-dsp2). Os dois mecanismos de assinatura, byte a byte, e as falhas de handshake mais frequentes são o assunto dos dois artigos seguintes. O texto integral da seção citada está no [manual STET 1.6.3](/pt-br/stet/1.6.3/framework/3-prerequisites-and-technical-details), com o PDF original lado a lado.

---

Fonte: https://openfinanceguide.com/pt-br/blog/qwac-vs-qsealc
