This is Part 2 of a three-part series on AI agent security. Part 1 covered the threat landscape. This post covers the cryptographic layer. Part 3 covers runtime monitoring.
Cryptographic controls address the attack surface that neither application code nor network security can reach: proving that the agent executing an action is the agent that was authorised to do so, and that the action falls within that agent's signed authority limits.
Why Cryptography Is the Right Layer
Prompt injection attacks work by subverting the LLM's judgement — the model is tricked into deciding an unauthorised action is appropriate. No amount of prompt engineering or LLM training fully solves this, because the attack exploits the model's core capability (following instructions).
Cryptographic scope enforcement works at a different layer: after the LLM decides what to do, and before the action executes. The question is not "does the LLM think this is appropriate?" but "does the agent's certificate permit this?" The certificate was signed by a CA whose private key is in KMS — no prompt injection can modify it.
X.509 Certificate Architecture
Every Kakunin agent instance holds an X.509 certificate issued by a tenant-specific intermediate CA, which is in turn signed by Kakunin's root CA. The root CA private key lives in AWS KMS (RSA_4096, eu-west-1) and never leaves the HSM.
The certificate contains:
— Subject: the agent's unique ID and name
— Validity window: issue date and expiry (30–365 days)
— Public key: RSA_2048, corresponding private key in agent's KMS slot
— Custom extensions: scope policy (max transaction size, allowed actions, permitted counterparties, geographic constraints, operating hours)
— Issuer signature: CA signature over all of the above
Changing the scope requires reissuance — obtaining a new certificate from the CA. The reissuance event is logged and auditable. There is no way to modify the scope in place.
Key Custody: Private Key Never Leaves KMS
The agent's private key is generated by AWS KMS and never leaves the HSM. The application code receives a KMS key ARN — a reference to the key, not the key itself. Signing happens via the KMS API:
1. Agent assembles action payload as JSON
2. Calls kms.sign() with the key ARN and payload hash
3. KMS returns the signature (RSA-SHA256)
4. Agent submits the payload + signature + certificate fingerprint to the target system
An attacker who escapes the container gets the KMS key ARN. Without IAM credentials bound to the pod's service account, the ARN is useless — they cannot call the KMS API to sign anything.
Action Signing
Every significant agent action should be signed before submission. The signature proves:
— This specific agent (not just any holder of the API key) authorised this specific action
— The action payload has not been modified in transit
— The signing occurred after the nonce was generated (prevents replay)
Downstream verification:
1. Retrieve the agent's certificate from Kakunin using the fingerprint
2. Verify the certificate is valid and not revoked (OCSP or Kakunin API)
3. Verify the signature over the payload using the certificate's public key
4. Check the nonce against a Redis set (reject if seen before, with 1-hour TTL)
5. Enforce the scope: compare action parameters against certificate scope extensions
If any check fails, the action is rejected before any downstream processing occurs.
Scope Enforcement at the Tool Layer
The ToolGuard wraps every tool the agent can call. Before the tool function executes, the guard:
1. Calls Kakunin to verify the certificate is still valid
2. Checks the action type against the certificate's allowed_actions extension
3. Checks numeric parameters (amounts, sizes) against scope limits
4. Checks string parameters (counterparties, instruments, regions) against whitelist extensions
If the check passes, the tool executes. If not, the guard returns a tool error to the LLM. The LLM sees a tool failure — it cannot see the scope policy itself, preventing it from crafting a response that circumvents the check.
Sub-Agent Scope Delegation
When an orchestrator spawns a sub-agent, the sub-agent's certificate scope must be a provable subset of the orchestrator's scope. Kakunin enforces this at the CA level:
The orchestrator presents its certificate to Kakunin when requesting a sub-agent certificate. Kakunin's CA reads the orchestrator's scope extensions and caps the sub-agent's scope at the intersection of the requested scope and the orchestrator's scope.
There is no way for the orchestrator (or prompt injection within it) to issue a sub-agent certificate with broader scope than the orchestrator's own.
Certificate Revocation
Revocation is near-instant via two mechanisms:
1. Kakunin API: DELETE /api/v1/certificates/{id} marks the certificate revoked in the database. All subsequent scope checks for that certificate return "revoked".
2. OCSP endpoint: Kakunin publishes an OCSP responder. Downstream systems that verify certificates out-of-band can check revocation status without calling the Kakunin API.
The revocation event is written to the WORM audit log with timestamp, actor, and reason. Post-revocation, any system holding a cached verification result should treat the certificate as revoked within the OCSP cache TTL (default: 60 seconds).
Next in This Series
Part 3: Runtime Monitoring for AI Agents — establishing behavioral baselines, scoring anomalies, and building automated incident response that satisfies MiCA Article 72 and EU AI Act Article 14.
