# Teste funcional RSFN: vHSM para PIX/SPB e DICT GetEntry mTLS via relay

Data: 2026-06-21
Ambiente: homologação
Conta: `990933657879`, região `sa-east-1`
Origem dos testes: VM de egress nossa `monetarie-rsfn-egress-homolog`
(`i-0692fbf5e0dd6d350`, `192.168.40.10`), operada via **SSM** (sem bastion, sem SSH).
Pré-requisito: rota isolada do relay já ativa, conforme
`docs/plans/2026-06-21-rsfn-relay-monetarie-isolado-design.md`.

## 1. Objetivo

Após a camada de rede (TCP + certificado do BACEN) já validada via relay Cecresa,
executar um **teste funcional** em duas frentes: provar a assinatura RSA no vHSM de teste
da RTM e fazer um GetEntry DICT real para identificar exatamente em que ponto o fluxo é
interrompido.

## 2. Distinção técnica que guia o teste

A consulta de chave PIX (DICT **GetEntry**) é `GET /api/v2/entries/{chave}` e autentica
**somente por mTLS** (certificado cliente ICP-Brasil). Ela **não** leva assinatura XML.
Confirmado no código: `pix/backend/apps/shared/lib/shared/bacen/dict_client.ex:120`
(GetEntry, corpo `nil`, sem assinatura) e `:86` (operações mutáveis assinam via
`XmlSigner`, que chama `Shared.Crypto.RtmHsm`).

O **vHSM** (fluxo `get-session-credential` seguido de `sign-rsa`) é usado em operações
**assinadas** pelas cabines PIX e SPB, como CreateEntry, claims, infrações no DICT e
mensagens SPI/ICOM no SPB. O core não assina mensagens BACEN e não participa desse fluxo
criptográfico de mensagem.

Existem dois controles de certificado independentes:
- **mTLS de transporte**: certificado cliente PEM (`bacen_client_cert_path/key_path`).
- **Assinatura da mensagem**: XMLDSig via vHSM (`privateKeyUid`).

## 3. Passo A: vHSM de teste assina (provado)

HSM de teste RTM: `http://200.160.162.200:63351`, vHSM `50154`, `cryptoUser=user2`,
token = **credencial de teste da RTM não reproduzida aqui**.

Aprendizados de integração:
- `sign-rsa` e `signature-verify-rsa` exigem header **`Authorization: Bearer <session>`**
  (além do `session` no corpo). Sem o header: `"Authorization header is empty"`.
  Confirmado no cliente do repo: `rtm_hsm/client.ex:127`.
- O `privateKeyUid` do exemplo do doc (`913NkS9lSoq6cMvjCkvW`) **não existe** nesta vHSM
  (`OperationFailed, ItemNotFound: Provided UniqueId`). UIDs reais obtidos via
  `POST /v1/kmip/50154/locate-all` (42 objetos) + `get-attributes`.
- `dataSignHex` = buffer **EMSA-PKCS1 v1_5** já montado (`00 01 FF...FF 00`,
  `DigestInfo`, `SHA-256`), 256 bytes para chave 2048, igual ao `Pkcs1.emsa_encode_hex`
  do repo.

Resultado (chave de teste `Teste_pri_18_02_26` / `Teste_pub_18_02_26`, RSA 2048):

| Operação | Retorno |
|---|---|
| `get-session-credential` | sessão (len 46) |
| `sign-rsa` | assinatura de **256 bytes** |
| `signature-verify-rsa` (correto) | `{"returnValue":"Valid"}` |
| `signature-verify-rsa` (buffer adulterado) | `{"returnValue":"Invalid"}` |

Conclusão: o fluxo de assinatura RSA do vHSM, implementado nas cabines PIX e SPB,
**funciona** ponta a ponta contra o HSM de teste.

## 4. Passo B: DICT GetEntry via relay, sem certificado cliente (prova do erro do BCB)

Comando (na VM `192.168.40.10`):
```
curl -k -v https://dict-h.pi.rsfn.net.br:16522/api/v2/entries/62188010000150 \
  -H "Accept: application/xml" -H "PI-Requesting-Participant: 46026562" \
  -H "PI-PayerId: 32189410835"
```

