How to set up Signed Exchanges using Web Packager

Learn how to serve signed exchanges (SXGs) using Web Packager.

Katie Hempenius
Katie Hempenius

A signed exchange (SXG) is a delivery mechanism that makes it possible to authenticate the origin of a resource independently of how it was delivered. The following instructions explain how to set up Signed Exchanges using Web Packager. Instructions are included for both self-signed and CanSignHttpExchanges certificates.

Using a self-signed certificate to serve SXGs is primarily used for demonstration and testing purposes. SXGs signed with a self-signed certificate will generate error messages in the browser when used outside of testing environments and should not be served to crawlers.

Prerequisites

To follow these instructions you will need to have openssl and Go installed in your development environment.

Generate a self-signed certificate

This section explains how to generate a self-signed certificate that can be used with signed exchanges.

Instructions

  1. Generate a private key.

    openssl ecparam -out priv.key -name prime256v1 -genkey
    

    The private key will be saved as a file named priv.key.

  2. Create a certificate signing request (CSR).

    openssl req -new -sha256 -key priv.key -out cert.csr -subj '/O=Web Packager Demo/CN=example.com'
    

    A certificate signing request is a block of encoded text that conveys the information necessary to request a certificate from a certificate authority(CA). Although you will not be requesting a certificate from a CA, it is still necessary to create a certificate signing request.

    The command above creates a certificate signing request for an organization named Web Packager Demo that has the common name example.com. The common name should be the fully qualified domain name of the site that contains the content that you want to package as SXG.

    In a production SXG setup, this would be a site that you own. However, in a testing environment like the one described in these instructions, it can be any site.

  3. Create a certificate that has the 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.com")
    

    This command uses the private key and the CSR created in steps 1 and 2 to create the certificate file cert.pem. The -extfile flag associates the certificate with the CanSignHttpExchanges certificate extension (1.3.6.1.4.1.11129.2.1.22 is the object identifier for the CanSignHttpExchanges extension). In addition, the -extfile flag also defines example.com as a Subject Alternative Name.

    If you are curious about the contents of cert.pem, you can view them using the following command:

    openssl x509 -in cert.pem -noout -text
    

    You are done creating private keys and certificates. You will need the priv.key and cert.pem files in the next section.

Setup the Web Packager server for testing

Prerequisites

  1. Install Web Packager.

    git clone https://github.com/google/webpackager.git
    
  2. Build webpkgserver.

    cd webpackager/cmd/webpkgserver
    go build .
    

    webpkgserver is a specific binary within the Web Packager project.

  3. Verify that webpkgserver has been installed correctly.

    ./webpkgserver --help
    

    This command should return information about the usage of webpkgserver. If this does not work, a good first troubleshooting step is to verify that your GOPATH is configured correctly.

