Skip to main content

Bundle Signing

OTA updates let you ship JavaScript changes directly to devices. Bundle signing ensures those updates came from you and have not been tampered with. The SDK cryptographically verifies every update before applying it.

Think of it like a wax seal on a letter -- if the seal is broken, you know someone tampered with it.

Why Signing Matters

Without signing, SwiftPatch verifies integrity with SHA-256 hashes -- but those hashes are server-generated. Signing adds end-to-end trust anchored to a key only you control:

ThreatWithout SigningWith Signing
Corrupted downloadProtected (hash check)Protected (hash + JWT)
Man-in-the-middle attackVulnerableProtected
Compromised CDN or serverVulnerableProtected
Supply chain tamperingVulnerableProtected
warning

If your app handles payments, auth tokens, or personal data, signing is a baseline security requirement.

How It Works

SwiftPatch uses JWT RS256 directory signing with RSA key pairs.

Signing (at deploy time)

  1. Generate manifest -- SHA-256 hash of every file in the bundle
  2. Compute package hash -- Sort manifest, serialize as JSON, SHA-256 the result
  3. Sign with JWT -- Create a JWT with the package hash, signed with your RSA private key
  4. Embed -- Write the JWT to .swiftpatchsigned inside the bundle ZIP

Verification (on device)

  1. Extract .swiftpatchsigned JWT from the downloaded bundle
  2. Verify JWT signature with the public key in your app
  3. Recompute the package hash independently
  4. Compare hashes -- if they match, apply the update; if not, reject it

Setup

Step 1: Generate a Key Pair

swiftpatch generate-key-pair -o ./keys

This creates:

  • ./keys/private.pem -- Signs bundles. Keep this secret.
  • ./keys/public.pem -- Embedded in your app for verification.

Or use OpenSSL directly:

openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem

Step 2: Configure the SDK

import { withSwiftPatch } from '@swiftpatch/react-native';

export default withSwiftPatch(App, {
publicKey: `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----`,
});

Once set, the SDK rejects any unsigned or incorrectly signed update.

Step 3: Sign Your Releases

swiftpatch deploy -p ios --hermes --private-key ./keys/private.pem

Security Best Practices

  • Never commit private keys to version control. Add *.pem to .gitignore.
  • Use a secrets manager (AWS Secrets Manager, Vault, etc.) to store keys.
  • Use different keys for different environments.
  • Rotate keys periodically. See the Key Rotation guide.
Private Key Compromise

If your private key is compromised:

  1. Generate a new key pair
  2. Update your app with the new public key and publish a new binary
  3. Update CI/CD secrets with the new private key
  4. Sign all future updates with the new key

Until users update to the new binary, they trust the old key. Prioritize getting the new binary through app review.

CI/CD Integration

Automate signing in your pipeline so every deploy is signed and no developer needs the private key directly:

swiftpatch deploy -p ios --hermes --private-key ${{ secrets.SWIFTPATCH_PRIVATE_KEY_PATH }}

See the CI/CD guide.