Problem
CI environments without OIDC token support—such as Jenkins, TeamCity, or internal pipelines—cannot attest bundles using keyless (Fulcio) signing. There is currently no way to sign bundles with a KMS-backed key.
Proposal
Add a --signing-key flag to aicr bundle that accepts a KMS URI for cosign-compatible key-based signing. This ensures key material never touches disk.
Supported Backends
All backends natively supported by cosign via URI scheme:
| Backend |
URI Scheme |
| AWS KMS |
awskms://arn:aws:kms:... |
| GCP KMS |
gcpkms://projects/... |
| Azure Key Vault |
azurekms://... |
| HashiCorp Vault |
hashivault://... |
Behavior
--signing-key <kms-uri> triggers KMS-backed signing instead of keyless OIDC flow
- The URI resolves to the appropriate KMS provider at runtime
- The resulting signature bundle uses the same format as keyless signing (cosign bundle)
- Verification works with the corresponding public key (
cosign verify --key <kms-uri>)
Mutual Exclusivity
--signing-key is mutually exclusive with keyless OIDC signing flags (--identity-token, --oidc-device-flow). Providing both is a validation error.
Implementation Notes
Updated 2026-06-02: signing has migrated off cosign's Go library to sigstore-go's sign package; the original notes (which referenced cosign.LoadKeyFromKMS() and "no new dependencies") no longer reflect the code.
- The signing path is now
pkg/bundler/attestation/signing.go (SignStatement), which uses sigstore-go's sign package (github.com/sigstore/sigstore-go/pkg/sign) for the keyless Fulcio + Rekor flow — not cosign's Go SignerVerifier API.
- KMS-backed signing means providing a key-based signer in place of the keyless Fulcio/Rekor path. With sigstore-go this is a keypair/signer abstraction; the most direct route is a cosign KMS
SignerVerifier adapter (sigstore/cosign/v2/pkg/signature + the cloud KMS provider packages). This will pull in new dependencies — the earlier "no new dependencies required" claim is no longer accurate.
- The branch point is the attester resolver (
attestation.ResolveAttesterLazy in pkg/bundler/attestation/resolver.go) plus SignStatement: select key-based vs keyless based on whether --signing-key is set, rather than the pkg/evidence/ / pkg/bundler/ location guessed originally.
- There is no
aicr bundle-sign subcommand; signing is folded into aicr bundle --attest.
Relationship to other attestation work
This is the foundation for air-gapped signing (#409) — keyless signing requires Fulcio network access, so offline attestation needs a key-based path. Tracked under the attestation epic.
Success Criteria
aicr bundle --signing-key gcpkms://projects/my-proj/locations/global/keyRings/kr/cryptoKeys/k produces a valid signed bundle
- Signature is verifiable with
cosign verify --key <same-kms-uri>
- Works in CI without OIDC token availability
make test and make lint pass with no regressions
Problem
CI environments without OIDC token support—such as Jenkins, TeamCity, or internal pipelines—cannot attest bundles using keyless (Fulcio) signing. There is currently no way to sign bundles with a KMS-backed key.
Proposal
Add a
--signing-keyflag toaicr bundlethat accepts a KMS URI for cosign-compatible key-based signing. This ensures key material never touches disk.Supported Backends
All backends natively supported by cosign via URI scheme:
awskms://arn:aws:kms:...gcpkms://projects/...azurekms://...hashivault://...Behavior
--signing-key <kms-uri>triggers KMS-backed signing instead of keyless OIDC flowcosign verify --key <kms-uri>)Mutual Exclusivity
--signing-keyis mutually exclusive with keyless OIDC signing flags (--identity-token,--oidc-device-flow). Providing both is a validation error.Implementation Notes
pkg/bundler/attestation/signing.go(SignStatement), which uses sigstore-go'ssignpackage (github.com/sigstore/sigstore-go/pkg/sign) for the keyless Fulcio + Rekor flow — not cosign's GoSignerVerifierAPI.SignerVerifieradapter (sigstore/cosign/v2/pkg/signature+ the cloud KMS provider packages). This will pull in new dependencies — the earlier "no new dependencies required" claim is no longer accurate.attestation.ResolveAttesterLazyinpkg/bundler/attestation/resolver.go) plusSignStatement: select key-based vs keyless based on whether--signing-keyis set, rather than thepkg/evidence//pkg/bundler/location guessed originally.aicr bundle-signsubcommand; signing is folded intoaicr bundle --attest.Relationship to other attestation work
This is the foundation for air-gapped signing (#409) — keyless signing requires Fulcio network access, so offline attestation needs a key-based path. Tracked under the attestation epic.
Success Criteria
aicr bundle --signing-key gcpkms://projects/my-proj/locations/global/keyRings/kr/cryptoKeys/kproduces a valid signed bundlecosign verify --key <same-kms-uri>make testandmake lintpass with no regressions