A CycloneDX CBOM (cryptographic bill of materials) is a CycloneDX document whose components describe cryptographic assets: algorithms, protocols, certificates and key material. Producing one in continuous integration is mostly plumbing: run detectors over source, dependencies, container images and test endpoints, merge the results into a single CycloneDX 1.6 document, validate it against the schema, then store it and compare it with the previous build. The difficult part is being honest about what a build pipeline can actually see. For the concept itself, start with our introduction to the CBOM.
Where cryptographic assets live in the CycloneDX 1.6 model
CycloneDX 1.6 introduced the component type cryptographic-asset. Each such component carries a cryptoProperties object, and its assetType field decides which group of properties applies. Four asset types exist.
| assetType | Property group | Typical content |
|---|---|---|
algorithm | algorithmProperties | Primitive (kem, signature, pke, hash, mac, block cipher), parameter set identifier, mode, padding, curve, cryptographic functions, NIST quantum security level |
certificate | certificateProperties | Subject name, issuer name, validity dates, certificate format, references to the signature algorithm and to the subject public key |
protocol | protocolProperties | Protocol type (tls, ssh, ipsec and others), version, cipher suites with their identifiers, references to the algorithms used |
related-crypto-material | relatedCryptoMaterialProperties | Keys, secrets, tokens, digests, initialization vectors, with size, state and lifecycle dates |
Two further fields do the real inventory work. The oid field pins an algorithm to its object identifier when you know it. And in the dependencies array, the provides field states that a software component implements a cryptographic asset. That is how a CBOM answers the question an auditor actually asks: which application uses this algorithm, and where?
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:4e1e1f2a-0000-0000-0000-000000000000",
"version": 1,
"metadata": {
"timestamp": "2026-10-19T06:12:00Z",
"component": {
"type": "application",
"bom-ref": "payments-api@1.42.0",
"name": "payments-api",
"version": "1.42.0"
}
},
"components": [
{
"type": "cryptographic-asset",
"bom-ref": "crypto/algorithm/ml-kem-768",
"name": "ML-KEM-768",
"cryptoProperties": {
"assetType": "algorithm",
"algorithmProperties": {
"primitive": "kem",
"parameterSetIdentifier": "768",
"cryptoFunctions": ["keygen", "encapsulate", "decapsulate"],
"nistQuantumSecurityLevel": 3
}
}
},
{
"type": "cryptographic-asset",
"bom-ref": "crypto/protocol/tls-1.3",
"name": "TLS 1.3",
"cryptoProperties": {
"assetType": "protocol",
"protocolProperties": {
"type": "tls",
"version": "1.3",
"cipherSuites": [
{ "name": "TLS_AES_256_GCM_SHA384", "identifiers": ["0x13", "0x02"] }
],
"cryptoRefArray": ["crypto/algorithm/ml-kem-768"]
}
}
}
],
"dependencies": [
{ "ref": "payments-api@1.42.0", "provides": ["crypto/protocol/tls-1.3"] }
]
}
ML-KEM is the key encapsulation mechanism standardized in NIST FIPS 203; its 768 parameter set sits at NIST quantum security category 3. Protocol components are where the hybrid key exchange groups appear, for example X25519MLKEM768, whose codepoint 0x11EC is listed in the IANA TLS parameters registry.
Static detection versus observed behavior
A CI job sees a repository, a lockfile and an image. It does not see a negotiated handshake. Treating the two as the same thing is the most common way a CBOM becomes misleading.
| Asset | What a static job can establish | What only observation establishes |
|---|---|---|
| Algorithm calls in your own code | The call sites, with file and line, plus parameter sets written as constants | Whether that code path runs in production |
| Crypto libraries | Library and version from the lockfile or the image, which bounds what is possible | Which provider or FIPS mode is loaded at run time |
| TLS key exchange and cipher suites | Only the directives in configuration files that live in the repository | The group and suite actually negotiated, including upstream defaults |
| Certificates | Certificate files, truststores and keystores shipped in the image, with validity and signature algorithm | The chain a live endpoint serves, and certificates issued after the build |
| Keys and lifetimes | Declared sizes, rotation parameters in configuration | Key state and dates held in a key manager or HSM |
| Third party endpoints you call | Hostnames at best | What those endpoints offer, which you do not control |
Record the difference in the document. Keep statically derived facts in the component itself, with the file path in the component evidence occurrences, and keep measured facts in protocolProperties and certificateProperties produced by a probing job. Always populate metadata.tools so a reader knows which detector made which claim.
A pipeline outline
1. Declare the scope
Before any tool runs, decide what one CBOM covers: one deployable component, one version, one environment. A single file describing twelve services ages badly and cannot be diffed usefully.
2. Collect in parallel
- Source and dependency scan: a static analyzer over the repository and the lockfiles, emitting algorithm assets with evidence locations.
- Image or filesystem scan: inspect the built artifact for crypto libraries, certificate bundles, keystores and configuration files.
- Observed endpoint probe: against staging only, confirm what is really negotiated.
# TLS: does the staging endpoint negotiate a hybrid group?
# requires OpenSSL 3.5 or later for the ML-KEM groups
openssl s_client -connect staging.example.com:443 \
-groups X25519MLKEM768 </dev/null | grep "Negotiated TLS1.3 group"
# SSH: which key agreement algorithms does this client build support?
ssh -Q kex
# SSH: confirm the server accepts the post-quantum hybrid key agreement
ssh -o KexAlgorithms=mlkem768x25519-sha256 -o BatchMode=yes \
git@staging.example.com true
The algorithm mlkem768x25519-sha256 was added in OpenSSH 9.9 and became the default key agreement in OpenSSH 10.0, per the OpenSSH release notes. Probe staging, never production, and keep a classical fallback such as X25519 after the hybrid group in every server configuration you touch.
3. Merge, stamp and validate
Merge the partial documents into one, then fail the job if the result is not schema valid. The CycloneDX CLI does both.
cyclonedx merge \
--input-files static.cbom.json image.cbom.json observed.cbom.json \
--output-file cbom.json
cyclonedx validate --input-file cbom.json --fail-on-errors
Stamp metadata.timestamp, the component version and the commit SHA. A CBOM without provenance is an opinion.
Storing and diffing the CBOM
Publish the file as a build artifact for traceability, and keep one normalized copy per component in a dedicated repository or BOM server so that history is queryable. Normalization matters more than storage: sort the components, use stable bom-ref values that do not contain the build number, and exclude the timestamp from the diff. Otherwise every build looks like a change.
# extract a stable, sorted view of the cryptographic assets
jq -S '[.components[]
| select(.type == "cryptographic-asset")
| {name, assetType: .cryptoProperties.assetType,
primitive: .cryptoProperties.algorithmProperties.primitive}]
| sort_by(.name)' cbom.json > current.view.json
diff -u previous.view.json current.view.json
Then decide what a difference means. Useful gates, in increasing severity: a new asset appears that no previous build declared; a quantum-vulnerable public key algorithm appears in a component that previously had none; an obsolete pre-standard group such as a Kyber draft TLS group appears anywhere; a hybrid group disappears from a service that had it. Start by warning, not failing, until the detector noise is understood. This diff is the measurable part of crypto agility: it shows whether your cryptographic surface changes on purpose or by accident.
Limits worth writing down
- Coverage is uneven by language and ecosystem. A detector that reads one language well may be blind in another. Record what was not scanned.
- Static findings are possibilities, not usage. A library that supports RSA does not mean RSA is used. Keep a confidence note in the component properties rather than implying certainty.
- Configuration resolved at run time is invisible. Environment variables, secret managers, service meshes and reverse proxies can override everything the repository says.
- Object identifiers are incomplete. For recent algorithms the
oidfield may simply be unavailable; leave it empty instead of guessing. - Merging duplicates. Two detectors naming the same algorithm differently produce two components unless your
bom-refscheme is deterministic. - No severity model is built in. CycloneDX describes assets; the judgment about what is quantum-vulnerable comes from you, or from a reference such as our algorithm pages.
What a CI generated CBOM evidences
Several texts require an inventory of cryptography in use. PCI DSS v4.0.1 requirement 12.3.3 asks that cipher suites and protocols in use be documented and reviewed at least every 12 months, including where they are used and a documented strategy for anticipated changes in cryptographic vulnerabilities, and it has been mandatory since 31 March 2025 (PCI SSC document library). DORA, Commission Delegated Regulation (EU) 2024/1774, requires in Article 6 an encryption policy with provisions for updating or changing cryptographic technology as cryptanalysis develops, and in Article 7 key management across the lifecycle plus a register of certificates for ICT assets supporting critical or important functions (EUR-Lex). For US federal agencies, OMB M-26-15 calls for an automated, continuously updated cryptographic inventory (M-26-15). The EU Coordinated Implementation Roadmap v1.1 lists a cryptographic inventory among first steps by the end of 2026, as coordinated guidance for Member States rather than a regulation binding companies.
Our interpretation, clearly labelled as such: none of these texts names CycloneDX, and a CBOM is one practical way to produce and keep such an inventory, not a required format. Build the file because it makes the next migration decision cheaper, then map it to whichever obligation applies. This is information, not legal advice.
Next step: compare the CBOM with reality
A CBOM generated in CI tells you what your build contains. It cannot tell you what your servers negotiate. Close that gap by testing the endpoints themselves: scan your domain with PQC Status to see the hybrid ML-KEM groups, TLS versions and certificate chain a client is actually offered, then feed those observed values back into the protocolProperties and certificateProperties of your next CBOM so the document reflects both code and behavior.