Signed Agent Cards: preventing poisoning and spoofing with JWS

11 min read

Os artigos são publicados em inglês; traduções são exibidas quando disponíveis.

1name · description · versionerror2supportedInterfaces[] · url · protocolBinding · protocolVersionerror3capabilities · defaultInputModes · defaultOutputModeserror4skills[] · id · name · description · tagserror5securitySchemes · securitywarning6provider · signatures[]warning
Cryptographic verification sits at the trust layer of the Agent Card validation pipeline, ensuring metadata integrity before any remote agent execution begins.

When AI agents discover each other across public networks by fetching an agent.json file, trust cannot depend on trusting the network. An unsigned card is a single DNS spoof or CDN misconfiguration away from redirecting autonomous systems to malicious endpoints. In A2A v1.0, Signed Agent Cards solve this with JSON Web Signatures.

Why unsigned cards are vulnerable

In the A2A protocol specification, discovery is designed to be lightweight, static, and unauthenticated: a client agent performs a simple HTTP GET request to /.well-known/agent.json.

While this simplicity enabled rapid ecosystem adoption, it introduces a severe attack surface known in enterprise security as Agent Card Poisoning:

  1. Endpoint Redirection: An attacker who compromises a DNS entry or an edge proxy can rewrite the supportedInterfaces[].url field, silently sending sensitive prompt payloads to an eavesdropping or impersonation server.
  2. Capability & Prompt Injection: By tampering with the skills array or description fields, malicious actors can deceive calling agents into delegating high-privilege financial or administrative tasks to an unvetted model.
  3. Identity Spoofing: Without cryptographic verification, anyone can host an agent card claiming that provider.organization is an established financial institution or software vendor.

To defend multi-agent architectures against these risks, the official specification maintained by the a2aproject/A2A repository formalizes the signatures array based on RFC 7515: JSON Web Signature (JWS).

Anatomy of the signatures array

In an A2A v1.0 Agent Card, the top-level signatures property holds an array of signature objects. This allows a card to be signed by multiple authorities—for example, the software vendor who authored the agent, and an enterprise compliance auditor who certified its security boundaries.

Each entry follows either standard JWS Flattened JSON serialization or the canonical JWS compact format:

Agent Card snippet with JWS signatures
{
  "name": "Treasury Reconciliation Agent",
  "version": "1.2.0",
  "supportedInterfaces": [
    {
      "url": "https://agents.acme-finance.com/a2a/v1",
      "protocolBinding": "JSONRPC",
      "protocolVersion": "1.0"
    }
  ],
  "signatures": [
    {
      "protected": "eyJhbGciOiJFUzI1NiIsImtpZCI6IjIwMjYtMDktY29yZSIsInR5cCI6IkpXUyJ9",
      "signature": "MEQCIFz8fV9Q2b6HqVqYx3...7k1vU8X3g"
    }
  ]
}

The fields within each signature object operate as follows:

  • protected: A Base64URL-encoded JSON string containing the JWS header parameters:
    • alg: The cryptographic algorithm used (such as ES256 or RS256).
    • kid: Key identifier, resolving to the public verification key hosted at the provider's trusted key store.
    • crit: (Optional) Array of critical header extensions that the verifying agent must recognize.
  • signature: The Base64URL-encoded digital signature computed over the normalized payload and protected header.

Signing an agent.json with ES256

To sign an Agent Card, the document payload must be deterministically canonicalized (stripping any existing signatures field prior to signing) to guarantee consistent digest generation across different programming languages and runtimes.

Here is a reference implementation in Node.js using the standard Web Crypto API:

sign-agent-card.mjs
import crypto from 'node:crypto';
import fs from 'node:fs';

// 1. Read card and remove any existing signatures for canonical payload
const rawCard = JSON.parse(fs.readFileSync('agent.json', 'utf8'));
const { signatures, ...unsignedPayload } = rawCard;
const payloadString = JSON.stringify(unsignedPayload);

// 2. Prepare JWS Protected Header
const header = {
  alg: 'ES256',
  kid: 'https://acme-finance.com/.well-known/jwks.json#key-2026',
  typ: 'JWS'
};

