---
title: "Patch Signing"
description: "How patches are signed by default, and how to sign with your own key"
---

Patch signing makes your app check that every patch it downloads carries a signature matching a public key built into your release, and refuse any patch that doesn’t. Someone who can tamper with your users’ network traffic can’t produce that signature, so they can’t push code to your app.

Starting with Shorebird CLI 1.6.132, every new release is signed by default, with a key Shorebird manages for your organization. There are three modes:

| Mode                                  | How you choose it                                                            | Who holds the private key            |
| ------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------ |
| [Managed](#managed-signing) (default) | `shorebird release` with no key flags                                        | Shorebird, in Google Cloud KMS       |
| [Self-managed](#self-managed-signing) | `--public-key-path` or `--public-key-cmd` on release, a private key on patch | You                                  |
| [Unsigned](#turn-signing-off)         | `--no-patch-signing` on release                                              | No one: patches aren’t signed at all |

The mode is fixed when you release each platform, because the public key is built into the release. To change it, make a new release. There are no changes to your code in any mode.

Releases made with an older CLI, without a key, are unsigned. Patches to them stay unsigned and keep installing. To protect an app made that way, update the CLI and make a new release; you don’t need to change your Flutter version.

## Managed signing

Managed signing is the default. To turn it on for an app, update the Shorebird CLI and make a new release:

```
shorebird upgrade
shorebird release android
```

The release summary shows which key the release uses:

```
🔏 Patch Signing: Shorebird-managed key
```

Patch as usual with `shorebird patch`. Shorebird signs each patch for you.

### How it works

* Shorebird creates a signing key for your organization in [Google Cloud KMS](https://cloud.google.com/kms/docs) the first time `shorebird release` needs one. The private key never leaves KMS: Cloud KMS doesn’t let anyone export it, including Shorebird.
* `shorebird release` fetches the organization’s public key from Shorebird and builds it into the release, the same way `--public-key-path` does.
* `shorebird patch` builds the patch with the same public key, so it matches the release. When the patch is uploaded, Shorebird’s servers sign it with the matching private key. A patch to a managed release is never published unsigned: if it can’t be signed, the upload fails.
* Each release keeps the key it was built with, and every patch to it is signed with that key.
* Your app verifies signatures exactly as it does for self-managed keys. See [Test and verify signed patch](#test-and-verify-signed-patch).

Patching a managed release takes no key flags. If you pass `--public-key-path`, `--public-key-cmd`, `--private-key-path`, or `--sign-cmd`, `shorebird patch` stops before uploading and names the flags to remove. A CLI older than 1.6.132 can’t patch a managed release; it tells you to run `shorebird upgrade`.

If `shorebird release` can’t get the signing key from Shorebird, the release fails rather than going out unsigned. Run it again. If you can’t wait, you can [turn signing off](#turn-signing-off) for that release, but it stays unsigned for good: every patch to it is unsigned, and only a new release turns signing back on.

### What managed signing protects against

Managed signing protects against anyone between your users and Shorebird. It doesn’t protect against someone who controls Shorebird itself. If you need that, use [self-managed signing](#self-managed-signing).

| An attacker who…                                                                        | Managed       | Self-managed                                  | Unsigned      |
| --------------------------------------------------------------------------------------- | ------------- | --------------------------------------------- | ------------- |
| Controls your users’ network, including with a fraudulent TLS certificate               | Protected     | Protected                                     | Not protected |
| Can change patch files in storage or on the CDN, but not Shorebird’s API                | Protected     | Protected                                     | Protected     |
| Compromises Shorebird’s servers or signing service                                      | Not protected | Protected                                     | Not protected |
| Compromises Google Cloud                                                                | Not protected | Protected, unless your key is in Google Cloud | Not protected |
| Has your Shorebird credentials (an account, API key, or CI token) and publishes a patch | Not protected | Protected, unless they have your private key  | Not protected |

Unsigned apps are protected in the second row because your app already rejects a patch whose contents don’t match the hash Shorebird’s API returned for it. Signing adds protection when the attacker can also change what the API returns, as in the first row.

In short, managed signing moves your app’s trust from the network and every certificate authority your app trusts to Shorebird and Google. Self-managed signing removes Shorebird and Google from it too.

### Switch to your own key

To move an app from managed signing to your own key, make a new release with `--public-key-path` or `--public-key-cmd`, as in [Self-managed signing](#self-managed-signing). Releases you made with managed signing keep receiving signed patches. Managed keys can’t be exported, so you can’t take your existing managed key with you.

### Turn signing off

You can release without signing, though it leaves your app open to the attacks above:

```
shorebird release android --no-patch-signing
```

Patches to that release are unsigned, as before 1.6.132. You can’t combine `--no-patch-signing` with `--public-key-path` or `--public-key-cmd`.

## Self-managed signing

With self-managed signing, you sign patches with your own key, so no one, including Shorebird, can change the content of your patches without your private key.

Signing works in two parts. First, `shorebird release` takes a `--public-key-path` or `--public-key-cmd` argument to embed your public key in your released application. The [Shorebird updater](https://github.com/shorebirdtech/updater) only loads patches signed with the corresponding private key.

Second, when you build your patch with `shorebird patch`, you pass `--private-key-path` or `--sign-cmd` to sign your patch with your private key. This is required for a release created with your public key.

You will need an RSA key pair. Shorebird supports RSA keys in PEM format, both PKCS#1 and PKCS#8.

### Generate keys

If you do not already have an RSA key pair you’d like to use, you can generate a pair with `openssl`:

```
# Generate a key pair
openssl genrsa -out private.pem 2048

# Extract the public key
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
```

Note

On Windows, you can use the `openssl` that comes with your git installation at `C:\Program Files\Git\usr\bin\openssl.exe`.

The above operation will generate a public/private key pair in `private.pem` and a public key in `public.pem`. The file containing the private key should be stored securely and kept secret. While the private key is not itself sufficient to make an update to your application (someone would also need access to your Shorebird credentials), it should not be checked into public source control.

### Key storage

If you don’t want to store keys yourself, use [managed signing](#managed-signing). If you are using self-managed keys, using a cloud key management service instead of just storing keys on disk is highly recommended. **If for any reason you were to lose your private key, there is no way to create a patch for an application containing the corresponding public key.** Even Shorebird is not able to create a patch for your application without your private key. In such a case, you would need to make and distribute a new release of your application to send patches to it.

Shorebird does support command-based signing for integration with cloud key management services (HashiCorp Vault, GCP Cloud KMS, AWS KMS, Azure Key Vault), hardware security modules (HSMs), and secrets managers (1Password, etc.). Please ensure you are using at least [v1.6.81](https://github.com/shorebirdtech/shorebird/releases/tag/v1.6.81) or greater to use this feature.

See [Cloud KMS Examples](#cloud-kms-examples) below for integration examples with popular services.

### Create a signed release

Note

These examples use Android, but patch signing works on all platforms.

To create a release that requires signed patches, run the following command:

```
# Build Release with local path to key file
shorebird release android --public-key-path /path/to/public.pem

# Build Release with command for cloud-managed public key
# Ensure that the command outputs a PEM-encoded public key to stdout
shorebird release android \
  --public-key-cmd="your-command-that-outputs-pem-public-key"
```

This will include the public key in the release artifact produced by `shorebird release` and cause the released app to require signed patches.

### Create a signed patch

To create a signed patch, run the following command:

```
# Build Patch with local path to key files
shorebird patch android --track=stable --public-key-path /path/to/public.pem \
 --private-key-path /path/to/private.pem

# Build Patch with cloud-managed signing
# Ensure that the command outputs a PEM-encoded public key to stdout
# sign-cmd should read data from stdin and output a base64 signature to
# stdout
  shorebird patch android --track=stable \
   --public-key-cmd="your-command-that-outputs-pem-public-key" \
   --sign-cmd="your-command-that-signs-stdin-and-outputs-base64"
```

This tells Shorebird to sign the patch with the key pair you provided.

### Patch a release signed with your own key

If `shorebird patch` stops before building with exit code 64 and this message, the release embeds your public key but the patch command didn’t pass a key pair:

```
Release 1.0.0+1 was signed with your own key, so its patches must be too. Pass the same public key as the release, with --public-key-path and --private-key-path, or --public-key-cmd and --sign-cmd.
```

Run the patch again with the same public key the release used and its private key, as in [Create a signed patch](#create-a-signed-patch).

Shorebird 1.6.135 and newer make this check for releases built with Shorebird 1.6.132 or newer. For older releases, or with an older CLI, nothing stops the patch, and devices on the release refuse it because it has no valid signature (see [Fallback behavior](#fallback-behavior)).

## Test and verify signed patch

This works the same for managed and self-managed signing. You can verify that the patch is properly signed using `shorebird preview`. On the first launch, you should see something like the following in your app logs:

```
05-22 23:47:51.645  6963  6994 I flutter : updater::updater: [shorebird] Patch 1 successfully downloaded. It will be launched when the app next restarts.
05-22 23:47:51.645  6963  6994 I flutter : updater::updater: [shorebird] Update thread finished with status: Update installed
```

If you close and relaunch your app, you should see this message telling you that the patch’s signature was verified:

```
05-23 11:32:33.944  7029  7029 I flutter : updater::cache::signing: [shorebird] Verifying patch signature...
05-23 11:32:33.944  7029  7029 I flutter : updater::cache::signing: [shorebird] Patch signature is valid
```

If the patch is missing a signature, or fails signature verification for any reason, Shorebird will not load it, and will instead use any previously installed and verifiable patch (if there is one) or the unpatched release version of your app.

With self-managed signing, you can also test the missing signature behavior (managed releases refuse these patches before they’re published). Create a patch without a private key and notice that `shorebird preview` rejects it. Similarly, you can create a patch with a different private key to do the same.

## Fallback behavior

Releases that contain a public key will reject all unsigned patches. If a patch is missing a signature or its signature is invalid, Shorebird will reject this patch at boot time. It will instead boot from the last known good patch (if still on disk) or the release build of the app. This will not cause your app to crash.

## Trade-offs

Signature verification, managed or self-managed, adds a small overhead at app launch. During testing this has been observed to be under 50ms with a medium-sized app on a 5-year-old Android phone. This overhead increases with application size. For very large apps, if this overhead shows up on your benchmarks, you can set `patch_verification: install_only` in `shorebird.yaml` to verify signatures only when patches are installed rather than on every launch.

Caution

On Shorebird’s builds of Flutter 3.41.9, 3.44.0 to 3.44.9, and 3.47.0 to 3.47.7, `patch_verification: install_only` skips the signature check entirely, so devices install patches without verifying them. Shorebird 1.6.132 and newer refuse to release with that combination:

```
`patch_verification: install_only` in shorebird.yaml does not check patch signatures with Flutter 3.47.7 (...).
```

Shorebird 1.6.134 and newer also warn when you patch a release built on one of these versions while `shorebird.yaml` still sets `install_only`. Either remove `patch_verification` from `shorebird.yaml` (or set it to `strict`, the default, which verifies at every launch and is unaffected), or release with a Flutter version outside these ranges, such as 3.41.8 or 3.47.8 and later. A release already built this way keeps installing patches without checking their signatures; changing `shorebird.yaml` only protects new releases.

## Cloud KMS examples

The following examples show how to integrate Shorebird patch signing with popular key management services using `--public-key-cmd` and `--sign-cmd`.

Note

Integrating with Cloud KMS requires that the executing environment (your local terminal or CI/CD runner) has sufficient IAM credentials to read keys and sign payloads.

* **GCP Cloud KMS**: Requires the `roles/cloudkms.viewer` (to read the public key) and `roles/cloudkms.signerVerifier` (to sign the patch) roles.
* **AWS KMS**: Requires the `kms:GetPublicKey` and `kms:Sign` IAM policy actions.
* **Azure Key Vault**: Requires Key Vault key permissions for `get` and `sign`.

### HashiCorp Vault

```
# Store your key in Vault Transit
vault write transit/keys/shorebird-signing type=rsa-2048

# Release
shorebird release android \
  --public-key-cmd="vault read -field=public_key transit/keys/shorebird-signing"

# Patch
shorebird patch android --track=stable \
  --public-key-cmd="vault read -field=public_key transit/keys/shorebird-signing" \
  --sign-cmd="vault write -field=signature transit/sign/shorebird-signing \
    hash_algorithm=sha2-256 signature_algorithm=pkcs1v15 input=-"
```

### GCP Cloud KMS

```
# Create a key ring and key
gcloud kms keyrings create shorebird --location=global
gcloud kms keys create signing-key --keyring=shorebird --location=global \
  --purpose=asymmetric-signing --default-algorithm=rsa-sign-pkcs1-2048-sha256

# Release
shorebird release android \
  --public-key-cmd="gcloud kms keys versions get-public-key 1 \
    --key=signing-key --keyring=shorebird --location=global"

# Patch (using a helper script for signing)
shorebird patch android --track=stable \
  --public-key-cmd="gcloud kms keys versions get-public-key 1 \
    --key=signing-key --keyring=shorebird --location=global" \
  --sign-cmd="gcloud kms asymmetric-sign --version=1 \
    --key=signing-key --keyring=shorebird --location=global \
    --digest-algorithm=sha256 --input-file=- --signature-file=- | base64"
```

### AWS KMS

```
# Create an RSA signing key
aws kms create-key --key-spec RSA_2048 --key-usage SIGN_VERIFY

# Create a helper script for public key (aws-kms-pubkey.sh).
# Needed to get the format correct from DER to PEM
#!/bin/bash
aws kms get-public-key --key-id alias/shorebird-signing --output text \
  --query PublicKey | base64 -d | openssl rsa -pubin -inform DER -outform PEM

# Create a helper script for signing (aws-kms-sign.sh)
#!/bin/bash
HASH=$(cat - | openssl dgst -sha256 -binary | base64)
aws kms sign --key-id alias/shorebird-signing \
  --signing-algorithm RSASSA_PKCS1_V1_5_SHA_256 \
  --message-type DIGEST --message "$HASH" \
  --output text --query Signature

# Release
shorebird release android --public-key-cmd="./aws-kms-pubkey.sh"

# Patch
shorebird patch android --track=stable \
  --public-key-cmd="./aws-kms-pubkey.sh" \
  --sign-cmd="./aws-kms-sign.sh"
```

### Azure Key Vault

```
# Create a key in Azure Key Vault
az keyvault key create --vault-name myVault --name shorebird-signing \
  --kty RSA --size 2048

# Helper script for public key (azure-kv-pubkey.sh)
#!/bin/bash
az keyvault key download --vault-name myVault --name shorebird-signing \
  --encoding PEM --file /dev/stdout

# Helper script for signing (azure-kv-sign.sh)
#!/bin/bash
HASH=$(cat - | openssl dgst -sha256 -binary | base64 -w0)
az keyvault key sign --vault-name myVault --name shorebird-signing \
  --algorithm RS256 --digest "$HASH" --query value -o tsv

# Release
shorebird release android --public-key-cmd="./azure-kv-pubkey.sh"

# Patch
shorebird patch android --track=stable \
  --public-key-cmd="./azure-kv-pubkey.sh" \
  --sign-cmd="./azure-kv-sign.sh"
```

### 1Password CLI

```
# Store your keys in 1Password
op item create --category="Secure Note" --title="Shorebird Signing" \
  "public_key[text]=$(cat public.pem)" \
  "private_key[text]=$(cat private.pem)"

# Helper script for signing (1password-sign.sh)
#!/bin/bash
PRIVATE_KEY=$(op item get "Shorebird Signing" --fields private_key)
cat - | openssl dgst -sha256 -sign <(echo "$PRIVATE_KEY") | base64

# Release
shorebird release android \
  --public-key-cmd="op item get 'Shorebird Signing' --fields public_key"

# Patch
shorebird patch android --track=stable \
  --public-key-cmd="op item get 'Shorebird Signing' --fields public_key" \
  --sign-cmd="./1password-sign.sh"
```