Resumo técnico da execução de 2026-06-21T17:40:26Z, com evidência bruta completa em
`docs/handoff/evidence/2026-06-21-dict-getentry-raw.txt`:
```
getent: dict-h.pi.rsfn.net.br -> 172.16.70.51   (relay Cecresa)
* Connected to dict-h.pi.rsfn.net.br (172.16.70.51) port 16522
* TLSv1.2 (IN), TLS handshake, Certificate (11):  { [3520 bytes] }   # cert do BACEN
* TLSv1.2 (IN), TLS handshake, Request CERT (13):  { [36 bytes] }    # BACEN exige cert cliente
* TLSv1.2 (OUT), TLS handshake, Certificate (11):  } [7 bytes] {     # enviamos cert vazio
* TLSv1.2 (IN), TLS alert, handshake failure (552):
* OpenSSL/3.0.13: error:0A000410:SSL routines::sslv3 alert handshake failure
curl: (35) ... sslv3 alert handshake failure
```

`openssl s_client` (identidade do servidor + pedido de cert cliente):
```
0 s:O = BANCO CENTRAL DO BRASIL, CN = *.pi.rsfn.net.br, serialNumber = 00038166000105
  i:O = ICP-Brasil, CN = Autoridade Certificadora do SERPRO SSLv1
1 s:O = ICP-Brasil, CN = Autoridade Certificadora do SERPRO SSLv1
  i:O = ICP-Brasil, CN = Autoridade Certificadora Raiz Brasileira v10
Client Certificate Types: RSA sign, DSA sign, ECDSA sign
Requested Signature Algorithms: RSA+SHA256:...:ECDSA+SHA1
SSL alert number 40 (handshake_failure)
```

Leitura: o relay entrega o handshake **até o BACEN real** (server cert = Banco Central,
cadeia SERPRO/ICP-Brasil), o BACEN envia `CertificateRequest` exigindo cert cliente
ICP-Brasil (RSA sign, RSA+SHA256), e sem cert válido encerra com `handshake_failure`.
**Esse é o erro de certificado do BCB: a rota isolada chega na camada mTLS do BC.**

## 5. Por que não dá para usar os certs da outra instituição do HSM de teste

Tentativa de exportar material do conjunto de teste BANESE-SPB3-HML
(`PriKey v8elUj2kdzP3jDymf4jJ` / `PubKey OFTeqAZS6jyXAa+BgO4l` / `Cert VMuEabpWV8CNQ8VCg7po`):

- `export-key-rsa` (privada): `"Wrap key must be of object type 'PublicKey'."`
- `export-key` (certificado): `"Wrap key must be of object type 'SymmetricKey'."`
- objetos marcados `Never Extractable`; só `export-public-key` (modulus/exponent) sai.
- emissor do cert: **`C=US, O=Discover Financial Services`**, **fora da ICP-Brasil**.

Ou seja: o HSM **não libera chave privada nem certificado em claro** (é o propósito dele),
então não há como montar um par cert+chave para o `curl`/`openssl`. E mesmo que houvesse,
o cert de teste não é ICP-Brasil, então o BACEN recusaria na validação de cadeia.

mTLS com chave em HSM só é viável por **assinatura delegada no handshake** (a chave nunca
sai; o TLS chama `sign-rsa` no `CertificateVerify`). Esse é o caminho técnico das cabines
PIX e SPB, e ainda assim depende do **certificado ICP-Brasil real** (arquivo público) de
uma identidade homolog, que **não existe nesta vHSM de demonstração compartilhada**.

## 6. Estado e o que falta para um erro de aplicação do BCB

Provado: (a) rede + relay até o BACEN; (b) BACEN exige/recusa cert cliente (mTLS); (c) vHSM
assina/verifica. Falta, vindo da RTM/BACEN, **um** dos itens abaixo:

1. Par **PEM de teste emitido por ICP-Brasil** (cert+chave como arquivos), para
   `curl --cert/--key` via relay resolver o transporte e chegar no nível de aplicação.
2. (definitivo) **certificado ICP-Brasil homolog da Monetarie** (arquivo público) + **UID da
   chave** na vHSM real da Monetarie. Com isso, o caminho de assinatura delegada faz o
   mTLS como a identidade `46026562`, e a consulta/operação DICT retorna erro ou resposta
   de aplicação.

Critério de aceite #6 do runbook (teste funcional real de mensagem) **continua aberto**.

## 7. Comandos de referência (todos via SSM, na VM 192.168.40.10)

vHSM:
```
POST http://200.160.162.200:63351/v1/kmip/50154/get-session-credential
POST .../sign-rsa                 (header Authorization: Bearer <session>)
POST .../signature-verify-rsa     (idem)
POST .../locate-all  +  .../get-attributes   (descobrir UIDs reais)
```
DICT GetEntry:
```
curl -k -v https://dict-h.pi.rsfn.net.br:16522/api/v2/entries/<chave> \
  -H "Accept: application/xml" -H "PI-Requesting-Participant: 46026562"
```
