Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
doc: add examples for sign and verify (#238)
This PR adds examples for notation-go sign and verify including fully functional examples (can run in go playground) for local sign and local verify. Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
- Loading branch information
1 parent
c90d521
commit e11bfd8
Showing
5 changed files
with
457 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package notation_test | ||
|
||
import ( | ||
"context" | ||
"crypto/x509" | ||
"fmt" | ||
|
||
"github.com/notaryproject/notation-core-go/signature" | ||
"github.com/notaryproject/notation-core-go/signature/cose" | ||
"github.com/notaryproject/notation-core-go/testhelper" | ||
"github.com/notaryproject/notation-go" | ||
"github.com/notaryproject/notation-go/signer" | ||
ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||
) | ||
|
||
var ( | ||
// exampleDesc is an example of the target OCI artifact manifest descriptor. | ||
exampleDesc = ocispec.Descriptor{ | ||
MediaType: "application/vnd.docker.distribution.manifest.v2+json", | ||
Digest: "sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c", | ||
Size: 528, | ||
} | ||
|
||
// exampleCertTuple contains a RSA privateKey and a self-signed X509 | ||
// certificate generated for demo purpose ONLY. | ||
exampleCertTuple = testhelper.GetRSASelfSignedSigningCertTuple("Notation Example self-signed") | ||
exampleCerts = []*x509.Certificate{exampleCertTuple.Cert} | ||
) | ||
|
||
// ExampleLocalSign demonstrates how to use signer.Sign to sign an artifact | ||
// at local (without using a registry.Repository). | ||
func Example_localSign() { | ||
// exampleSigner is a notation.Signer given key and X509 certificate chain. | ||
// Users should replace `exampleCertTuple.PrivateKey` with their own private | ||
// key and replace `exampleCerts` with the corresponding full certificate | ||
// chain, following the Notary certificate requirements: | ||
// https://github.com/notaryproject/notaryproject/blob/v1.0.0-rc.1/specs/signature-specification.md#certificate-requirements | ||
exampleSigner, err := signer.New(exampleCertTuple.PrivateKey, exampleCerts) | ||
if err != nil { | ||
panic(err) // Handle error | ||
} | ||
|
||
// Both COSE ("application/cose") and JWS ("application/jose+json") | ||
// signature mediaTypes are supported. | ||
exampleSignatureMediaType := cose.MediaTypeEnvelope | ||
|
||
// exampleSignOptions is an example of notation.SignOptions. | ||
exampleSignOptions := notation.SignOptions{ | ||
SignatureMediaType: exampleSignatureMediaType, | ||
SigningAgent: "example signing agent", | ||
} | ||
|
||
// local sign core process | ||
// upon successful signing, signature envelope and signerInfo are returned. | ||
// signatureEnvelope can be used in a verification process later on. | ||
signatureEnvelope, signerInfo, err := exampleSigner.Sign(context.Background(), exampleDesc, exampleSignOptions) | ||
if err != nil { | ||
panic(err) // Handle error | ||
} | ||
|
||
fmt.Println("Successfully signed") | ||
|
||
// a peek of the signature envelope generated from Sign | ||
sigBlob, err := signature.ParseEnvelope(exampleSignatureMediaType, signatureEnvelope) | ||
if err != nil { | ||
panic(err) // Handle error | ||
} | ||
sigContent, err := sigBlob.Content() | ||
if err != nil { | ||
panic(err) // Handle error | ||
} | ||
fmt.Println("signature Payload ContentType:", sigContent.Payload.ContentType) | ||
fmt.Println("signature Payload Content:", string(sigContent.Payload.Content)) | ||
fmt.Println("signerInfo SigningAgent:", signerInfo.UnsignedAttributes.SigningAgent) | ||
|
||
// Output: | ||
// Successfully signed | ||
// signature Payload ContentType: application/vnd.cncf.notary.payload.v1+json | ||
// signature Payload Content: {"targetArtifact":{"mediaType":"application/vnd.docker.distribution.manifest.v2+json","digest":"sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c","size":528}} | ||
// signerInfo SigningAgent: example signing agent | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
package notation_test | ||
|
||
import ( | ||
"context" | ||
"encoding/pem" | ||
"errors" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/notaryproject/notation-core-go/signature/cose" | ||
"github.com/notaryproject/notation-go" | ||
"github.com/notaryproject/notation-go/dir" | ||
"github.com/notaryproject/notation-go/verifier" | ||
"github.com/notaryproject/notation-go/verifier/trustpolicy" | ||
"github.com/notaryproject/notation-go/verifier/truststore" | ||
ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||
) | ||
|
||
// examplePolicyDocument is an example of a valid trust policy document. | ||
// trust policy document should follow this spec: | ||
// https://github.com/notaryproject/notaryproject/blob/v1.0.0-rc.1/specs/trust-store-trust-policy.md#trust-policy | ||
var examplePolicyDocument = trustpolicy.Document{ | ||
Version: "1.0", | ||
TrustPolicies: []trustpolicy.TrustPolicy{ | ||
{ | ||
Name: "test-statement-name", | ||
RegistryScopes: []string{"example/software"}, | ||
SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: trustpolicy.LevelStrict.Name}, | ||
TrustStores: []string{"ca:valid-trust-store"}, | ||
TrustedIdentities: []string{"*"}, | ||
}, | ||
}, | ||
} | ||
|
||
// ExampleLocalVerify demonstrates how to use verifier.Verify to verify a | ||
// signature of the target artifact at local (without using a | ||
// registry.Repository). | ||
func Example_localVerify() { | ||
// exampleArtifactReference is an example of the target artifact reference | ||
exampleArtifactReference := "example/software@sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c" | ||
|
||
// Both COSE ("application/cose") and JWS ("application/jose+json") | ||
// signature mediaTypes are supported. | ||
exampleSignatureMediaType := cose.MediaTypeEnvelope | ||
|
||
// exampleTargetDescriptor is an example of the target OCI artifact manifest | ||
// descriptor. | ||
exampleTargetDescriptor := ocispec.Descriptor{ | ||
MediaType: "application/vnd.docker.distribution.manifest.v2+json", | ||
Digest: "sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c", | ||
Size: 528, | ||
} | ||
|
||
// exampleSignatureEnvelope is a valid signature envelope. | ||
exampleSignatureEnvelope := generateExampleSignatureEnvelope() | ||
|
||
// exampleVerifyOptions is an example of notation.VerifyOptions | ||
exampleVerifyOptions := notation.VerifyOptions{ | ||
ArtifactReference: exampleArtifactReference, | ||
SignatureMediaType: exampleSignatureMediaType, | ||
} | ||
|
||
// createTrustStore creates a trust store directory for demo purpose. | ||
// Users could use the default trust store from Notary and add trusted | ||
// certificates into it following the trust store spec: | ||
// https://github.com/notaryproject/notaryproject/blob/v1.0.0-rc.1/specs/trust-store-trust-policy.md#trust-store | ||
if err := createTrustStore(); err != nil { | ||
panic(err) // Handle error | ||
} | ||
|
||
// exampleVerifier is an example of notation.Verifier given | ||
// trust policy document and X509 trust store. | ||
exampleVerifier, err := verifier.New(&examplePolicyDocument, truststore.NewX509TrustStore(dir.ConfigFS()), nil) | ||
if err != nil { | ||
panic(err) // Handle error | ||
} | ||
|
||
// local verify core process | ||
// upon successful verification, the signature verification outcome is | ||
// returned. | ||
outcome, err := exampleVerifier.Verify(context.Background(), exampleTargetDescriptor, exampleSignatureEnvelope, exampleVerifyOptions) | ||
if err != nil { | ||
panic(err) // Handle error | ||
} | ||
|
||
fmt.Println("Successfully verified") | ||
|
||
// a peek of the payload inside the signature envelope | ||
fmt.Println("payload ContentType:", outcome.EnvelopeContent.Payload.ContentType) | ||
|
||
// Note, upon successful verification, payload.TargetArtifact from the | ||
// signature envelope matches exactly with our exampleTargetDescriptor. | ||
// (This check has been done for the user inside verifier.Verify.) | ||
fmt.Println("payload Content:", string(outcome.EnvelopeContent.Payload.Content)) | ||
|
||
// Output: | ||
// Successfully verified | ||
// payload ContentType: application/vnd.cncf.notary.payload.v1+json | ||
// payload Content: {"targetArtifact":{"mediaType":"application/vnd.docker.distribution.manifest.v2+json","digest":"sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c","size":528}} | ||
} | ||
|
||
func generateExampleSignatureEnvelope() []byte { | ||
// exampleSignatureEnvelopePem is a valid signature envelope in COSE format | ||
// wrapped by a PEM block. | ||
// The signature envelope is generated in a previous Sign process. | ||
// Users should replace it with their own signature envelope. | ||
// Regarding how to generate such signature envelopes, users could refer to | ||
// `example_localSign_test.go`. | ||
exampleSignatureEnvelopePem := `-----BEGIN EXAMPLE SIGNATURE ENVELOPE----- | ||
0oRYnqUBOCQCgXgcaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZQN4K2FwcGxp | ||
Y2F0aW9uL3ZuZC5jbmNmLm5vdGFyeS5wYXlsb2FkLnYxK2pzb254GmlvLmNuY2Yu | ||
bm90YXJ5LnNpZ25pbmdUaW1lwRpju22GeBxpby5jbmNmLm5vdGFyeS5zaWduaW5n | ||
U2NoZW1la25vdGFyeS54NTA5ohghgVkDRDCCA0AwggIooAMCAQICAVEwDQYJKoZI | ||
hvcNAQELBQAwTjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdT | ||
ZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxDzANBgNVBAMTBmFscGluZTAgFw0wMDA4 | ||
MjkxMzUwMDBaGA8yMTIzMDgyOTEzNTAwMFowTjELMAkGA1UEBhMCVVMxCzAJBgNV | ||
BAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxDzANBgNV | ||
BAMTBmFscGluZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKHIN6hL | ||
MjQwy3wfDhw+HYYvjNTTytQLMuTV/OHPoL2nGbqDy08JqTn5upz7exjzwfRu0usc | ||
YRTW0cU2H2FIyvpGgo9/F4wUX+ZRnsG0iSlZMUPNv2sVO9HHkltyaWs62oHjfSVE | ||
2fyX4uDH54qSE8HJjKIoo/Hhqx7JI8lcmWyXdFjDCkQ1lYSz1IjzFVrf+He2LWSL | ||
c2nGkCrV5l4LEwk1qSKJbN4H7TWI60KDLFHpVHQ/LzgFjfSdvP0sgvrkofytSn8l | ||
JW6rn5+HYvAxAcZ7T+cJ12GyS9Y7Y7FIBMQFY0MU9cyOfV9+pt7d2CqgkIdXLndN | ||
i+aJzm2Os4+ezekCAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG | ||
CCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQAvAIwS8uxLEaYuqILgnRUm+p0R | ||
o7xdezfm+pg297AEZwfE9acb8009usmvgMlkfo46HRMGzCEvtd5Vfvak9i8KpWzl | ||
DWOdfT2thkUWO9iY3qMFiN4ipCmjM32VAe5UUxl3RLmQKS20zv9yVXEfX37tNDdV | ||
GgD/WBhJUreCQyWAPTPnf0LaPh4iLBNCo/o3Z8CGKLzzzpa8iji3xW/69RhKu5+t | ||
8RWc/N4sljWmXbCeTd2B8XTqZGwWwmpThAQyU40CqngGAS6ADTVNDgbJZqhwkgUx | ||
J4W6iRzekCshdPUnDpeS8DNULE5dFGObIhiwH4/40n/Th/VS0zxzkvPzdCmueBtp | ||
by5jbmNmLm5vdGFyeS5zaWduaW5nQWdlbnRuTm90YXRpb24vMS4wLjBYtXsidGFy | ||
Z2V0QXJ0aWZhY3QiOnsibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tl | ||
ci5kaXN0cmlidXRpb24ubWFuaWZlc3QudjIranNvbiIsImRpZ2VzdCI6InNoYTI1 | ||
NjpjMGQ0ODhhODAwZTQxMjdjMzM0YWQyMGQ2MWQ3YmMyMWI0MDk3NTQwMzI3MjE3 | ||
ZGZhYjUyMjYyYWRjMDIzODBjIiwic2l6ZSI6NTI4fX1ZAQBUIYPA45B/iFylmloW | ||
s/NpTVsheuedJb6nnXK0XR46BYs4AeCXVVYSRDK2Bq+tA9C7BXHoeCMcqnAHa8qs | ||
ZR/fcMa9FrEPI6Pl8QVE/6QRMkT+Drn+9CSFxzHk6CU9S1vRsUVYNcibyejnuVEE | ||
RPYaORrnfTc5wIs4XxeqprmrLimMMNn+u82Uadtc57tbHbY8Vh4XEKP++hBJJNvQ | ||
E60X5aWKIS2RnOEc4n9T7LdN0bOL1OoM1lW4iTFMhzfcy/VmF8PrOStFS9LllX3J | ||
69V0WwHbmD33cjtVBDCF44UXRWgLQGbE6yaaVmdxEUBGKqSUeHf8Gp7WoZ/YaFmz | ||
xQr/ | ||
-----END EXAMPLE SIGNATURE ENVELOPE-----` | ||
|
||
block, _ := pem.Decode([]byte(exampleSignatureEnvelopePem)) | ||
if block == nil { | ||
panic(errors.New("invalid signature envelope pem block")) | ||
} | ||
|
||
// block.Bytes contains the binary of the signature envelope. | ||
return block.Bytes | ||
} | ||
|
||
func createTrustStore() error { | ||
// changing the path of the trust store for demo purpose. | ||
// Users could keep the default value, i.e. os.UserConfigDir. | ||
dir.UserConfigDir = "tmp" | ||
|
||
// an example of a valid X509 self-signed certificate for demo purpose ONLY. | ||
// (This self-signed cert is paired with the private key used to | ||
// generate the `exampleSignatureEnvelopePem` above.) | ||
// Users should replace `exampleX509Certificate` with their own trusted | ||
// certificate and add to the trust store, following the | ||
// Notary certificate requirements: | ||
// https://github.com/notaryproject/notaryproject/blob/v1.0.0-rc.1/specs/signature-specification.md#certificate-requirements | ||
exampleX509Certificate := `-----BEGIN CERTIFICATE----- | ||
MIIDQDCCAiigAwIBAgIBUTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzEL | ||
MAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEP | ||
MA0GA1UEAxMGYWxwaW5lMCAXDTAwMDgyOTEzNTAwMFoYDzIxMjMwODI5MTM1MDAw | ||
WjBOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUx | ||
DzANBgNVBAoTBk5vdGFyeTEPMA0GA1UEAxMGYWxwaW5lMIIBIjANBgkqhkiG9w0B | ||
AQEFAAOCAQ8AMIIBCgKCAQEAocg3qEsyNDDLfB8OHD4dhi+M1NPK1Asy5NX84c+g | ||
vacZuoPLTwmpOfm6nPt7GPPB9G7S6xxhFNbRxTYfYUjK+kaCj38XjBRf5lGewbSJ | ||
KVkxQ82/axU70ceSW3JpazrageN9JUTZ/Jfi4MfnipITwcmMoiij8eGrHskjyVyZ | ||
bJd0WMMKRDWVhLPUiPMVWt/4d7YtZItzacaQKtXmXgsTCTWpIols3gftNYjrQoMs | ||
UelUdD8vOAWN9J28/SyC+uSh/K1KfyUlbqufn4di8DEBxntP5wnXYbJL1jtjsUgE | ||
xAVjQxT1zI59X36m3t3YKqCQh1cud02L5onObY6zj57N6QIDAQABoycwJTAOBgNV | ||
HQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQAD | ||
ggEBAC8AjBLy7EsRpi6oguCdFSb6nRGjvF17N+b6mDb3sARnB8T1pxvzTT26ya+A | ||
yWR+jjodEwbMIS+13lV+9qT2LwqlbOUNY519Pa2GRRY72JjeowWI3iKkKaMzfZUB | ||
7lRTGXdEuZApLbTO/3JVcR9ffu00N1UaAP9YGElSt4JDJYA9M+d/Qto+HiIsE0Kj | ||
+jdnwIYovPPOlryKOLfFb/r1GEq7n63xFZz83iyWNaZdsJ5N3YHxdOpkbBbCalOE | ||
BDJTjQKqeAYBLoANNU0OBslmqHCSBTEnhbqJHN6QKyF09ScOl5LwM1QsTl0UY5si | ||
GLAfj/jSf9OH9VLTPHOS8/N0Ka4= | ||
-----END CERTIFICATE-----` | ||
|
||
// Adding the certificate into the trust store. | ||
if err := os.MkdirAll("tmp/truststore/x509/ca/valid-trust-store", 0700); err != nil { | ||
return err | ||
} | ||
return os.WriteFile("tmp/truststore/x509/ca/valid-trust-store/NotationLocalExample.pem", []byte(exampleX509Certificate), 0600) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package notation_test | ||
|
||
import ( | ||
"context" | ||
"crypto/x509" | ||
"fmt" | ||
|
||
"github.com/notaryproject/notation-core-go/signature/cose" | ||
"github.com/notaryproject/notation-core-go/testhelper" | ||
"github.com/notaryproject/notation-go" | ||
"github.com/notaryproject/notation-go/registry" | ||
"github.com/notaryproject/notation-go/signer" | ||
"oras.land/oras-go/v2/registry/remote" | ||
) | ||
|
||
// Both COSE ("application/cose") and JWS ("application/jose+json") | ||
// signature mediaTypes are supported. | ||
var exampleSignatureMediaType = cose.MediaTypeEnvelope | ||
|
||
// ExampleRemoteSign demonstrates how to use notation.Sign to sign an artifact | ||
// in the remote registry and push the signature to the remote. | ||
func Example_remoteSign() { | ||
// exampleArtifactReference is an example of the target artifact reference | ||
var exampleArtifactReference = "localhost:5000/software@sha256:60043cf45eaebc4c0867fea485a039b598f52fd09fd5b07b0b2d2f88fad9d74e" | ||
|
||
// exampleCertTuple contains a RSA privateKey and a self-signed X509 | ||
// certificate generated for demo purpose ONLY. | ||
exampleCertTuple := testhelper.GetRSASelfSignedSigningCertTuple("Notation Example self-signed") | ||
exampleCerts := []*x509.Certificate{exampleCertTuple.Cert} | ||
|
||
// exampleSigner is a notation.Signer given key and X509 certificate chain. | ||
// Users should replace `exampleCertTuple.PrivateKey` with their own private | ||
// key and replace `exampleCerts` with the corresponding full certificate | ||
// chain, following the Notary certificate requirements: | ||
// https://github.com/notaryproject/notaryproject/blob/v1.0.0-rc.1/specs/signature-specification.md#certificate-requirements | ||
exampleSigner, err := signer.New(exampleCertTuple.PrivateKey, exampleCerts) | ||
if err != nil { | ||
panic(err) // Handle error | ||
} | ||
|
||
// exampleRepo is an example of registry.Repository. | ||
remoteRepo, err := remote.NewRepository(exampleArtifactReference) | ||
if err != nil { | ||
panic(err) // Handle error | ||
} | ||
exampleRepo := registry.NewRepository(remoteRepo) | ||
|
||
// exampleSignOptions is an example of notation.SignOptions. | ||
exampleSignOptions := notation.SignOptions{ | ||
ArtifactReference: exampleArtifactReference, | ||
SignatureMediaType: exampleSignatureMediaType, | ||
} | ||
|
||
// remote sign core process | ||
// upon successful signing, descriptor of the sign content is returned and | ||
// the generated signature is pushed into remote registry. | ||
targetDesc, err := notation.Sign(context.Background(), exampleSigner, exampleRepo, exampleSignOptions) | ||
if err != nil { | ||
panic(err) // Handle error | ||
} | ||
|
||
fmt.Println("Successfully signed") | ||
fmt.Println("targetDesc MediaType:", targetDesc.MediaType) | ||
fmt.Println("targetDesc Digest:", targetDesc.Digest) | ||
fmt.Println("targetDesc Size:", targetDesc.Size) | ||
} |
Oops, something went wrong.