Skip to content
All guides
Migration engineering

Implementing crypto-agility: patterns for swappable cryptography

8 min
TL;DR

Read this first

Crypto-agility is an engineering property you build from a handful of concrete patterns: negotiate the algorithm at runtime, route every operation through one provider, choose the algorithm from configuration, store a versioned algorithm identifier next to the data, size every field for the largest primitive, and automate rotation with dual-stack support. Apply them and the post-quantum swap — and the one after it — becomes a configuration change rather than a programme of work.

This is the implementation companion to crypto-agility (the concept): that guide argues why agility, not the algorithm swap, is the real deliverable of a post-quantum migration. This one is the how. None of the patterns below are new — they are the mechanisms mature protocols already use to survive algorithm change — but they have to be applied deliberately, because rigidity is what you get by default. Each pattern moves one decision out of hardcoded code and into a place you can change cheaply later.

Negotiate the algorithm at runtime

The most durable pattern is to let the endpoints agree on the algorithm when they connect, rather than fixing it in the wire format. TLS is the model: cipher suites and `supported_groups` let a client and server pick a mutually supported primitive — the same mechanism that makes the TLS hybrid group deployable without a flag day. Design new protocols with an explicit negotiation step and a version field from day one, even when only one algorithm exists, so the second one is an addition rather than a breaking change.

Put every operation behind one provider

Application code should never name a concrete algorithm. Route all cryptographic operations through a single interface — a provider, in the JCA/JCE or PKCS#11 sense — that takes an abstract request ("establish a shared secret", "sign this") and resolves the actual primitive internally. The algorithm becomes an implementation detail behind a stable boundary, so a swap is one change in one place instead of an edit at every call site. This is also what makes negotiation and configuration enforceable: there is exactly one code path to govern.

Select from configuration and policy

With a provider in place, the choice of primitive belongs in configuration or a policy engine, never in identifiers scattered through the source. Centralising it means a change — a new parameter set, a deprecation, an emergency response to a broken algorithm — is a single edit that propagates everywhere, ideally without a redeploy. Keep the policy versioned alongside your migration roadmap so the running configuration and the plan for what changes next stay in step.

Store self-describing, versioned data

Anything cryptographic that outlives a single session must carry its own algorithm identifier, so you can rotate primitives and still read what you wrote before. This is exactly what X.509 `AlgorithmIdentifier`, the JOSE/COSE `alg` header, and message-format version fields already do — mirror them in your own formats. Two rules make it work in practice:

  • **Tag the data, don't infer it.** Persist an explicit algorithm-and-parameters identifier next to every key, signature, and ciphertext; never assume the algorithm from context or deployment date.
  • **Size for the largest primitive.** Never hardcode key, signature, or ciphertext lengths — post-quantum outputs dwarf classical ones, and SLH-DSA's large signatures can run to tens of kilobytes. Use length-prefixed or variable-length fields in schemas, columns, and buffers.
Pitfall

Agility is measured by the next change, not this one

The common failure is treating post-quantum as a one-time swap — ripping out RSA and hardcoding ML-KEM into exactly the same fixed fields and inline calls. You spend a multi-year effort and rebuild the same rigidity, so the next transition costs just as much. Judge every pattern here by one test: how cheap does it make the change after this one? If the answer is "another migration programme," you have swapped the algorithm but not built agility.

Rotate continuously and run dual-stack

Agility only holds if you exercise it. Automate key and certificate rotation so changing a primitive is a routine, tested operation rather than a rare manual event — a rotation path that is never used is a rotation path that will fail when you need it. And support running old and new algorithms at once: hybrid key exchange, dual signatures, or parallel certificate chains let you cut over gradually and roll back safely, instead of forcing every endpoint to upgrade in lockstep.

How quantakrypto helps

These patterns are cheap to state and hard to retrofit, because the rigidity is spread across code, schemas, and protocols that already ship. We start with an audit that produces a machine-readable inventory — a CBOM — of where cryptography is hardcoded, pinned, or size-locked, so you can find and track what still needs to change. We turn it into a migration roadmap that fixes the boundaries — negotiation, provider layers, config-driven selection, versioned formats — so the post-quantum swap and every swap after it is a configuration change. And we run training so your engineers reach for these patterns by default.

Frequently asked questions

How is this different from the crypto-agility concept guide?

The concept guide explains what crypto-agility is and argues that building it — not the one-time algorithm swap — is the real goal of a post-quantum migration. This guide is the implementation companion: the concrete engineering patterns (runtime negotiation, a provider abstraction, config-driven selection, self-describing versioned formats, size-agnostic storage, automated rotation and dual-stack) that make an algorithm a replaceable component in a real system.

What is the single most important pattern to start with?

Route every cryptographic operation through one provider interface so application code never names a concrete algorithm. It is the foundation the other patterns build on: with a single code path, you can add runtime negotiation, move the choice into configuration, and enforce policy centrally. Without it, the algorithm is referenced in dozens of call sites and every other pattern has to be applied dozens of times.

Why do fixed-size fields matter so much for post-quantum work?

Post-quantum keys, ciphertexts, and especially signatures are far larger than their classical equivalents — SLH-DSA signatures can reach tens of kilobytes against a few hundred bytes for ECDSA. Any database column, wire field, or buffer sized to a classical primitive breaks the moment a post-quantum one arrives. Using length-prefixed or variable-length fields, and never hardcoding lengths, keeps storage and protocols agnostic to which algorithm produced the bytes.

How do self-describing formats enable algorithm rotation?

If every key, signature, and ciphertext carries an explicit algorithm-and-parameters identifier — as X.509 AlgorithmIdentifier and the JOSE/COSE alg header already do — you can introduce a new primitive while still reading everything produced under the old one. Rotation becomes additive rather than a breaking change, because the reader dispatches on the stored identifier instead of assuming a single fixed algorithm across all data.

Take the next step

Related reading

References

Get started

Turn quantum risk into a credential.

Book a discovery call and get an indicative scope and pricing for your organisation.