securesbom-verifier is a Go library for verifying signed SBOMs and signed
digests with public keys. It is designed to be reused by:
- API servers that need SBOM verification business logic
- offline or air-gapped verification workflows
- other Go services that need digest signature verification
- command-line tooling that wants to wrap the same library behavior
- CycloneDX embedded signature verification
- AI-BOM embedded signature verification
- SPDX detached signature verification
- digest signature verification
- JSON canonicalization helpers used during verification
- PEM normalization and key utility helpers
- an optional offline verification CLI
- tests for the lower-level verification building blocks
This module intentionally excludes:
- key generation
- signing
- private-key verification helpers
- key store backends
The only cryptographic operations here are verification using a public key.
make test
make build-cliImport the module root directly:
import securesbomverifier "github.com/shiftleftcyber/securesbom-verifier"For CycloneDX embedded signatures:
verifier := securesbomverifier.NewVerifier()
result, err := verifier.VerifyCycloneDXEmbeddedVersioned(
signedSBOM,
string(publicKeyPEM),
securesbomverifier.VerificationV2,
)When key metadata is available, use the key-based root API:
result, err := verifier.VerifyCycloneDXEmbeddedWithKeyVersioned(
signedSBOM,
securesbomverifier.VerificationKey{
KeyID: "production-key-2026-04",
Algorithm: "ES256",
PublicKey: string(publicKeyPEM),
},
securesbomverifier.VerificationV2,
)For AI-BOM embedded signatures:
verifier := securesbomverifier.NewVerifier()
result, err := verifier.VerifyAIBOMEmbeddedVersioned(
signedAIBOM,
string(publicKeyPEM),
securesbomverifier.VerificationV2,
)AI-BOM verification is supported only with VerificationV2.
When key metadata is available, use the key-based root API:
result, err := verifier.VerifyAIBOMEmbeddedWithKeyVersioned(
signedAIBOM,
securesbomverifier.VerificationKey{
KeyID: "production-ai-bom-key-2026-05",
Algorithm: "ES256",
PublicKey: string(publicKeyPEM),
},
securesbomverifier.VerificationV2,
)For SPDX detached signatures:
verifier := securesbomverifier.NewVerifier()
result, err := verifier.VerifySPDXDetachedVersioned(
spdxSBOM,
signatureB64,
string(publicKeyPEM),
securesbomverifier.VerificationV2,
)
if err != nil {
if errors.Is(err, securesbomverifier.ErrSignatureFail) {
// Signature did not verify.
}
}For digest verification:
import (
"errors"
securesbomverifier "github.com/shiftleftcyber/securesbom-verifier"
)
verifier := securesbomverifier.NewVerifier()
result, err := verifier.VerifyDigest(
securesbomverifier.VerifyDigestInput{
KeyID: "production-key-2026-04",
HashAlgorithm: "sha256",
Digest: digestB64,
Signature: signatureB64,
},
securesbomverifier.VerificationKey{
KeyID: "production-key-2026-04",
Algorithm: "ES256",
PublicKey: string(publicKeyPEM),
},
)
if err != nil {
switch {
case errors.Is(err, securesbomverifier.ErrInvalidDigest),
errors.Is(err, securesbomverifier.ErrInvalidSignature),
errors.Is(err, securesbomverifier.ErrInvalidHashAlgorithm),
errors.Is(err, securesbomverifier.ErrInvalidKeyID),
errors.Is(err, securesbomverifier.ErrInvalidKey):
// Bad request or bad key metadata.
case errors.Is(err, securesbomverifier.ErrVerificationFailed):
// Well-formed input, but the signature did not verify.
default:
// Unexpected operational failure.
}
return err
}
_ = result.VerifiedThe lower-level packages remain available for advanced integrations:
import (
digestsigning "github.com/shiftleftcyber/securesbom-verifier/services/digest"
"github.com/shiftleftcyber/securesbom-verifier/verificationkey"
)VerifyDigest verifies an ASN.1 DER ECDSA signature over a digest that was
computed by the caller.
Request fields:
KeyID: required key identifier. It must matchVerificationKey.KeyIDwhen the key metadata includes one.HashAlgorithm: required digest algorithm. Currently onlysha256is supported.Digest: required standard-base64 encoding of the raw digest bytes. Forsha256, this must decode to exactly 32 bytes.Signature: required standard-base64 encoding of the ASN.1 DER ECDSA signature bytes.
Key metadata uses the same root type for digest and SBOM integrations:
type VerificationKey struct {
KeyID string
Algorithm string
PublicKey string
}Algorithm is the signing key algorithm. Digest verification accepts ECDSA
algorithm labels ES256, ES384, and ES512; current digest hashing is
sha256.
The root package exports stable errors for mapping verification results to API
responses. Use errors.Is because errors may include additional context.
- Bad input or bad metadata:
ErrInvalidSBOM,ErrMissingSignature,ErrInvalidHashAlgorithm,ErrInvalidDigest,ErrInvalidKeyID,ErrInvalidSignature,ErrInvalidKey,ErrKeyNotFound. - Cryptographic verification failure:
ErrSignatureFailfor SBOM signatures andErrVerificationFailedfor digest signatures.
For HTTP APIs, bad input generally maps to 400 Bad Request, missing keys to
404 Not Found, and cryptographic verification failures to 422 Unprocessable Entity or a domain-specific verification-failed response.
Use VerificationV2 for new integrations.
VerificationV1preserves the original canonicalization behavior used by the source service. It is useful when verifying signatures produced by older signing flows.VerificationV2uses the newer canonicalization behavior and is the default recommendation for new signatures, including AI-BOM embedded signatures.
AI-BOM embedded verification is only available with VerificationV2; the V1
verification behavior remains limited to the existing CycloneDX and SPDX flows.
Migration note: keep verifying historical artifacts with the version used when
they were signed. Sign new CycloneDX embedded, AI-BOM embedded, and SPDX
detached artifacts with VerificationV2, then update callers to pass
securesbomverifier.VerificationV2.
_, err = verifier.VerifyCycloneDXEmbeddedVersioned(signedSBOM, publicKeyPEM, securesbomverifier.VerificationV1)
_, err = verifier.VerifyCycloneDXEmbeddedVersioned(signedSBOM, publicKeyPEM, securesbomverifier.VerificationV2)
_, err = verifier.VerifyAIBOMEmbeddedVersioned(signedAIBOM, publicKeyPEM, securesbomverifier.VerificationV2)
_, err = verifier.VerifySPDXDetachedVersioned(spdxSBOM, signatureB64, publicKeyPEM, securesbomverifier.VerificationV1)
_, err = verifier.VerifySPDXDetachedVersioned(spdxSBOM, signatureB64, publicKeyPEM, securesbomverifier.VerificationV2)See examples/production-api/main.go for an HTTP-style integration that fetches public key metadata, verifies CycloneDX, SPDX detached, and digest requests, handles stable errors, and maps them to responses.
The testsupport package provides reusable fixtures for downstream service
tests:
fixtures := testsupport.NewContractFixtures(t)The fixture set includes valid and invalid CycloneDX embedded signatures, AI-BOM embedded signatures, SPDX detached signatures, and digest signatures, along with public keys and expected verification outcomes.
This project currently relies on GOEXPERIMENT=jsonv2, matching the behavior
already used by the source repository. The included Makefile sets that for the
common build and test flows.
Build the offline verification CLI as a container:
make docker-buildRun it locally:
docker run --rm \
-v "$PWD:/work" \
secure-sbom-verification-cli:dev \
--sbom /work/path/to/signed-sbom.json \
--pubkey /work/path/to/public.pemThe offline CLI is an optional command. Library consumers do not need to import or build it. It lives at:
cmd/sbom-offline-verification
Example:
GOEXPERIMENT=jsonv2 go run ./cmd/sbom-offline-verification \
--sbom ./path/to/signed-sbom.json \
--pubkey ./path/to/public.pemThe same command verifies signed CycloneDX or signed AI-BOM files. The verifier
detects AI-BOM files as AI-SBOM and verifies the embedded
metadata.sbomAuthorSignature value with the supplied public key.
For detached SPDX verification:
GOEXPERIMENT=jsonv2 go run ./cmd/sbom-offline-verification \
--sbom ./path/to/sample.spdx.json \
--signature BASE64_SIGNATURE \
--pubkey ./path/to/public.pem \
--verification-version v2For digest verification:
GOEXPERIMENT=jsonv2 go run ./cmd/sbom-offline-verification \
--digest BASE64_DIGEST \
--signature BASE64_SIGNATURE \
--pubkey ./path/to/public.pem \
--hash-algorithm sha256 \
--signature-algorithm ES256Tagged releases matching vX.X.X are built with Goreleaser using
.goreleaser.yml. The release workflow publishes multi-platform
CLI archives, a checksum file, and a container image to GitHub-hosted release
surfaces. The container image is published to GitHub Container Registry as
ghcr.io/<owner>/<repo> for linux/amd64 and linux/arm64, with tags for the
full version, vMAJOR.MINOR, vMAJOR, and latest.
See examples/library/main.go for a minimal embedding
example for CycloneDX verification, examples/spdx-detached/main.go
for SPDX detached verification, examples/digest/main.go
for digest verification, and MIGRATION.md for a suggested next-step
extraction plan from sbom-signing-api.
The example programs expect you to provide signed content, signatures, and/or a public key path at runtime depending on the verification mode.