Security Error Guide: cosign 'no matching signatures' Verification Failed
Fix cosign 'no matching signatures' errors: diagnose wrong keys, missing signatures, identity/issuer mismatches, and Rekor/TUF failures, and verify container image signatures correctly.
- #security
- #hardening
- #troubleshooting
- #errors
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
Cosign (part of Sigstore) verifies that a container image or artifact was signed by a trusted identity before it is deployed. When cosign verify cannot find a signature that satisfies the trust criteria you supplied — the right key, or the right OIDC identity and issuer for keyless — it fails closed and refuses the image. This is the supply-chain control working: an unsigned, wrongly signed, or tampered image should not verify. But the same error appears when your verification command is simply pointed at the wrong key or identity.
The literal failure from cosign verify is:
Error: no matching signatures:
main.go:74: error during command execution: no matching signatures:
The keyless variant is more specific about why the identity did not match:
Error: no matching signatures: none of the expected identities matched what was in the certificate,
got subjects [https://github.com/acme/app/.github/workflows/release.yml@refs/tags/v1.2.3] with issuer https://token.actions.githubusercontent.com
And when no signature exists at all for the image:
Error: no signatures found for image
The difference matters: “no matching signatures” with identity output means a signature exists but did not satisfy your --certificate-identity/--certificate-oidc-issuer; “no signatures found” means nothing was ever attached.
Symptoms
- A deploy or admission controller blocks an image with
no matching signatures. cosign verifyworks for one image/tag but fails for another that you expected to be signed.- Verification fails only after switching from a signed tag to a
:latestor rebuilt digest. - Keyless verification fails with an identity/issuer mismatch even though the image is signed.
cosign verify --key cosign.pub registry.example.com/app:1.2.3
Error: no matching signatures:
cosign tree registry.example.com/app:1.2.3
📦 Supply Chain Security Related artifacts for an image: registry.example.com/app:1.2.3
No Signatures were found
Common Root Causes
1. Verifying with the wrong public key
The image was signed with a different private key than the --key you are verifying against (e.g., a rotated key, or the wrong environment’s key).
cosign verify --key cosign.pub registry.example.com/app:1.2.3
sha256sum cosign.pub
Error: no matching signatures:
The signature is valid — just not for this public key.
2. Keyless identity or issuer mismatch
For keyless (Fulcio) signatures, --certificate-identity (or --certificate-identity-regexp) and --certificate-oidc-issuer must match the signer recorded in the certificate. A wrong workflow path, branch, or issuer URL fails the match.
cosign verify \
--certificate-identity "https://github.com/acme/app/.github/workflows/release.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
registry.example.com/app:1.2.3
Error: ... got subjects [...@refs/tags/v1.2.3] with issuer ...
The certificate is for the v1.2.3 tag ref, not main.
3. The image simply is not signed
The tag was rebuilt/repushed without re-signing, or signing was skipped in CI, so no signature is attached to that digest.
cosign tree registry.example.com/app:1.2.3
No Signatures were found
4. Tag moved to a new, unsigned digest
Cosign signs a digest, not a tag. If :1.2.3 (or :latest) was re-pushed to a new digest, the old signature no longer applies.
crane digest registry.example.com/app:1.2.3
cosign verify --key cosign.pub registry.example.com/app@sha256:<olddigest>
5. Missing transparency-log or TUF trust
Keyless verification checks Rekor and fetches Sigstore trust roots via TUF. An air-gapped or offline host without the trust bundle, or --insecure-ignore-tlog mismatch, fails verification.
cosign verify ... registry.example.com/app:1.2.3
Error: ... error verifying bundle: retrieving Rekor public keys: updating local metadata and targets: ...
6. Annotation or policy predicate not satisfied
A --annotations constraint or a policy expecting a specific attestation predicate does not match what was signed.
Diagnostic Workflow
Step 1: Confirm a signature actually exists
cosign tree registry.example.com/app:1.2.3
cosign triangulate registry.example.com/app:1.2.3 # the signature's storage tag
If no signatures are listed, the problem is signing (or a moved tag), not your verify command.
Step 2: Pin to the digest, not the tag
DIGEST=$(crane digest registry.example.com/app:1.2.3)
echo "$DIGEST"
cosign verify --key cosign.pub registry.example.com/app@"$DIGEST"
Verifying the exact digest removes tag-mutation ambiguity.
Step 3: For keyless, read the actual signer identity
cosign verify \
--certificate-identity-regexp ".*" \
--certificate-oidc-issuer-regexp ".*" \
registry.example.com/app:1.2.3 2>&1 | head
The output (or the error’s got subjects [...] with issuer ...) shows the real identity and issuer to match against — then tighten your --certificate-identity accordingly.
Step 4: Verify the key matches
# Confirm you're using the key that corresponds to the signer
cosign public-key --key cosign.key 2>/dev/null | diff - cosign.pub && echo "key pair matches"
Step 5: Check transparency-log / TUF connectivity
cosign initialize # refresh TUF trust root
# air-gapped: point at a bundled trust root
cosign verify --trusted-root trusted_root.json ... registry.example.com/app:1.2.3
Example Root Cause Analysis
An admission controller starts rejecting a production image with no matching signatures, breaking a rollout. The image was signed keyless from GitHub Actions and verification is configured to expect the main branch workflow:
cosign verify \
--certificate-identity "https://github.com/acme/app/.github/workflows/release.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
registry.example.com/app:1.4.0
Error: no matching signatures: none of the expected identities matched what was in the certificate,
got subjects [https://github.com/acme/app/.github/workflows/release.yml@refs/tags/v1.4.0] with issuer https://token.actions.githubusercontent.com
The signature is genuine and the issuer is correct — but the release now runs on tag pushes, so the certificate identity carries @refs/tags/v1.4.0, while the policy still pins @refs/heads/main. The verification criteria drifted from the CI trigger, not the image.
Fix: update the policy to match the tag-based signer identity (using a regexp that allows any release tag) and re-verify:
cosign verify \
--certificate-identity-regexp "^https://github.com/acme/app/.github/workflows/release.yml@refs/tags/v.*$" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
registry.example.com/app:1.4.0
Verification for registry.example.com/app:1.4.0 --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- Existence of the claims in the transparency log was verified offline
- The code-signing certificate was verified using trusted certificate authority certificates
The image verifies, and the admission policy now matches the real signing identity.
Prevention Best Practices
- Verify by digest in policy and CI, never by mutable tag; sign and admit the same immutable digest.
- Keep verification identity in lockstep with the signing trigger — if CI signs on tags, the policy must expect the tag ref (use
--certificate-identity-regexpfor release families). - Fail closed: keep admission controllers (Kyverno/policy-controller/OPA) rejecting unsigned or unmatched images rather than falling back to allow.
- For air-gapped clusters, bundle and pin the Sigstore trust root (
--trusted-root) and runcosign initializefrom a mirrored TUF repo. - Alert when
cosign treeshows an expected image has no signatures, so a skipped signing step is caught in CI, not at deploy. - Document key ownership and rotation; when a key rotates, roll the corresponding
--key/public key in every verifier at the same time.
Quick Command Reference
# What supply-chain artifacts exist for the image?
cosign tree registry.example.com/app:1.2.3
cosign triangulate registry.example.com/app:1.2.3
# Verify by digest (avoid tag mutation)
DIGEST=$(crane digest registry.example.com/app:1.2.3)
cosign verify --key cosign.pub registry.example.com/app@"$DIGEST"
# Keyless verify with explicit identity + issuer
cosign verify \
--certificate-identity-regexp "^https://github.com/acme/app/.github/workflows/release.yml@refs/tags/v.*$" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
registry.example.com/app:1.2.3
# Refresh Sigstore trust root (TUF)
cosign initialize
# Confirm a key pair matches
cosign public-key --key cosign.key | diff - cosign.pub
Conclusion
no matching signatures means cosign found no signature that satisfied your trust criteria — the supply-chain gate doing exactly its job. Read why before loosening anything:
- Verifying with the wrong (or rotated) public key.
- Keyless
--certificate-identity/--oidc-issuernot matching the real signer. - The image was never signed for that digest.
- A tag moved to a new, unsigned digest — verify by digest instead.
- Missing Rekor/TUF trust on an offline host.
- An annotation or predicate constraint not satisfied.
Confirm a signature exists with cosign tree, pin the digest, read the actual signer identity, then align your verification criteria — never disable verification or admit unsigned images to make the error go away.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.