Snippet that uses openssl to verify a cosign signature
Demonstrates the manifest and image hash calculation using cosign
ref
- https://blog.sigstore.dev/cosign-image-signatures-77bab238a93/
- https://github.com/containers/image/blob/main/docs/containers-signature.5.md
- https://blog.salrashid.dev/articles/2022/cosign_tpm/
- Sample of GCP Binary Authorization audit log and GPG verification
# start with an image
export IMAGE="gcr.io/distroless/static-debian11@sha256:e7e79fb2947f38ce0fab6061733f7e1959c12b843079042fe13f56ca7b9d178c"
# create a private key manually to import into cosign
openssl genrsa -out priv.pem 2048
cosign import-key-pair --key priv.pem
# sign using cosign
cosign sign --key=import-cosign.key --tlog-upload=false -a timestamp=12344 --upload=false --output-signature=signature.dat --output-payload=payload.dat --tlog-upload=false $IMAGE
$ cat payload.dat | jq '.'
{
"critical": {
"identity": {
"docker-reference": "gcr.io/distroless/static-debian11"
},
"image": {
"docker-manifest-digest": "sha256:e7e79fb2947f38ce0fab6061733f7e1959c12b843079042fe13f56ca7b9d178c"
},
"type": "cosign container image signature"
},
"optional": {
"timestamp": "12344"
}
}
$ cat signature.dat
I26TU/Z2j194EgoR83Np7X2rhStZQ82uZrWZ5pKGxAqFn864fNCRAPc0ELfo6xlfRYp1COmtscSFJ6wjM+wiiF1F6MY4Qfm1GZaEjPmnOCW8ecMUB85HrN9CoYyIs8EkMwh52s8sAU5qvfG7rQIHkS+D+Tbphw9XG7pnAYYYu9opUY6bORt8eHwfwC7R8RP+BgDh2uv4sJMun9Q6nYOyx0hE4xtTNRE5hbqi8weOvFOcFQVclOFOuywWLqijYBoAfDN78Gi7IFN4+Cw202PTX9UgNZC40qVe3UD5sK9BBshh0eYlQDKBqgaLDkDDA03PvqIdLSrMhDx4E+QuwjW88w=
# verify using cosign
$ cosign verify --key import-cosign.pub --signature=signature.dat --payload=payload.dat --insecure-ignore-tlog=true $IMAGE | jq '.'
Verification for gcr.io/distroless/static-debian11@sha256:e7e79fb2947f38ce0fab6061733f7e1959c12b843079042fe13f56ca7b9d178c --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified public key
[
{
"critical": {
"identity": {
"docker-reference": "gcr.io/distroless/static-debian11"
},
"image": {
"docker-manifest-digest": "sha256:e7e79fb2947f38ce0fab6061733f7e1959c12b843079042fe13f56ca7b9d178c"
},
"type": "cosign container image signature"
},
"optional": {
"timestamp": "12344"
}
}
]
# use crane to create the manifest
$ crane manifest $IMAGE > manifest.json
# calculate the hash of the manifest; this is the image digest itself
$ sha256sum manifest.json
e7e79fb2947f38ce0fab6061733f7e1959c12b843079042fe13f56ca7b9d178c manifest.json
# now create the cosign signature JSON file itself. note this includes the manifest/digest from above
$ cosign generate -a timestamp=12344 $IMAGE | jq '.'
{
"critical": {
"identity": {
"docker-reference": "gcr.io/distroless/static-debian11"
},
"image": {
"docker-manifest-digest": "sha256:e7e79fb2947f38ce0fab6061733f7e1959c12b843079042fe13f56ca7b9d178c"
},
"type": "cosign container image signature"
},
"optional": {
"timestamp": "12344"
}
}
## as a side note, "type" field is different for GCP binary authorization signature attachments:
$ gcloud beta container binauthz create-signature-payload --artifact-url=$IMAGE
{
"critical": {
"identity": {
"docker-reference": "gcr.io/distroless/static-debian11"
},
"image": {
"docker-manifest-digest": "sha256:a01d47d4036cae5a67a9619e3d06fa14a6811a2247b4da72b4233ece4efebd57"
},
"type": "Google cloud binauthz container signature"
}
}
# now put it all together and use the raw private key and openssl to sign the json above
$ cosign generate -a timestamp=12344 $IMAGE | awk '{ printf "%s", $0 }' - | openssl dgst -sha256 -sign priv.pem -out /tmp/sign.sha256 -
# we now got back the json from above
$ base64 /tmp/sign.sha256 --wrap=0
I26TU/Z2j194EgoR83Np7X2rhStZQ82uZrWZ5pKGxAqFn864fNCRAPc0ELfo6xlfRYp1COmtscSFJ6wjM+wiiF1F6MY4Qfm1GZaEjPmnOCW8ecMUB85HrN9CoYyIs8EkMwh52s8sAU5qvfG7rQIHkS+D+Tbphw9XG7pnAYYYu9opUY6bORt8eHwfwC7R8RP+BgDh2uv4sJMun9Q6nYOyx0hE4xtTNRE5hbqi8weOvFOcFQVclOFOuywWLqijYBoAfDN78Gi7IFN4+Cw202PTX9UgNZC40qVe3UD5sK9BBshh0eYlQDKBqgaLDkDDA03PvqIdLSrMhDx4E+QuwjW88w==srashid@srashid12- priv.pem
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD3ZgxnM0VELQyl
CQORi9eLSPgLJ1swJZ76ky+TMYi22NM3s/WXKb5VYmeOyataSIfQntuZ3DViv3GL
nkt38UBK4A077QfNMtFba1y5DeK5afVzUqvb/sG52ActPsJeeuhv2GZzB6YMYGRS
7mDWmJkvvkNfihEEzdrsaQxlIy2FXs3PARDPsTtyBjGAGRqy3XY+ewvubqJFWuRf
q44ifvEA51GfyrhnixU1VgMDln9az/FsUfhxKgM6zqPnU4qO8HzORV5VV1ul37YW
oUIyCuA2CkOGk+PxXXHBmW2KP5cO8SOKY0ma4Bn7Vdj3yLVSZW6Q8H1nj1oZNUD6
KZ7LtjWlAgMBAAECggEADt8UBMoYslHw7iS+Eawxzz4aJvV9LkY8qbxaQkNaf/9F
pxYKv8TzG4fYSMWHptwwSdKaycNqdB6k8SsZR0L5xeZ09TK44tVYr2GjR751cAbW
CdIg4Y8NTyIUwFwC7BIAUeTUt7pweVL8BN4udQfkQXHy89XFUyAatlZUN+om7NrZ
SzzUg1ZVDr7ARbOTlJmJAVoAfBMlG3KKwReDKWSb6W/Z4a2/oILQpMYZy1uXPv/u
WEwDwNyRgb4O7E+ZQMxT05YBObN/jOfCigRsD060ryu+cP26fwGLuK/IrbRMv0Td
GDCTXHlOFBil5gM0cQzz+ZzLrktBcMdE9lvoKr9cuwKBgQD+n0J/uh4LwBUYymH1
I6NxD9gAb8th2dhQbTwFofsgVtE1AZNOHqWtBYCORHTRbdYalF2oQi1XfGy7YKNk
0I2HAxt9r9NAXu2UqO57T9oz5nZQhBX4z7VzygNCdddTTXQymU2FWQaMS/U5tGdI
f/ABq4z3w6UBZTZ8t5PHqALd1wKBgQD4vMgaWYZLRnj5fRe5tX55DdHw0HFVPa/8
4rmY4kOXm0DRPGim/o86ymEyf9AvI2M4E1uZp5tVyJzvOlTwmCvhCYaOK38Fg56h
ltXD4SIVwOmnhFEMc9MHRu4p8Xr7ns//huRYBmmFBCpUS4d5vLI/Z16QdEEuTLD3
bP7A58CA4wKBgQCugJF/nhuHbA5JXjKCYflavEQKEJQvrLAOdr6jEZ9dS4K3tCuz
Z1eeW93+IrZ47bDFqJcDgM84XSLbzGBO8It+U0fc/MpoHGbw8lH3GwPte+3sQooY
aqnuAC35t29qPPP81YoGcLwHHOukr5n1WxEu49aJ24DTkv0jvmC+fcvxIwKBgCaC
A3Cg33uT43KsfRnREU1oaFIh0wnhTPeXmigjD1fPP0+lTIZd7oqpfja1NuO1VMz/
ojS9j/zN3A3lN/UvecuTD/O+pCGUaOCXW2zB0+cOQJ4gi/Ojks4Tmxd/PQzrX0Qt
+/fstjVTmcIHF2gvYGEUfb1bYiPq15LWmvH+bv0FAoGAE/2kq1j5z1oK+AX24nJ4
3stii1sjSGkvPMaCzj26J0mtfgYaTmb6bkp9u4UF5n6lcQAycsdKIdvTmZiKUU9R
ktSszccNLiZazmLOdeG0JabrFrZLC2hK6HxRD3sx7CgbxGVT3VJM1NEDys69uCGS
tg/1BtUqT+gHLkXBswnPsy8=
-----END PRIVATE KEY-----