This directory contains a reference implementation of Signed HTTP Exchanges format generator.
We currently provide two command-line tools: gen-signedexchange and gen-certurl.
gen-signedexchange generates a signed exchange file. The gen-signedexchange command constructs an HTTP request and response pair from given command line flags, attach the cryptographic signature of the pair, and serializes the result to an output file.
gen-certurl converts an X.509 certificate chain, an OCSP response, and an SCT (if one isn't already included in the certificate or OCSP response) to application/cert-chain+cbor format, which is defined in the Section 3.3 of the Signed HTTP Exchanges spec.
You are also welcome to use the code as a Go lib (e.g. import "github.com/WICG/webpackage/go/signedexchange"), but please be aware that the API is not yet stable and is subject to change any time.
The Go environment needs to be set up in prior to using the tool. We are testing the tool on the latest version of Go. Please refer to the Go Getting Started documentation for the details.
We recommend using go install (Golang 1.18+) to install the command-line tool.
go install github.com/WICG/webpackage/go/signedexchange/cmd/...@latest
In this section, we guide you to create a signed exchange file that is signed using a self-signed certificate pair.
Here, we assume that you have an access to an HTTPS server capable of serving static content. [1] Please substitute https://yourcdn.example.net/ URLs to your web server URL, and example.org to the domain for which you want to sign the exchange.
-
Prepare a file to be enclosed in the signed exchange. This serves as the payload of the HTTP response in the signed exchange.
echo "<h1>hi</h1>" > payload.html -
Prepare a certificate and private key pair to use for signing the exchange. To generate a signed-exchange-compatible self-signed key pair with OpenSSL, invoke:
# Generate prime256v1 ecdsa private key. openssl ecparam -out priv.key -name prime256v1 -genkey # Create a certificate signing request for the private key. openssl req -new -sha256 -key priv.key -out cert.csr \ -subj '/CN=example.org/O=Test/C=US' # Self-sign the certificate with "CanSignHttpExchanges" extension. openssl x509 -req -days 90 -in cert.csr -signkey priv.key -out cert.pem \ -extfile <(echo -e "1.3.6.1.4.1.11129.2.1.22 = ASN1:NULL\nsubjectAltName=DNS:example.org") -
Convert the PEM certificate to
application/cert-chain+cborformat usinggen-certurltool. This command will show warnings about OCSP and SCT, but you can ignore them.# Fill in dummy data for OCSP, since the certificate is self-signed. gen-certurl -pem cert.pem -ocsp <(echo ocsp) > cert.cbor -
Host the
application/cert-chain+cborcreated in Step 3 on the HTTPS server. Configure the resource to be served withContent-Type: application/cert-chain+cborHTTP header. The steps below assume thecert.cboris hosted athttps://yourcdn.example.net/cert.cbor, so substitute the URL to the actual URL in below steps.- Note: If you are using Firebase Hosting as your HTTPS server, see an example config here.
-
Generate the signed exchange using
gen-signedexchangetool.gen-signedexchange \ -uri https://example.org/hello.html \ -content ./payload.html \ -certificate cert.pem \ -privateKey priv.key \ -certUrl https://yourcdn.example.net/cert.cbor \ -validityUrl https://example.org/resource.validity.msg \ -o example.org.hello.sxg -
Host the signed exchange file
example.org.hello.sxgon the HTTPS server. Configure the resource to be served withContent-Type: application/signed-exchange;v=b3HTTP header.- Note: If you are using Firebase Hosting as your HTTPS server, see an example config here.
-
Navigate to the signed exchange URL using a web browser supporting signed exchanges.
- Chrome: To ignore certificate errors of the self-signed certificate:
# Note that --user-data-dir is required for --ignore-certificate-errors-spki-list # to take effect. google-chrome \ --user-data-dir=/tmp/udd \ --ignore-certificate-errors-spki-list=`openssl x509 -noout -pubkey -in cert.pem | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64` \ https://yourcdn.example.net/example.org.hello.sxg
- Chrome: To ignore certificate errors of the self-signed certificate:
[1]: You can deploy your own HTTPS server or use a cloud hosting service. Note that the server must support configuring "Content-Type" HTTP headers, like Firebase Hosting.
In this section, you will create a signed exchange using a certificate issued by a publicly trusted CA.
Your signed exchange needs to be signed with a certificate with the "CanSignHttpExchanges" extension.
-
Get a certificate from a CA. You have to use prime256v1 ecdsa keys, as you did in the previous section. Please follow the CA's instructions. (For example, DigiCert offers the right kind of certificates.)
Assume you got a server certificate
server.pemand an intermediate certificateintermediates.pem. The tools need all certificates in a single file, so concatenate them.cat server.pem intermediates.pem > cert-chain.pem -
Convert the PEM certificate to
application/cert-chain+cborformat usinggen-certurltool.gen-certurl -pem cert-chain.pem > cert.cborIf you got the warning message
Warning: Neither cert nor OCSP have embedded SCT list. Use -sctDir flag to add SCT from files., you need to prepare SCT files.Otherwise, you can skip the next step.
-
To get SCTs, submit your certificate chain to Certificate Transparency log servers.
- Install a tool to submit certificates to log servers.
go install github.com/grahamedgecombe/ct-submit@latest - Submit your cert chain to logs as appropriate, and write out SCTs:
mkdir scts ct-submit ct.googleapis.com/logs/argon2018 < cert-chain.pem > scts/argon2018.sct ct-submit ct.cloudflare.com/logs/nimbus2018 < cert-chain.pem > scts/nimbus2018.sct - Create a cert-chain with the obtained SCTs.
gen-certurl -pem cert-chain.pem -sctDir scts > cert.cbor
- Install a tool to submit certificates to log servers.
-
Host
cert.cboron a HTTPS server. Please see the previous section for details. -
Generate the signed exchange using
gen-signedexchangetool.priv.keyis the private key used to create your certificate.gen-signedexchange \ -uri https://example.org/hello.html \ -content ./payload.html \ -certificate cert-chain.pem \ -privateKey priv.key \ -certUrl https://yourcdn.example.net/cert.cbor \ -validityUrl https://example.org/resource.validity.msg \ -o example.org.hello.sxg -
Host
example.org.hello.sxgon a HTTPS server. Please see the previous section for details. -
Navigate to the signed exchange URL using a web browser supporting signed exchanges.
You can dump the content of your sxg file by dump-signedexchange. If you want to see the content of the signed exchange file example.org.hello.sxg you created above, run this command.
dump-signedexchange -i example.org.hello.sxg
If the -json flag is passed, the output will be in JSON.
dump-signedexchange -i example.org.hello.sxg -json
You can also dump the content of a signed exchange from a URI. If you want to see the content of the signed exchange you're hosting at https://example.org/hello.html, run this command. By default this will request the latest version.
dump-signedexchange -uri https://example.org/hello.html
If the specified URI requires a special header to serve a signed exchange, you can pass request headers via the -requestHeader flag. The header key and value should be separated by a :.
dump-signedexchange -uri https://example.org/hello.html -requestHeader AMP-Cache-Transform:any -requestHeader "foo:bar"
When the -uri flag is passed to dump-signedexchange, you can specify the sxg version to request by passing a -version flag. For instance, if you wanted to request a 1b2 signed exchange, you would run the following command. By default, the version is 1b3.
dump-signedexchange -uri https://example.org/hello.html -version=1b2
dump-signedexchange can also operate on piped input. For instance, you could run the following command to retrieve a b3 signed exchange.
curl -H "AMP-Cache-Transform:any" -H "Accept:application/signed-exchange;v=b3" https://example.org/hello.html | dump-signedexchange
dump-signedexchange can print the information you want about your signed exchange. By default, both the headers and the payload are printed, but they can be suppressed by passing -headers=false and -payload=false.
If you would like only the signature to be printed, pass the -signature flag.
dump-signedexchange -i example.org.hello.sxg -signature
If the -verify command-line flag is specified, dump-signedexchange checks if the signed exchange is valid.
By default, dump-signedexchange fetches certificate chain from network (from the URL you specified as -certUrl parameter of gen-signedexchange). But if -cert filename flag is given, dump-signedexchange reads certificates from filename.
For example, If you want to verify example.org.hello.sxg using certificates in cert.cbor, run this command.
dump-signedexchange -i example.org.hello.sxg -verify -cert cert.cbor