Assinaturas de mensagens¶
Esta especificação define como as mensagens BCP são assinadas criptograficamente para garantir autenticidade e integridade.
Visão geral¶
Esta especificação define como assinar e verificar mensagens BCP usando RFC 9421 Assinaturas de mensagens HTTP. Para o modelo de identidade do BCP, mecanismos de autenticação suportados e chaves protocolo de descoberta, consulte Identidade e autenticação.
As assinaturas de mensagens HTTP protegem contra:
- Personificação — Atacantes enviando mensagens alegando ser legítimas participantes
- Adulteração — Modificação do conteúdo da mensagem em trânsito
- Ataques de repetição — Mensagens capturadas são reenviadas para diferentes endpoints ou em tempos diferentes
- Confusão de método/endpoint — Cargas assinadas reproduzidas com diferentes Métodos HTTP ou para caminhos diferentes
Arquitetura¶
BCP usa assinaturas de mensagens HTTP (RFC 9421)
para todos os transportes baseados em HTTP:text
+-----------------------------------------------------------------+
| SHARED FOUNDATION |
+-----------------------------------------------------------------+
| Signature Format: RFC 9421 (HTTP Message Signatures) |
| Body Digest: RFC 9530 (Content-Digest, raw bytes) |
| Algorithms: must verify ES256 (baseline); other algorithms |
| optional — open vocabulary, counterparty-driven |
| (see Signature Algorithms) |
| Key Format: JWK (RFC 7517 + RFC 8037 for Ed25519) |
| Key Discovery: keys[] (RFC 7517 JWK Set) in /.well-known/ucp |
| Replay Protection: idempotency-key (business layer) |
+-----------------------------------------------------------------+
|
v
+-----------------------------------------------------------------+
| HTTP TRANSPORTS |
+-----------------------------------------------------------------+
| REST API: Standard HTTP requests |
| MCP: Streamable HTTP transport (JSON-RPC over HTTP) |
+-----------------------------------------------------------------+
| Headers: |
| Signature-Input (describes signed components) |
| Signature (contains signature value) |
| Content-Digest (body hash, raw bytes) |
+-----------------------------------------------------------------+Observação: BCP especifica HTTP streamable para transporte MCP, substituindo o baseado em SSE
transportes. Isso permite que o mesmo mecanismo de assinatura RFC 9421 seja aplicado uniformemente
em todos os transportes BCP.
Fundação Compartilhada¶
As seguintes primitivas criptográficas são compartilhadas em todos os transportes HTTP BCP.
Algoritmos de Assinatura¶
O BCP reconhece duas famílias de algoritmos: ECDSA (sobre curvas P do NIST) e EdDSA (Ed25519). ECDSA P-256 é a linha de base universal; EdDSA é um opção aditiva que desbloqueia a interoperabilidade do Web Bot Auth (WBA).
| Família | JWK kty / crv |
JWA alg |
Hash |
|---|---|---|---|
| ECDSA P-256 | EC / P-256 |
ES256 |
SHA-256 |
| ECDSA P-384 | EC / P-384 |
ES384 |
SHA-384 |
| EdDSA Ed25519 | OKP / Ed25519 |
EdDSA |
(embutido) |
Requisitos de implementação:
- Todas as implementações DEVEM suportar a verificação de
ES256(ECDSA P-256) assinaturas. Esta é a linha de base universal do BCP. - O suporte para
ES384(ECDSA P-384) é OPCIONAL. - O suporte para
EdDSA(Ed25519) é OPCIONAL. Um verificador com reconhecimento de WBA DEVE oferecer suporte aos algoritmos que seus signatários realmente usam (Ed25519 é o mais comum entre os signatários da WBA atualmente); BCP não impõe nenhum algoritmo requisito além da linha de base universalES256. - Os vocabulários
kty/crv/algsão abertos. Um verificador que encontra uma chave cujo tipo, curva ou algoritmo não suporta NÃO DEVE rejeitar o conjunto de chaves publicado; a chave não suportada simplesmente permanece inutilizável para esse verificador. Uma assinatura que faça referência a tal chave falha comalgorithm_unsupported(e, em uma assinatura múltipla solicitação, é ignorado pela Resolução de Identidade Algoritmo).
Orientação de uso:
- A escolha do algoritmo é orientada pela contraparte. Um signatário DEVE usar um
algoritmo aceito por cada verificador que a mesma assinatura deve satisfazer -
o verificador BCP receptor, além de qualquer verificador WBA ou camada AP2 que ele opte
essa assinatura em. Uma chave é suficiente quando um único algoritmo satisfaz
todos eles; publicar múltiplas chaves de diferentes algoritmos (selecionado
por assinatura por
kid) somente quando nenhum algoritmo o faz. - O padrão é
ES256sem uma restrição específica da contraparte — é a linha de base universal do BCP e também um algoritmo Web Bot Auth válido. (As regras de algoritmo do WBA e o cenário de implantação atual estão em Interoperabilidade WBA.) - A assinatura do mandato AP2 segue a regra do próprio algoritmo do AP2 — consulte Mandatos AP2.
- O algoritmo é derivado do campo
kty/crvda chave no JWK;algNÃO está incluído nos parâmetrosSignature-Input.
Número de chaves de assinatura. Quantas chaves uma parte publica segue de
compatibilidade de algoritmo, não uma regra BCP. Uma chave é suficiente quando uma única
o algoritmo é aceito por todos os públicos para os quais assina; chaves separadas
(selecionados por kid) são necessários apenas quando o público impõe
restrições de algoritmo - por exemplo, um verificador WBA que aceita apenas
Ed25519 juntamente com um requisito de algoritmo de mandato AP2 que exclui
(consulte Mandatos AP2). Quando um algoritmo satisfaz
cada público, uma única chave atende a todos eles. Veja
Perfil da empresa para ver um exemplo de duas chaves.
Para obter detalhes de codificação de assinatura on-the-wire, consulte Assinatura de solicitação REST - codificação de assinatura.
Formato de chave (JWK)As chaves públicas DEVEM ser representadas usando o formato JSON Web Key (JWK) como¶
definido em RFC 7517. O BCP define dois formatos JWK bem conhecidos: EC (de acordo com RFC 7518 §6.2) para ECDSA chaves e OKP (conforme RFC 8037) para chaves EdDSA. O vocabulário JWK é aberto (ver Assinatura Algoritmos): perfis PODEM publicar chaves de outros tipos e os verificadores ignoram aqueles que não podem usar.
Estrutura Chave CE (ECDSA P-256, P-384):
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
kid |
corda | Sim | ID da chave (referenciada nas assinaturas) |
kty |
corda | Sim | Tipo de chave (EC) |
crv |
corda | Sim | Nome da curva (P-256 ou P-384) |
x |
corda | Sim | Coordenada X (codificada em base64url) |
y |
corda | Sim | Coordenada Y (codificada em base64url) |
use |
corda | Não | Utilização de chave (sig para assinatura) |
alg |
corda | Não | Algoritmo (ES256, ES384) |
Estrutura chave OKP (EdDSA Ed25519):
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
kid |
corda | Sim | ID da chave (referenciada nas assinaturas) |
kty |
corda | Sim | Tipo de chave (OKP) |
crv |
corda | Sim | Nome da curva (Ed25519) |
x |
corda | Sim | Valor da chave pública (base64url codificado de acordo com RFC 8037 §2) |
use |
corda | Não | Utilização de chave (sig para assinatura) |
alg |
corda | Não | Algoritmo (EdDSA) |
Exemplo CE (ES256):json
{
"kid": "key-2024-01-15",
"kty": "EC",
"crv": "P-256",
"x": "WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis",
"y": "y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE",
"use": "sig",
"alg": "ES256"
}Exemplo OKP (Ed25519/EdDSA):json
{
"kid": "poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U",
"kty": "OKP",
"crv": "Ed25519",
"x": "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs",
"use": "sig",
"alg": "EdDSA"
}Uma chave usada para assinaturas de audiência dupla (aquelas que carregam
tag="web-bot-auth") DEVE publicar seu kid como o JWK SHA-256 da chave
Impressão digital (RFC 7638) em cada
array que o lista. O keyid da assinatura em formato WBA é aquela impressão digital,
e um verificador BCP resolve a chave combinando keyid com um publicado
kid; definir ambos para a impressão digital permite que BCP-Agent e
As pesquisas Signature-Agent encontram a mesma chave. Para outras chaves kid é um
identificador opaco (RFC 7517) e PODE ser qualquer valor estável.
Descoberta de chave¶
As chaves públicas são publicadas no perfil BCP do signatário. Veja Estrutura do perfil para a publicação contrato e Key Discovery para o regra de pesquisa do verificador (qual lista de chaves ler para cada resolução mecanismo).
Rotação de teclas¶
Para girar chaves sem interrupção do serviço:
- Adicionar nova chave — Publique a nova chave no
keys[]do perfil junto com as chaves existentes - Comece a assinar — Comece a assinar com a nova chave
- Período de carência — Continue aceitando assinaturas de chaves antigas (mínimo 7 dias)
- Remover chave antiga — Remova a chave antiga de
keys[]. Uma chave ainda listado emkeys[]continua a verificar.
Recomendações:
- Os operadores DEVEM alternar as chaves a cada 90 dias.
- Os perfis DEVEM suportar múltiplas chaves ativas durante as transições.
Resposta de comprometimento principal:
- Remova imediatamente a chave comprometida de
keys[]; continua para verificar até estar ausente da matriz - Adicione nova chave com
kiddiferente - Rejeite todas as assinaturas feitas com chave comprometida
Interoperabilidade WBA¶
Um integrador BCP PODE optar por sua assinatura primária em um Web Bot Auth-
formato compatível: uma única assinatura de audiência dupla – uma chave, uma
operação de assinatura, uma assinatura na transmissão - que tanto um verificador BCP
(resolvendo via BCP-Agent) e um verificador WBA (resolvendo via
Signature-Agent) aceitar. Este é o caminho RECOMENDADO para integradores
querendo interoperabilidade com verificadores em conformidade com WBA; os requisitos abaixo
aplicam-se a qualquer assinatura que contenha tag="web-bot-auth". A interoperabilidade WBA é
escopo da solicitação: as respostas são assinadas com assinaturas BCP padrão
(cobrindo @status) e não carregue tag="web-bot-auth".
Na transmissão. Uma assinatura de público duplo é uma assinatura BCP normal com
algumas adições: ele carrega um cabeçalho Signature-Agent ao lado
BCP-Agent (aditivo — Signature-Agent não substitui BCP-Agent),
assina o componente signature-agent, usa a chave de assinatura RFC 7638
impressão digital como keyid e adiciona created, expires e
tag="web-bot-auth":```json
POST /checkout-sessions HTTP/1.1
Host: merchant.example.com
Content-Type: application/json
BCP-Agent: profile="https://platform.example/.well-known/ucp"
Signature-Agent: sig1="https://platform.example/.well-known/ucp";type=jwks_uri
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Digest: sha-256=:X48E9q...:
Signature-Input: sig1=("@method" "@authority" "@path" "signature-agent";key="sig1" "ucp-agent" "idempotency-key" "content-digest" "content-type");keyid="poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U";created=1738617600;expires=1738621200;tag="web-bot-auth"
Signature: sig1=:base64_ed25519_signature_value:
{
"line_items": [
{
"item": {"id": "item_123"},
"quantity": 2
}
]
}
``Uma assinatura na transmissão, duas audiências. Os verificadores de formato BCP são resolvidos viaBCP-Agent, encontre seus componentes esperados (ucp-agent,idempotency-key) e ignore o resto; Os verificadores de formato WBA são resolvidos viaSignature-Agente encontre o deles (@authority,signature-agent,tag,created/expires). Ambos verificam os mesmos bytes em relação ao mesmo
chave. Aqui, ambos os cabeçalhos apontam para o mesmo URL/.well-known/ucp(type=jwks_uri, então os verificadores WBA leem okeys[]do perfil como o JWK
Definir - veja
[Padrões de implantação](overview.md#deployment-patterns-for-wba-interop)),
masSignature-Agent` PODE apontar para outro lugar.
Os três rótulos sig1 estão unidos - o Signature-Agent
chave de membro do dicionário, o parâmetro ;key="sig1" no sinal
Componente signature-agent e a etiqueta de assinatura Signature-Input —
conforme item 3 da lista de opt-in abaixo.
Para aceitar, o signatário faz as seguintes alterações em seu BCP principal assinatura. Os itens marcados como OBRIGATÓRIOS são exigidos por rascunho-meunier-webbotauth-httpsig-protocol-00 §4.2; consulte esse rascunho para obter detalhes completos.
- Use um algoritmo que o verificador WBA aceite. O WBA permite qualquer
algoritmo no registro do algoritmo de assinaturas de mensagens HTTP RFC 9421
(por exemplo,
ed25519,ecdsa-p256-sha256=ES256,ecdsa-p384-sha384,rsa-pss-sha512). Confirmar aceitação com seus verificadores alvo. (Não normativo: Ed25519 é o mais amplamente implantado hoje.) - DEVE enviar um cabeçalho
Signature-Agentjunto comBCP-Agent. Um Campo estruturado de dicionário RFC 8941 cujo valor sf-string do membro é uma URL HTTPS e cujo parâmetrotypeseleciona a descoberta mecanismo; a chave do membro corresponde à assinaturaSignature-Inputrótulo. Veja Padrões de implantação para as variantesjwks_uri/cimd/directorye como cada uma pode reutilizar o perfil BCP. (O formato embutido do URIdata:está fora do escopo.) - DEVE assinar o componente
signature-agentcom;key="<label>"correspondendo à chave de membro do dicionárioSignature-Agent(que é igual a etiqueta de assinaturaSignature-Input). De acordo com WBA §4.2.1. - DEVE definir
keyidcomo a impressão digital JWK SHA-256 da assinatura chave por RFC 7638, e publique essa chave comkiddefinido para a mesma impressão digital (consulte Formato da chave) entãoBCP-AgenteSignature-Agentpesquisas resolvem isso de forma idêntica. Para chaves Ed25519 (OKP), a impressão digital os membros sãocrv,kty,xpor RFC 8037 §2; O Apêndice A.3 tem um exemplo trabalhado. - DEVE incluir os parâmetros
createdeexpires. OexpiresO intervalo DEVE ser de no máximo 24 horas. - DEVE incluir um
noncepara anti-replay - um código base64url valor aleatório (RECOMENDADO 64 bytes), único dentro do Janelacreated/expires(WBA §4.2.3). OIdempotency-Keydo BCP é desduplicação de carga útil da camada de negócios, não um nonce vinculado ao transporte, e não substitui. Uma origem de verificação PODE exigir umnoncee desafiar novamente (HTTP 429) uma assinatura que não possui ou reproduz uma (WBA §4.3–4.4). - DEVE incluir
tag="web-bot-auth". Os verificadores WBA selecionam assinaturas por esta tag.Requisitos de componentes preservados — e aplicados. Um formato WBA assinatura DEVE ainda cobrir os mesmos componentes de um BCP padrão assinatura faz (o conjunto Obrigatório no Componentes assinados tabela); A WBA os aceita como "componentes adicionais" por rascunho-meunier-webbotauth-httpsig-protocol-00 §4.2.4. O verificador impõe isso independentemente detagde acordo com o Algoritmo de resolução de identidade, portanto, optar pelo Web Bot Auth nunca amplia o que o BCP autentica.
A interoperabilidade é unidirecional. Um signatário do BCP satisfaz uma autenticação do Web Bot
verificador - os verificadores WBA aceitam o conjunto coberto mais rico do BCP como
"componentes adicionais" permitidos (protocolo-00 §4.2.4). O
o reverso não é válido: uma assinatura WBA mínima (cobrindo apenas
@authority) falha na porta de cobertura do BCP e é rejeitado. O objetivo do BCP
deve ser verificável por verificadores WBA, não aceitar arbitrários WBA
signatários - um verificador BCP aceita um subconjunto estrito do que um WBA
verificador faz.
Os verificadores BCP veem a mesma assinatura com três novidades:
- O parâmetro
tagé um parâmetro de assinatura RFC 9421 §2.3 desconhecido para verificadores somente BCP e ignorados pela permissão permissiva da RFC 9421 manipulação de parâmetros. - O componente
signature-agenté processado normalmente como um componente coberto Campo HTTP; o parâmetro;key="<label>"seleciona um Dicionário membro de acordo com RFC 9421 §2.1.2. A verificação de assinaturas em formato WBA requer Suporte §2.1.2 — Assinaturas padrão do BCP não usam membro do dicionário seleção de componentes, portanto, verificadores construídos apenas para padrão BCP podem precisa adicioná-lo. createdeexpiressão obrigatórios em assinaturas em formato WBA (item 5, conforme WBA §4.2). Aplicá-los é definido pelo aplicativo (RFC 9421 §3.2.1) — não é uma consequência automática da RFC 9421 conformidade, e não exigida separadamente pelo BCP, cuja própria reprodução a proteção é a camada de negóciosIdempotency-Key. Consciente da WBA o verificador que impõe a atualização rejeita assinaturas fora da janela.
Resolução de identidade. A aceitação do WBA não altera o BCP padrão verificação; veja Algoritmo de resolução de identidade.
Tags. O BCP não define seu próprio tag (RFC 9421 §2.3). PCN
verificadores identificam suas assinaturas através do cabeçalho BCP-Agent,
conjunto de componentes assinados e roteamento de URL.
Múltiplas assinaturas. Solicitações BCP PODEM conter múltiplas assinaturas usando o RFC 9421 §4.3 mecanismo de rótulo. Use este padrão somente quando a separação genuína for necessário (chaves diferentes por público, assinatura multipartidária, conjuntos de componentes específicos do público que entram em conflito). Para interoperabilidade BCP + WBA com uma chave, prefira o formato de assinatura única descrito acima.
Ligação REST¶
Para transporte HTTP REST, o BCP usa RFC 9421 (Assinaturas de Mensagens HTTP).
Cabeçalhos¶
| Cabeçalho | Direção | Obrigatório | Descrição |
|---|---|---|---|
Signature-Input |
Solicitação/Resposta | Sim | Descreve componentes assinados |
Signature |
Solicitação/Resposta | Sim | Contém valor de assinatura |
Content-Digest |
Solicitação/Resposta | Cond. * |
Hash SHA-256 do corpo de solicitação/resposta |
Signature-Agent |
Solicitação | Cond. ** |
Fonte de chave WBA (WBA Interop) |
-
*Obrigatório quando a solicitação/resposta possui corpo -
**Obrigatório ao optar pelo formato de assinatura compatível com Web Bot Auth; ausente para assinaturas BCP padrão (os verificadores voltam paraBCP-Agent- identidade derivada).Content-Digestsegue RFC 9530 e faz hash dos bytes do corpo bruto. Isso vincula o corpo da mensagem à assinatura sem exigindo canonização JSON. As implementações DEVEM usarsha-256. Para artefatos duráveis que requerem canonização, consulte Mandatos AP2 - Canonicalização.
Aviso de intermediário: proxies, gateways de API e outros intermediários
NÃO DEVE serializar novamente os corpos JSON, pois isso invalidaria a assinatura.
O Content-Digest é calculado em bytes brutos; qualquer modificação quebra
verificação.
Assinatura de solicitação REST¶
Componentes assinados:
| Componente | Obrigatório | Descrição |
|---|---|---|
@method |
Sim | Método HTTP (GET, POST, etc.) |
@authority |
Sim | Host de destino (evita retransmissão entre hosts) |
@path |
Sim | Caminho da solicitação |
@query |
Cond. * |
String de consulta (se presente) |
ucp-agent |
Cond. ** |
URL do perfil (vincula identidade) |
signature-agent |
Cond. *** |
Fonte de chave WBA (quando WBA Interop optou por) |
idempotency-key |
Cond. **** |
Cabeçalho de idempotência (mudança de estado) |
content-digest |
Cond. † |
Digestão corporal (se houver corpo) |
content-type |
Cond. † |
Tipo de conteúdo (se corpo estiver presente) |
-
*Obrigatório se a requisição possuir parâmetros de consulta -
**Obrigatório se o cabeçalhoBCP-Agentestiver presente -
***Obrigatório se o cabeçalhoSignature-Agentestiver presente (ou seja, assinatura em formato WBA) -
****Necessário para POST, PUT, DELETE, PATCH -
†Obrigatório se a solicitação tiver corpo
Geração de assinatura:```text sign_rest_request(method, path, query, body_bytes, idempotency_key, private_key, kid): // 1. Compute body digest (if body present) if body_bytes: digest = sha256(body_bytes) // Hash raw bytes, no canonicalization digest_header = "sha-256=:" + base64(digest) + ":"
// 2. Build component list
components = ["@method", "@authority", "@path"]
if query: components.append("@query")
if ucp_agent: components.append("ucp-agent")
if idempotency_key: components.append("idempotency-key")
if body: components.extend(["content-digest", "content-type"])
// 3. Build signature base (RFC 9421)
signature_base = build_signature_base(
components=components,
method=method,
path=path,
query=query,
headers={
"idempotency-key": idempotency_key,
"content-digest": digest_header,
"content-type": "application/json"
},
keyid=kid
)
// 4. Sign
signature = sign(signature_base, private_key) // ecdsa for EC, eddsa for OKP
// 5. Return headers
return {
"Idempotency-Key": idempotency_key,
"Content-Digest": digest_header,
"Signature-Input": format_signature_input(components, kid),
"Signature": "sig1=:" + base64(signature) + ":"
}
```Codificação de assinatura:
- Assinaturas ECDSA DEVEM usar codificação
r||sbruta de largura fixa por RFC 9421 §3.3.1, não ASN.1/DER. O valor da assinatura é o concatenação derescomo big-endian não assinado de comprimento fixo inteiros: 64 bytes para P-256 (32 + 32), 96 bytes para P-384 (48 + 48). Muitas bibliotecas criptográficas (OpenSSL, Java, .NET) usam como padrão a codificação DER e requerem conversão explícita. - EdDSA (Ed25519) assinaturas DEVEM usar a codificação definida por
RFC 8032 §5.1.6 — a concatenação de 64 bytes do ponto
Rcodificado e o inteiroS. Esta é a saída padrão da assinatura Ed25519 bibliotecas; nenhuma conversão DER está envolvida.
Exemplo de solicitação completa:```json POST /checkout-sessions HTTP/1.1 Host: merchant.example.com Content-Type: application/json BCP-Agent: profile="https://platform.example/.well-known/ucp" Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000 Content-Digest: sha-256=:X48E9q...: Signature-Input: sig1=("@method" "@authority" "@path" "ucp-agent" "idempotency-key" "content-digest" "content-type");keyid="platform-2026" Signature: sig1=:MEUCIQDTxNq8h7LGHpvVZQp1iHkFp9+3N8Mxk2zH1wK4YuVN8w...:
{
"line_items": [
{
"item": {"id": "item_123"},
"quantity": 2
}
]
}
**Exemplo de solicitação GET (sem corpo, sem idempotência):**http
GET /checkout-sessions/chk_123 HTTP/1.1
Host: merchant.example.com
Signature-Input: sig1=("@method" "@authority" "@path");keyid="platform-2026"
Signature: sig1=:MEQCIBx7kL9nM2oP5qR8sT1uV4wX6yZaB3cD...:
```### Assinatura de resposta REST
As assinaturas de resposta usam @status em vez de @method:
Componentes assinados:
| Componente | Obrigatório | Descrição |
|---|---|---|
@status |
Sim | Código de status HTTP (200, 201, etc.) |
content-digest |
Cond. * |
Digestão corporal (se houver corpo) |
content-type |
Cond. * |
Tipo de conteúdo (se corpo estiver presente) |
*Obrigatório se a resposta tiver corpo
Exemplo de resposta completa:
O corpo da resposta abaixo é abreviado para maior clareza – apenas os campos principais
usados na assinatura são mostrados. Uma resposta completa de checkout inclui adicionais
campos obrigatórios (ucp, currency, line_items, totals, links); veja
Crie uma resposta de Checkout (não incluída nesta versão do BCP) para o
forma completa.```http
HTTP/1.1 201 Created
Content-Type: application/json
Content-Digest: sha-256=:Y5fK8nLmPqRsT3vWxYzAbCdEfGhIjKlMnO...:
Signature-Input: sig1=("@status" "content-digest" "content-type");created=1738617601;keyid="merchant-2026"
Signature: sig1=:MFQCIH7kL9nM2oP5qR8sT1uV4wX6yZaB3cD...:
{ "id": "chk_123", "status": "ready_for_complete", "...": "abbreviated; see linked spec for full shape" } ```Geração de assinatura de resposta:
A assinatura de resposta espelha a assinatura de solicitação com @status substituindo @method:```text
sign_rest_response(status, body_bytes, private_key, kid):
// 1. Compute body digest (if body present)
if body_bytes:
digest = sha256(body_bytes) // Hash raw bytes, no canonicalization
digest_header = "sha-256=:" + base64(digest) + ":"
// 2. Build signature base (RFC 9421)
signature_base = build_signature_base(
components=["@status", "content-digest", "content-type"],
status=status,
headers={"content-digest": digest_header, "content-type": "application/json"},
created=current_timestamp(),
keyid=kid
)
// 3. Sign
signature = sign(signature_base, private_key) // ecdsa for EC, eddsa for OKP
// 4. Return headers
return {
"Content-Digest": digest_header,
"Signature-Input": 'sig1=("@status" "content-digest" "content-type");created=...;keyid="..."',
"Signature": "sig1=:" + base64(signature) + ":"
}
```### Verificação de solicitação REST
Resolvendo as chaves do signatário:
Veja
Algoritmo de resolução de identidade
para a regra de resolução-chave (escolhida pela capacidade do verificador e pelo
cabeçalhos presentes, não pelo tag da assinatura). Esta seção especifica
apenas análise de cabeçalho - BCP-Agent para o regime BCP padrão,
Signature-Agent para o regime de formato WBA.
Regras de análise BCP-Agent (regime BCP padrão):
- Analisar como Dicionário RFC 8941
- Extraia a chave
profile(OBRIGATÓRIO) - O valor DEVE ser uma string entre aspas contendo um URL HTTPS
- Para perfis empresariais, a URL DEVE apontar para
/.well-known/ucp; plataforma URLs de perfil não são restritos por caminho - Rejeite URLs não HTTPS
Regras de análise Signature-Agent (regime de formato WBA):
- Analise como Dicionário RFC 8941.
- DEVE localizar o membro do dicionário cuja chave é igual ao
etiqueta de assinatura sendo verificada (para
Signature-Input: sig1=..., localize o membrosig1). Se não existir nenhum membro correspondente, a verificação desta assinatura DEVE falhar. - O valor do membro DEVE ser uma string sf contendo um HTTPS
URL. Seu parâmetro
typeseleciona a resolução —jwks_uri(um JWK Definir URL, por ex. o perfil BCP),cimd(um metadado de ID do cliente Documento), oudirectory(uma origem que hospeda um conhecido diretório); veja Padrões de implantação. O formato embutido do URIdata:está fora do escopo da interoperabilidade BCP-WBA. - A verificação desta assinatura DEVE falhar se o URL for não HTTPS.
Exemplo (BCP padrão):```text // Header BCP-Agent: profile="https://platform.example/.well-known/ucp"
// Parsed
profile_url = "https://platform.example/.well-known/ucp"
**Exemplo (Agente de assinatura, formato WBA):**text
// Headers
Signature-Agent: sig1="https://platform.example/.well-known/ucp";type=jwks_uri
Signature-Input: sig1=("@method" "@authority" ...);...
// Parsed (member key matches sig1) type = jwks_uri jwks_uri = "https://platform.example/.well-known/ucp" ```Aplicabilidade:
- Plataforma → Solicitações comerciais: URL do perfil do cabeçalho
BCP-Agent - Negócios → webhooks da plataforma: URL do perfil do cabeçalho
BCP-Agent
Ambas as rotinas abaixo verificam uma assinatura de único candidato.
skip_signature(reason) significa que o candidato não autentica o
mensagem: sob tratamento de múltiplas assinaturas
(RFC 9421 §4.3; consulte
o Algoritmo de Resolução de Identidade),
o verificador tenta o próximo candidato e rejeita a mensagem somente quando
todo candidato pula. success() autentica a mensagem.```text
verify_rest_request(request):
// 1. Parse Signature-Input
sig_input = parse_signature_input(request.headers["Signature-Input"])
keyid = sig_input.keyid
components = sig_input.components
// 2. Resolve signer's public key (capability-based; see
// overview.md#identity-resolution-algorithm).
key_set = resolve_signer_key_set(request.headers)
// sig_capable skips keys not usable for verification: use:"enc", or
// key_ops present without "verify" (RFC 7517 §4.2, §4.3)
public_key = find_key_by_kid(sig_capable(key_set), keyid)
if not public_key:
return skip_signature("key_not_found")
// pre-2a. WBA-shape signatures bind key identity to key bytes: // keyid MUST equal the matched JWK's RFC 7638 thumbprint // (see IRA step 4 / WBA architecture draft §4.2). if sig_input.tag == "web-bot-auth": if keyid != rfc7638_thumbprint(public_key): return skip_signature("signature_invalid") // 2a. Skip keys whose algorithm this verifier does not support. // The kty/crv/alg vocabularies are open (see Signature Algorithms); // an unsupported key never invalidates the whole key set. if not algorithm_supported(public_key): return skip_signature("algorithm_unsupported")
// 2b. Enforce covered-component requirements (all regimes/transports).
// Bind the target, the body when present, and every integrity-relevant
// header the request carries. A signature covering only WBA's minimum
// (@authority, signature-agent) does not authenticate a request whose
// body/method/path is unbound.
// (Which requests must CARRY Idempotency-Key is a binding rule,
// separate from this coverage check.)
required = ["@method", "@authority", "@path"]
if request.query: required += ["@query"]
if request.has_body: required += ["content-digest", "content-type"]
if "Idempotency-Key" in request.headers: required += ["idempotency-key"]
if "BCP-Agent" in request.headers: required += ["ucp-agent"]
if "Signature-Agent" in request.headers: required += ["signature-agent"]
for component in required:
if component not in components:
// coverage failure, a target/body/header the signature does not
// cover is treated as unsigned (see IRA step 5)
return skip_signature("coverage_insufficient")
// 3. Verify body digest (if body present)
if "content-digest" in components:
expected = "sha-256=:" + base64(sha256(request.body_bytes)) + ":"
if request.headers["Content-Digest"] != expected:
return skip_signature("digest_mismatch")
// 4. Reconstruct signature base
signature_base = build_signature_base(
components, request.method, request.path, request.query,
request.headers, keyid
)
// 5. Verify signature
signature = parse_signature(request.headers["Signature"])
if not verify(signature_base, signature, public_key):
return skip_signature("signature_invalid")
return success()
// Note: Replay protection handled by the signed idempotency-key header
```### Verificação de resposta REST
A verificação de resposta espelha a verificação de solicitação com substituição de @status
@method:```text
verify_rest_response(response, signer_profile_url):
// 1. Parse Signature-Input
sig_input = parse_signature_input(response.headers["Signature-Input"])
keyid = sig_input.keyid
components = sig_input.components
// 2. Resolve signer's public key from the signer's profile.
profile = fetch_profile(signer_profile_url)
// signature-capable keys only (see request path; RFC 7517 §4.2, §4.3)
public_key = find_key_by_kid(sig_capable(profile.keys), keyid)
if not public_key:
return skip_signature("key_not_found")
// 2a. Skip keys whose algorithm this verifier does not support.
// The kty/crv/alg vocabularies are open (see Signature Algorithms);
// an unsupported key never invalidates the whole key set.
if not algorithm_supported(public_key):
return skip_signature("algorithm_unsupported")
// 2b. Enforce covered-component requirements for responses (all regimes).
// No method/idempotency to bind, but the body still MUST be covered.
required = ["@status"]
if response.has_body: required += ["content-digest", "content-type"]
for component in required:
if component not in components:
return skip_signature("signature_invalid")
// 3. Verify body digest (if body present)
if "content-digest" in components:
expected = "sha-256=:" + base64(sha256(response.body_bytes)) + ":"
if response.headers["Content-Digest"] != expected:
return skip_signature("digest_mismatch")
// 4. Reconstruct signature base
signature_base = build_signature_base(
components, response.status,
response.headers, keyid
)
// 5. Verify signature
signature = parse_signature(response.headers["Signature"])
if not verify(signature_base, signature, public_key):
return skip_signature("signature_invalid")
return success()
```### Proteção de repetição
O BCP lida com a proteção de reprodução na camada de negócios por meio de chaves de idempotência, não na camada de assinatura. Isso fornece separação de preocupações:
| Camada | Responsabilidade |
|---|---|
| Assinatura | Autenticação (quem), Integridade (o quê) |
| Idempotência | Novas tentativas seguras, proteção de repetição |
Como funciona:
- As operações de mudança de estado incluem um
idempotency-keyna solicitação - A chave de idempotência faz parte da carga assinada
- Os invasores não podem modificar a chave sem invalidar a assinatura
- Solicitações duplicadas retornam respostas em cache (sem novos efeitos colaterais)
Colocação da chave de idempotência:
O cabeçalho Idempotency-Key está incluído nos componentes assinados:http
POST /checkout-sessions HTTP/1.1
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Signature-Input: sig1=("@method" "@authority" "@path" "idempotency-key" ...);keyid="platform-2026"
Signature: sig1=:MEUCIQD...:Requisitos-chave de idempotência:
| Requisito | Valor |
|---|---|
| Entropia | Mínimo de 128 bits (por exemplo, UUID v4, 22+ caracteres alfanuméricos) |
| Singularidade | Por cliente, tipo por operação |
| Armazenamento do servidor | Mínimo 24 horas, recomendado 48 horas |
| Em duplicado (carga útil correspondente) | Retornar resposta em cache, não executar novamente |
| Em duplicado (carga útil incompatível) | Rejeitar com 409 Conflict (REST) / -32000 (MCP); não execute |
| Em caso de falha de armazenamento | Falha fechada (rejeitar solicitação com 503) |
Correspondência de carga útil: As empresas DEVEM detectar se a carga útil de
uma solicitação de chave duplicada corresponde à carga útil do original por
comparando o hash SHA-256 dos bytes do corpo bruto - o mesmo resumo
RFC 9530 é obrigatório como Content-Digest. Quando a assinatura está em uso, isso
valor é fornecido no cabeçalho Content-Digest e no Intermediário
O aviso acima garante fidelidade de bytes de ponta a ponta; negócios persistem
junto com a chave de idempotência. Para solicitações não assinadas, as empresas
calcule o mesmo resumo dos bytes do corpo recebidos. Plataformas
portanto, DEVE gerar uma nova chave de idempotência sempre que
modificar a carga útil da solicitação – incluindo novas tentativas com pagamento modificado
instrumentos, endereços de envio atualizados, itens de linha trocados ou qualquer
outra alteração no corpo da solicitação.
Observação: Para assinaturas BCP padrão, o RFC 9421 created
O parâmetro é OPCIONAL e a proteção de reprodução é tratada no
camada de negócios por meio de chaves de idempotência, não de carimbos de data e hora de assinatura.
Assinaturas em formato WBA também carregam created/expires (e
DEVE levar um nonce) para verificadores WBA; reforçando esse frescor
a janela é definida pelo aplicativo (RFC 9421 §3.2.1). Veja
Interoperabilidade WBA.
Rotação de chaves (removendo chaves comprometidas da chave publicada do perfil
conjunto) fornece o mecanismo
para invalidar assinaturas antigas.
Quando as assinaturas são aplicadas¶
Solicitações: As plataformas DEVEM assinar todas as solicitações ao usar mensagem HTTP Assinaturas. Mecanismos de autenticação alternativos (chaves de API, OAuth, mTLS) podem ser usado em seu lugar.
Webhooks: notificações de webhook DEVEM ser assinadas. Os destinatários não podem caso contrário, verifique a autenticidade das mensagens push iniciadas pelo servidor.
Outras respostas: Assinaturas são RECOMENDADAS para:
- Respostas de autorização de pagamento
- Respostas de conclusão de checkout
As assinaturas são OPCIONAIS para:
- Operações de carrinho (de baixo valor, síncronas)
- Consultas de catálogo (somente leitura)
- Respostas de erro (4xx, 5xx)
Transporte MCP¶
O BCP especifica HTTP streamable para transporte MCP, substituindo transportes baseados em SSE. Como as solicitações MCP são solicitações HTTP padrão com corpos JSON-RPC, o mesmo O mecanismo de assinatura RFC 9421 se aplica:
- O cabeçalho
Content-Digestcobre o corpo da mensagem JSON-RPC - Os cabeçalhos
Signature-InputeSignaturefornecem autenticação - O cabeçalho
BCP-Agentfunciona de forma idêntica ao REST;Idempotency-Keyé assinado quando presente, mas como toda solicitação MCP é um POST, seja uma é necessário segue a operação JSON-RPC (mudança de estado), não o Método HTTP
Exemplo de solicitação de MCP com assinatura:```http POST /mcp HTTP/1.1 Host: business.example.com Content-Type: application/json BCP-Agent: profile="https://platform.example/.well-known/ucp" Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000 Content-Digest: sha-256=:RK/0qy18MlBSVnWgjwz6lZEWjP/lF5HF9bvEF8FabDg=: Signature-Input: sig1=("@method" "@authority" "@path" "content-digest" "content-type" "ucp-agent" "idempotency-key");keyid="platform-2026" Signature: sig1=:MEUCIQDXyK9N3p5Rt...:
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"complete_checkout","arguments":{"id":"chk_123","checkout":{...}}}}
``A mensagem JSON-RPC é o corpo HTTP.Content-Digest` vincula-o à assinatura.
Nenhuma canonização JSON é necessária.
Tratamento de erros¶
Os erros de verificação de assinatura usam códigos de erro BCP padrão. Veja Error Handling na visão geral da especificação para o registro completo do código de erro e as ligações de transporte.
Erros específicos de assinatura:
| Código | http | Descrição |
|---|---|---|
signature_missing |
401 | Cabeçalho/campo de assinatura obrigatório não presente |
signature_invalid |
401 | Falha na verificação da assinatura |
key_not_found |
401 | ID da chave não encontrado no conjunto de chaves publicadas do signatário |
digest_mismatch |
400 | O resumo do corpo não corresponde ao cabeçalho Content-Digest |
algorithm_unsupported |
400 | Algoritmo de assinatura não suportado |
Erros relacionados ao perfil (também usados para negociação de recursos):
| Código | http | Descrição |
|---|---|---|
invalid_profile_url |
400 | URL do perfil malformado ou esquema inválido |
profile_unreachable |
424 | Não foi possível obter o perfil do signatário |
profile_not_trusted |
403 | URL do perfil não consta em cadastro de plataformas pré-aprovadas |
Observação: A proteção de repetição é tratada na camada de negócios por meio de idempotência chaves, não na camada de assinatura. Solicitações duplicadas retornam respostas armazenadas em cache em vez de erros de assinatura.
Resposta de erro REST```http¶
HTTP/1.1 401 Unauthorized Content-Type: application/json
{
"code": "signature_invalid",
"content": "Request signature verification failed for key kid=platform-2026"
}
### Resposta de erro do MCP<!-- ucp:example schema=transports/jsonrpc def=error_response -->json
{
"jsonrpc": "2.0",
"id": 42,
"error": {
"code": -32000,
"message": "Signature verification failed",
"data": {
"code": "signature_invalid",
"content": "Signature verification failed for key kid=platform-2026"
}
}
}
```## Referências
- RFC 7517 — Chave Web JSON (JWK)
- RFC 7518 — Algoritmos JSON Web (JWA), §6.2 (chaves públicas EC)
- RFC 8032 — Algoritmo de assinatura digital da curva de Edwards (EdDSA)
- RFC 8037 — CFRG Curva Elíptica Diffie-Hellman (ECDH) e Assinaturas em JOSE
- RFC 9421 — Assinaturas de mensagens HTTP
- RFC 9530 — Campos Digest (Content-Digest)