Instructions

  1. Navigate to the webpkgserver directory (you might already be in this directory).

    cd /path/to/cmd/webpkgserver
    
  2. Create a webpkgsever.toml file by copying the example.

    cp ./webpkgserver.example.toml ./webpkgserver.toml
    

    This file contains the configuration options for webpkgserver.

  3. Open webpkgserver.toml with an editor of your choice and make the following changes:

    • Change the line #AllowTestCert = false to AllowTestCert = true.
    • Change the line PEMFile = 'path/to/your.pem' to reflect the path to the PEM certificate, cert.pem, that you created. Do not change the line mentioning TLS.PEMFile—this is a different configuration option.
    • Change the line KeyFile = 'priv.key' to reflect the path of the private key, priv.key, that you created. Do not change the line mentioning TLS.KeyFile—this is a different configuration option.
    • Change the line #CertURLBase = '/webpkg/cert' to CertURLBase = 'data:'. CertURLBase indicates the serving location of the SXG certificate. This information is used to set the cert-url parameter in the Signature header of the SXG. In production environments, CertURLBase is used like this: CertURLBase = 'https://mysite.com/'. However, for local testing, CertURLBase = 'data:' can be used to instruct webpkgserver to use a data URL to inline the certificate in the cert-url field. For local testing, this is the most conveninent way to serve the SXG certificate.
    • Change the line Domain = 'example.org' to reflect the domain that you created a certificate for. If you have followed the instructions in this article verbatim, this should be changed to example.com. webpkgserver will only fetch content from the domain indicated by webpkgserver.toml. If you try to fetch pages from a different domain without updating webpkgserver.toml, the webpkgserver logs will show the error message URL doesn't match the fetch targets.

    Optional

    If you want to enable or disable subresource preloading, the following webpkgserver.toml configuration options are relevant:

    • To have webpkgserver insert directives for preloading stylesheet and script subresources as SXGs, change the line #PreloadCSS = false to PreloadCSS = true. In addition, change the line #PreloadJS = false to PreloadJS = true.

      As an alternative to using this configuration option, you can manually add Link: rel="preload" headers and <link rel="preload"> tags to a page's HTML.

    • By default, webpkgserver replaces existing <link rel="preload"> tags with the equivalent <link> tags necessary for fetching this content as SXG. In doing so, webpkgserver will set the allowed-alt-sxg and header-integrity directives as needed—HTML authors do not need to add these by hand. To override this behavior and keep existing non-SXG preloads, change #KeepNonSXGPreloads (default = false) to KeepNonSXGPreloads = true. Keep in mind that enabling this option may make the SXG ineligible for the Google SXG cache per these requirements.

  4. Start webpkgserver.

    ./webpkgserver
    

    If the server has started successfully, you should see the following log messages: shell Listening at 127.0.0.1:8080 Successfully retrieved valid OCSP. Writing to cache in /private/tmp/webpkg

    Your log messages may look slightly different. In particular, the directory that webpkgserver uses to cache certificates varies by operating system.

    If you do not see these messages, a good first troubleshooting step is to double-check webpkgserver.toml.

    If you update webpkgserver.toml you should restart webpkgserver.

  5. Launch Chrome using the following command: shell /Applications/Google\ Chrome.app/Contents/MacOS/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`

    This command instructs Chrome to ignore the certificate errors associated with cert.pem. This makes it possible to test SXGs using a test certificate. If Chrome is launched without this command, inspecting the SXG in DevTools will display the error Certificate verification error: ERR_CERT_INVALID.

    Note:

    You may need to adjust this command to reflect the location of Chrome on your machine, as well as the location of cert.pem. If you've done this correctly, you should see a warning displayed below the address bar. The warning should be similar to this: You are using an unsupported command-line flag: --ignore-certificate-errors-spki-list=9uxADcgc6/ho0mJLRMBcOjfBaN21k0sOInoMchr9CMY=.

    If the warning does not include a hash string, you have not correctly indicated the location of the SXG certificate.

  6. Open the DevTools Network tab, then visit the following URL: http://localhost:8080/priv/doc/https://example.com.

    This makes a request to the webpackager instance running at http://localhost:8080 for a SXG containing the contents of https://example.com. /priv/doc/ is the default API endpoint used by webpackager.

    Screenshot of the DevTools Network tab showing a SXG and its certificate.

    The following resources are listed in the Network tab:

    • A resource with the type signed-exchange. This is the SXG.
    • A resource with the type cert-chain+cbor. This the SXG certificate. SXG certificates must use the application/cert-chain+cbor format.
    • A resource with the type document. This is the content that has been delivered via SXG.

    If you don't see these resources, try clearing the browser cache, then reloading http://localhost:8080/priv/doc/https://example.com.

    Click on the Preview tab to see more information about the Signed Exchange and its signature.

    Screenshot of the Preview tab showing a SXG

Serve signed exchanges using a CanSignHttpExchanges certificate

The instructions in this section explain how to serve SXGs using a CanSignHttpExchanges certificate. Production use of SXGs requires a CanSignHttpExchanges certificate.

For the sake of brevity, these instructions are written with the assumption that you understand the concepts discussed in the Setup Signed Exchanges using a self-signed certificate section.

Prerequisites

  • You have a CanSignHttpExchanges certificate. This page lists the CAs that offer this type of certificate.

  • If you don't have a certificate, then you can configure your webpkgserver to automatically retrieve certificates from your CA. You can follow the directions for what goes in webpkgserver.toml in this page.

  • Although not a requirement, it is strongly recommended that you run webpkgserver behind an edge server. If you do not use an edge server, you will need to configure the TLS.PEMFile and TLS.KeyFile options in webpkgserver.toml. By default, webpkgserver runs over HTTP. However, SXG certificates must be served over HTTPS to be considered valid by the browser. Configuring TLS.PEMFile and TLS.KeyFile allows webpkgserver to use HTTPS and therefore serve the SXG certificate directly to the browser.

Instructions

  1. Create a PEM file by concatenating your site's SXG certificate followed by your site's CA certificate. More instructions on this can be found here.

    PEM is a file format that is commonly used as a "container" for storing multiple certificates.

  2. Create a fresh webpkgsever.toml file by copying the example.

    cp ./webpkgserver.example.toml ./webpkgserver.toml
    
  3. Open webpkgserver.toml with the editor of your choice and make the following changes:

    • Change the line PEMFile = cert.pem to reflect the location of the PEM file containing your full certificate chain.
    • Change the line KeyFile = 'priv.key' to reflect the location of the private key corresponding to your PEM File.
    • Change the line Domain = 'example.org' to reflect your site.
    • (Optional) To have webpkgserver auto-renew the SXG certificate every 90 days (45 days for Google), configure the options in the [SXG.ACME] section of webpkgserver.toml. This option only applies to sites with a DigiCert or Google ACME account setup.
  4. Configure your edge server to forward traffic to the webpkgserver instance.

    There are two primary types of requests handled by webpkgserver: requests for SXGs (which are served by the /priv/doc/ endpoint) and requests for the SXG certificate (which are served by the /webpkg/cert/ endpoint). The URL rewriting rules for each of these request types varies slightly. For more information, see Running behind front end edge server.

    Note:

    By default, webpkgserver serves the SXG certificate at /webpkg/cert/$CERT_HASH—for example, /webpkg/cert/-0QmE0gvoedn92gtwI3s7On9zPevJGm5pn2RYhpZxgY. To generate $CERT_HASH, run the following command: shell openssl base64 -in cert.pem -d | openssl dgst -sha256 -binary | base64 | tr /+ _- | tr -d =