![Robert Ferens](https://web.dev/images/authors/rferens.jpg) Robert Ferens ![Demián Renzulli](https://web.dev/images/authors/demianrenzulli.jpg) Demián Renzulli [X](https://twitter.com/drenzulli) [GitHub](https://github.com/demianrenzulli)

<br />

This document provides essential information regarding the process of key
rotation for an [Isolated Web App
(IWA)](https://chromeos.dev/en/web/isolated-web-apps), including when it is
necessary and the steps developers must follow if a key is lost or compromised.

## Key rotation for IWAs

Key rotation is a mechanism that allows the private keys used to sign your IWA
to be replaced in the event of a leak or loss. This process keeps the app
functional and maintains a stable bundle ID and origin, ensuring the continuity
of your app's distribution and updates.

The key rotation process doesn't require any administrator or user changes, and if
performed correctly, won't be perceptible to them. The trusted contact
channels established during the
[initial allowlisting process](https://developer.chrome.com/docs/iwa/allowlist) are essential for
enabling key rotation.

## The need for key rotation

Key rotation is strictly necessary in two primary scenarios:

- **Key leak or compromise:** If your private signing key is compromised or potentially leaked, you must immediately start the key rotation process to secure your application. A leaked key can be used to impersonate you, cause harm to your app users, and endanger your company's reputation.
- **Key loss:** If you lose access to your private signing key and cannot sign new updates, key rotation is required to re-establish your ability to distribute your application.

## Developer requirements

Before initiating a key rotation, you must meet the following criteria:

- **Allowlisted app:** Only [allowlisted apps](https://developer.chrome.com/docs/iwa/allowlist) can be subject to a key rotation.
- **Trusted email:** You must use the trusted email address provided during the allowlisting process to request a key rotation.

## Resigning hosted apps and releasing new versions

After rotating a key, all installed app instances that are not signed with a
valid key are blocked. To prevent disruptions for your users, you must deliver
an app signed with the new key (or both keys). The technical details of
resigning bundles can be found in the section
[How to sign a bundle with multiple keys](https://developer.chrome.com/docs/iwa/key-rotation?tab=t.0#heading=h.sky60nhp3pec).
Depending on your specific
situation, there are different scenarios:

## Scenario A: The key is not lost (for example, proactive rotation or leak)

You must sign the app with both keys (the old and the new one) and deliver them
to users by either:

- Releasing a new app version (adding the new version to your update manifests), or
- Updating your hosted `.swbn` files (linked in your update manifests) to be signed with both keys. The CLI tool lets you add another signature to the existing `swbn` bundle.

This must be done before the key rotation is processed by Google to ensure your
users won't notice the change.

> [!NOTE]
> **Note:** If the administrator uses a version pinning, they need to update the hosted bundle for this version or pin the app to the version that is signed with both keys.

## Scenario B: The key is lost

Because the app cannot be signed with the lost key this case is more complex.
Request the key rotation and ask your Google contact for further steps. You
might be asked to add the new signatures to your hosted bundles following
[these instructions](https://developer.chrome.com/docs/iwa/key-rotation?tab=t.0#bookmark=id.czyrbzn2yi9p).

> [!NOTE]
> **Note:** If you are using version pinning and releasing a new app version for a key rotation, consider changing the pinned version.

## Key rotation process

The process for key rotation involves the following steps:

| Step | Action | Details | Responsible |
|---|---|---|---|
| 1 | Request key rotation | Developer/partner reaches out to their Google contact (Partner Engineering or other point of contact) through **trusted email** provided in [allowlisting process](https://developer.chrome.com/docs/iwa/allowlist) and asks for the key rotation instructions explaining the reason. In the key compromised case we recommend attaching the update manifest with bundles signed with old and new keys to speed up the process. | Developer / Partner |
| 2 | Response to requestor | Google contact provides further instructions and questions to the requestor. This step may involve a few back-and-forths. | Google Contact |
| 3 | Provide data | Developer/partner follows instructions, which among others may involve: - Resigning bundles with new keys. <!-- --> - Updating or providing your update manifest with updated apps. <!-- --> - Providing more information. For example, situation details, dangers, or technical details like a partner's app release cycle. <!-- --> - Acknowledging the readiness for rotation: we suggest doing that after at least three days after updating client apps and making sure they were updated. | Developer / Partner |
| 4 | Request processing and providing feedback | Google reviews the key rotation request and responds within one business week, either approving or denying it, or contacting the developer with further questions. Upon approval, Google rotates the key and provides feedback. | Google Contact |

## Update channel and version pinning

Administrators sometimes use custom update channels or version pinning, which lets them
manage app versions on their client devices. After a key rotation, all
apps installed with invalid keys become invalid and are blocked. To prevent
this, earlier versions of apps hosted under links provided in the
`update_manifest.json` must also be updated and signed with two signatures prior
to the key rotation. If your old key is lost and the app cannot be signed with
two keys, notify your Google contact immediately. We will work with you
to mitigate the situation so the app can be updated without disturbing your
users' workflows.

## How to sign a bundle with multiple keys

This section describes how you can sign your web bundles with one or two keys
through CLI tools, or Webpack/Rollup/Vite plugins configuration. CLI tools also
enable adding one more signature to existing `swbn` bundles, which might be
useful in key-lost cases or updating the previous hosted bundles.

### CLI Tools

First, ensure you have the `wbn-sign` package installed with `npm`:

    $ npm i wbn-sign

Add `-g` to install the package globally instead of doing it only in the current
folder.

#### Signing with one key

    $ wbn-sign -i unsigned.wbn -o signed.swbn -k private.pem

#### Signing with two keys

    $ wbn-sign sign webbundle.wbn old_key.pem new_key.pem -o signed.swbn --web-bundle-id <your-id-from-old-private>

The `--web-bundle-id <id>` argument can be skipped, if old_key is the first key
the initial version of the app was signed with, then the bundle ID will be
derived from the key. You can verify the bundle ID with
`wbn-sign info signed.swbn` command.

#### Adding a signature to the existing bundle

    $ wbn-sign add-signature signed.swbn key3.pem --in-place

Use `-o` if you don't want to override the bundle but create a new
double-signed.

#### More information about CLI Tool

For more information on using CLI tool check the [npm wbn-sign
page](https://www.npmjs.com/package/wbn-sign). Alternatively, use: `wnb-sign
help.`

### Webpack plugin

#### Signing with one key

To configure the Webpack plugin to sign your IWA using a single private key:

- Set up the `WebBundlePlugin` to automatically derive the required Base URL directly from this key.
- Use a single `NodeCryptoSigningStrategy` to produce your signed `.swbn` file.

    // key is parsePemKey(private.pem)

    plugins.push(
      new WebBundlePlugin({
        baseURL: new WebBundleId(key).serializeWithIsolatedWebAppOrigin(),
        static: { dir: 'public' },
        output: 'signed.swbn',
        integrityBlockSign: {
          strategy: new NodeCryptoSigningStrategy(key)
        }
      })
    )

#### Signing with two keys

When you need to double-sign your application during a key rotation:

- Define the `webBundleId` and Base URL based on your original (`old_key`) to ensure the app's identity remains stable for your users.
- Use an array of signing strategies to apply both your old and new private keys.

    // old_key is parsePemKey(old_private.pem)
    // new_key is parsePemKey(new_private.pem)

    plugins.push(
      new WebBundlePlugin({
        baseURL: new WebBundleId(old_key).serializeWithIsolatedWebAppOrigin(),
        static: { dir: 'public') },
        output: 'signed.swbn',
        integrityBlockSign: {
          strategies: [
            new NodeCryptoSigningStrategy(old_key),
            new NodeCryptoSigningStrategy(new_key)
          ],
          webBundleId: new WebBundleId(old_key).serialize()
        }
      })
    )

### Rollup or Vite plugin

#### Signing with one key

To use the Rollup or Vite plugin to sign your application with a single private
key, similar to the Webpack setup, the plugin uses a single signing strategy and
handles deriving the Base URL from the provided key to generate your signed
output.

    // key is parsePemKey(private.pem)

    plugins.push({
      ...wbn({
        baseURL: new WebBundleId(key).serializeWithIsolatedWebAppOrigin(),
        static: { dir: 'public' },
        output: 'signed.swbn',
        integrityBlockSign: {
          strategy: new NodeCryptoSigningStrategy(key)
        }
      }),
      enforce: 'post'
    })

#### Signing with two keys

To double-sign your application using Rollup or Vite, you must configure the
plugin to accept multiple keys. This snippet applies both the old and new
private keys using the strategies array. It ensures a seamless transition by
explicitly maintaining the Base URL and `webBundleId` derived from your
original, existing key.

    // old_key is parsePemKey(old_private.pem)
    // new_key is parsePemKey(new_private.pem)

    plugins.push({
      ...wbn({
        baseURL: new WebBundleId(old_key).serializeWithIsolatedWebAppOrigin(),
        static: { dir: 'public' },
        output: 'signed.swbn',
        integrityBlockSign: {
          strategies: [
            new NodeCryptoSigningStrategy(old_key),
            new NodeCryptoSigningStrategy(new_key)
          ],
          webBundleId: new WebBundleId(old_key).serialize()
        }
      }),
      enforce: 'post'
    })

## Conclusion

Successfully managing key rotation for your IWA is crucial for maintaining the
security and continuity of your application's distribution. Independently on
your specific situation (for example, whether the key is lost or not), following
the outlined steps ensures that your users experience minimal disruption. By
utilizing the provided CLI tools or plugin configurations for Webpack, Rollup,
or Vite, you can efficiently manage your app's signatures and navigate the key
rotation process smoothly. In emergency scenarios, remember to communicate
promptly with your Google contact through your designated trusted email.