const base64Url = (str) => Buffer.from(str).toString('base64url');

const protectedHeaderB64 = base64Url(JSON.stringify(header));
const payloadB64 = base64Url(payloadString);
const signingInput = `${protectedHeaderB64}.${payloadB64}`;

// 3. Sign using your private EC key (P-256)
const privateKeyPem = fs.readFileSync('private-key.pem', 'utf8');
const signer = crypto.createSign('SHA256');
signer.update(signingInput);
signer.end();

const signatureB64 = signer.sign(privateKeyPem, 'base64url');

// 4. Attach signature block back to the agent card
const signedCard = {
  ...rawCard,
  signatures: [
    {
      protected: protectedHeaderB64,
      signature: signatureB64
    }
  ]
};

fs.writeFileSync('agent.signed.json', JSON.stringify(signedCard, null, 2));
console.log('✓ Agent Card successfully signed with ES256');

How client agents verify signatures

When a client agent retrieves an Agent Card from an unvetted peer, verification must occur before inspecting capabilities or invoking interfaces:

  1. Extraction: Parse signatures from the document. If missing, flag as unverified or reject based on security policy.
  2. Header Decode: Decode protected using base64url and inspect alg and kid. Reject algorithms that do not meet your organization's minimum security baseline (e.g., discard insecure algorithms like none or deprecated RSA key lengths).
  3. Key Retrieval: Fetch the corresponding public key referenced by kid. Verify that the domain of the key URL matches provider.url declared in the card.
  4. Digest Validation: Reconstruct the unsigned payload, compute the canonical digest, and verify the cryptographic signature against the public key.

Key rotation and security checklist

Follow these operational guidelines when managing Signed Agent Cards in production:

  • Decouple Key Lifecycle from Deployments: Use key IDs (kid) referencing a JWKS URL (/.well-known/jwks.json) rather than hardcoding static keys. This allows seamless zero-downtime key rotation.
  • Enforce HTTPS Everywhere: Every interface listed under supportedInterfaces and every key endpoint must mandate HTTPS with valid TLS certificates.
  • Bind Domain to Provider: Ensure the domain hosting the Agent Card strictly matches the provider.url and the origin of the JWKS endpoint.
  • Pre-flight Audit: Always run your completed card through the validator before publishing to your public root.

Perguntas frequentes

Are JWS signatures mandatory for an Agent Card in A2A v1.0?

They are optional in the baseline schema but strongly recommended for any production agent exposed across public or multi-tenant networks. Without signatures, client agents cannot verify whether the card was altered in transit.

What is "Agent Card Poisoning"?

Agent Card Poisoning occurs when an attacker modifies an agent.json document (e.g., through DNS cache poisoning, CDN misconfigurations, or man-in-the-middle attacks) to redirect interface endpoints to malicious infrastructure or inject fabricated skill descriptions.

Which cryptographic algorithm is recommended for Signed Agent Cards?

ES256 (ECDSA using P-256 and SHA-256) is recommended for its compact signature size and high performance. RS256 (RSA with SHA-256) is also widely supported for legacy enterprise PKI compatibility.

Where should public keys or JWKS endpoints be hosted?

Public keys should be published over HTTPS under the provider's authoritative domain, commonly at /.well-known/jwks.json, or referenced via the kid / x5u headers in the JWS protected header.

How does the Agent Card validator evaluate signatures?

The validator verifies that the signatures array contains well-formed JWS compact or flattened objects with valid protected headers, standard algorithm identifiers, and verifiable key references.

Referências

  1. A2A protocol specification (a2a-protocol.org)a2a-protocol.org
  2. A2A protocol repository (a2aproject/A2A)github.com/a2aproject/A2A
  3. RFC 7515: JSON Web Signature (JWS)datatracker.ietf.org/doc/html/rfc7515

Ferramenta relacionada: Agent Card Validator

Próximo passo

Test your Agent Card integrity before deployment.

The validator inspects your card structure, checks JWS signature formatting, and flags missing public key references or unencrypted interface endpoints.