The first post in this series argued that the DST roadmap's first milestone is the hard one. Governance, an owner and a cryptographic inventory by December 2027 sounds like paperwork, but every later milestone depends on that inventory being right.
The second post showed how to rank migration by data shelf life, and ended on an uncomfortable assumption: that your map of which flows carry which data is complete. It rarely is.
This post is about why. Almost every cryptographic inventory starts life as a spreadsheet, usually produced by an audit, and a spreadsheet fails in three specific ways. It can't cover where cryptography actually lives. It can't hold the context that turns an algorithm into a risk. And it can't stay current in an estate that ships every week.
The spreadsheet everyone starts with
It usually looks like this:
| Asset | Algorithm | Key size | Location | Owner | Expiry |
|---|---|---|---|---|---|
| api.example.in | RSA | 2048 | ALB, ap-south-1 | Platform | 2027-03-02 |
| jwt-signing-prod | RSA (RS256) | 2048 | KMS | Identity | — |
| sftp-colender | RSA | 3072 | Batch server 2 | Ops | — |
Nothing in it is wrong. It is a reasonable record of what someone found on the day they looked. The problem is everything it can't say: what it didn't find, what each row is connected to, and what changed the day after.
Failure 1: Coverage. Cryptography lives in five places, and each needs a different method to find it
Cryptography isn't a product you deploy once. It is a capability that every layer of a modern stack uses independently, usually with its own defaults. Each layer has a discovery method that works for that layer and is blind to the rest.
Application code. Explicit calls are the easy part: Cipher.getInstance("RSA/ECB/PKCS1Padding"), a KeyPairGenerator for EC keys, jwt.sign(payload, key, { algorithm: "RS256" }). The hard part is the crypto you never wrote. A library picks a default algorithm when the caller doesn't specify one. The algorithm name is read from a config file or environment variable. A helper class wraps it all three calls deep. Static analysis with crypto-aware rules finds most of this. A text search finds the explicit names and misses the rest.
Dependencies. Your SBOM says you ship a particular version of an SSH library, a PGP library or an HTTP client. It does not say which algorithms that library actually negotiates on your behalf, or whether your code ever reaches its crypto paths. Go binaries statically link their own TLS stack. Java services bundle their own crypto providers. The SFTP job from post 2 negotiates its key exchange inside a dependency nobody on the current team chose. Composition analysis lists packages. It doesn't list cryptographic behaviour.
Cloud configuration. Here the crypto is a setting, not code: load-balancer TLS security policies, KMS key specs, API gateway and CDN TLS settings, VPN tunnel IKE parameters, managed-certificate key types, database TLS. A good example of why settings drift: AWS added post-quantum TLS policies with hybrid ML-KEM key exchange to its Application and Network Load Balancers in November 2025, but they are opt-in. Existing listeners stay classical until someone explicitly changes the policy. Posture tools see the setting. They generally don't know that a policy name encodes a key-exchange decision.
Identity. Every token is a signature. Your identity provider signs JWTs, typically with RS256 or ES256, and publishes the verification keys at a JWKS endpoint. SAML assertions are signed with certificates that are often self-signed and valid for a decade. Cloud service-account keys, deploy SSH keys and service-mesh mTLS certificates all add more. IAM reviews look at who can do what. They don't look at the algorithm that proves who's who.
Network endpoints. What a server actually negotiates is the ground truth: which TLS versions, which key-exchange groups, which certificate algorithms, on which hostnames and ports. That includes the partner-only port, the SFTP host key and the VPN concentrator nobody has logged into for a year. External scanning sees this layer clearly, but only what it can reach, and it can't tell you which code or which key sits behind the handshake.
Five methods, five layers, one diagonal. Run them all separately and you get five partial inventories, with the gaps exactly where the layers meet.
An audit that runs all five methods still produces five lists in five formats. Somebody merges them into the spreadsheet by hand, and the merge is where most of the errors come from.
There is one more thing the row can't capture. Post 2 explained that key exchange and signatures run on different migration clocks: key exchange is harvestable now, while signatures matter when forgery becomes possible. "RSA-2048" in a row tells you neither which one it is nor which clock it is on.
Failure 2: Context. A row can't hold a relationship
Take the second row above: jwt-signing-prod, RSA-2048, in KMS, owned by the identity team. It's accurate. Now try to answer the questions a migration plan actually needs:
- Which services call this key to sign tokens?
- Which gateways and APIs verify those tokens?
- What data do those APIs serve? (Post 2's shelf-life tiers.)
- Which IAM roles can use the key, and could anything outside its intended path reach it?
- Are any of those APIs reachable from the internet?
None of these answers is in the row. Each needs a join across layers. Code tells you which service calls kms:Sign with this key. IAM tells you which roles are allowed to. Cloud configuration tells you which listener fronts the service. The endpoint layer tells you whether that listener faces the internet. Data classification tells you what flows through it.
Two keys, identical rows. One sits on a chain that ends at internet-facing identity data. The other signs nothing anyone uses. The risk lives in the connections, and a spreadsheet stores only the endpoints.
This is the real reason cryptographic inventory is hard. The individual facts are easy to collect. The relationships between them are what make an inventory useful, and they span exactly the layer boundaries where separate tools stop. Post 2's flow map is a set of these relationships, and so is the reachability ranking in the next post. Neither can be built from a list of rows.
The CBOM format the DST roadmap points toward reflects this. CycloneDX, the most widely used CBOM format, records cryptographic assets as components with references to what depends on them. Flatten a CBOM into a spreadsheet and the first thing you lose is the dependency structure.
Failure 3: Currency. The audit is out of date before it's signed off
Suppose an audit gets coverage and context right. It still describes one moment. Here is what typically changes in the six weeks between fieldwork and sign-off:
- Deploys. Each merge can add a dependency, bump a library that changes its default ciphers, or introduce a new call to a crypto API.
- Infrastructure templates. A team creates a new load-balancer listener from a Terraform module written in 2024. The policy defaults to a classical one, and a flow migrated to hybrid key exchange last quarter now has a classical sibling.
- Identity changes. The IdP rotates its signing key. A new SaaS tool is connected over SAML with a fresh ten-year certificate. A developer creates a service-account key for a one-off script.
- Certificate renewal. Under the CA/Browser Forum's ballot SC-081, publicly trusted TLS certificates issued since 15 March 2026 can be valid for at most 200 days. That falls to 100 days in March 2027 and 47 days in March 2029, the same year as the CII full-adoption deadline. Every certificate row in your spreadsheet will soon be replaced several times a year, often with a different key.
- Partners. Your co-lender upgrades its SFTP server, or your bureau changes its TLS configuration. Their change alters your flow, and nobody tells you.
By the time the report is signed, it is a document about the past. By 2029, public certificate rows will turn over roughly every six weeks.
The most dangerous kind of drift is regression. Migration work can be silently undone: a hybrid listener replaced by a classical one, a PQ-capable library pinned back to an older version because of an unrelated bug. A spreadsheet will keep showing the flow as migrated. An inventory has to check not just what exists but whether what you fixed is still fixed.
Time also matters for HNDL in a way it doesn't for most vulnerabilities. Under the assume-breach planning the DST report requires, you will eventually want to answer a question like: between which dates did this flow carry Permanent-tier data over classical key exchange? That is your harvest exposure window. Only an inventory that keeps its history can answer it.
What a cryptographic inventory actually needs to be
Put the three failures together and the requirements follow:
- Multi-source. Collected from code, dependencies, cloud configuration, identity and live endpoints, each by the method that suits it, and merged automatically rather than by hand.
- Connected. Assets linked across layers by how they're used, what trusts them, what data passes through them and what can reach them. Then you can ask one question across all of it, such as: show every classical key exchange protecting Permanent-tier data on an internet-facing path.
- Temporal. Versioned over time, so you can see what changed since last week, which deploy introduced a regression, and how long a flow was exposed.
- Regenerated, not maintained. Rebuilt continuously from the sources themselves. People add what machines can't infer, such as ownership and data tier, rather than transcribing what machines can.
- Exportable. Able to produce a CBOM for the procurement gate described in post 1, and a spreadsheet view for anyone who wants one.
The spreadsheet isn't wrong as a view. It is wrong as the system of record.
Measure your own gap in two weeks
You don't need new tooling to find out how big this problem is in your estate. Take the "pick two services" exercise from post 1 and make it a measurement:
Day 1: build it by hand. Have the owning team list every algorithm, key and certificate involved in two production services, the way an audit would.
Days 2–3: build it from the sources. For the same two services:
# Code: explicit crypto usage (a starting point; static analysis does better)
rg -n "RSA|ECDSA|ECDH|RS256|ES256|secp256|PKCS1|KeyPairGenerator|Cipher\.getInstance" src/
# Cloud: what TLS policy each load-balancer listener actually uses
aws elbv2 describe-listeners --load-balancer-arn <arn> \
--query 'Listeners[].{port:Port,policy:SslPolicy}'
# Cloud: key specs behind the KMS keys the services use
aws kms describe-key --key-id <key-id> --query 'KeyMetadata.KeySpec'
# Identity: what your IdP signs tokens with
curl -s https://<idp>/.well-known/jwks.json | jq '.keys[] | {kid, kty, alg}'
# Endpoint: does it negotiate hybrid ML-KEM? (needs OpenSSL 3.5+)
openssl s_client -connect <host>:443 -groups X25519MLKEM768 </dev/null
Add your SCA tool's output for the crypto-related packages in each service's dependency tree.
Day 4: compare. Count what the hand-built list missed. That is your coverage gap.
Day 14: rebuild from the sources and diff. Everything that changed in two weeks, across two services, is your drift rate. Multiply both numbers by your service count and you have an honest estimate of what Milestone 1 involves, and of how long a one-off audit stays accurate.
Most teams who run this find that the manual list is incomplete on day 4 and stale by day 14. That isn't a failure of diligence. It is what happens when you use a static document to describe something that changes every week.
Where this series goes next
A connected, current inventory tells you what exists and how it links together. It does not yet tell you what to fix first. The next post covers reachability-weighted migration: why an internal RSA key behind three layers of isolation is a 2031 problem while a public TLS endpoint on a customer-data API is a 2027 one, and how to score the difference.
Xhield builds continuous security intelligence across code, dependencies, cloud, identity and the external attack surface, so teams know what changed before attackers do. If you've run the two-service test and want to compare notes, we'd like to hear what you found: contact@xhield.tech.