---
title: EAS Update Code Signing
description: Learn how code signing and key rotation work in EAS Update.
sidebar_title: Code Signing
---

import { Terminal } from '~/ui/components/Snippet';
import { Step } from '~/ui/components/Step';

> **info** EAS Update Code Signing is only available to accounts subscribed to the EAS Enterprise plan. [Sign up](https://expo.dev/pricing).

## Introduction

The `expo-updates` library supports end-to-end code signing. Code signing allows developers to cryptographically sign their updates with their own keys. The signatures are then verified on the client before the update is applied, which ensures ISPs, CDNs, cloud providers, and even EAS itself cannot tamper with updates run by apps.

## Code Signing with EAS Update

<Step label="1">

Generate a private key and corresponding code signing certificate for your app:

<Terminal
  cmd={[
    'npx expo-updates codesigning:generate \\',
    '  --key-output-directory keys \\',
    '  --certificate-output-directory certs \\',
    '  --certificate-validity-duration-years 10 \\',
    '  --certificate-common-name "My App"',
  ]}
  cmdCopy='npx expo-updates codesigning:generate --key-output-directory keys --certificate-output-directory certs --certificate-validity-duration-years 10 --certificate-common-name "My App"'
/>

- The generated private key must be kept private and secure.
- The certificate validity duration is a setting that may vary based on the security needs of your app.
  - A shorter validity duration will require [key rotation](#key-rotation) more frequently but is considered better practice since a compromised private key will have a sooner expiration which limits exposure.
  - Shorter validity durations add overhead to your app's release process as the key must be rotated more frequently. Binaries with expired certificates won't apply new updates.
  - For example, Expo sets this value to 20 years for the public Expo Go app, but only 1 year for internal apps with binaries that are distributed more frequently. We plan to rotate our keys every 10 years.

</Step>

<Step label="2">

Configure your app's builds to use code signing:

<Terminal
  cmd={[
    'npx expo-updates codesigning:configure \\',
    '  --certificate-input-directory certs \\',
    '  --key-input-directory keys',
  ]}
  cmdCopy="npx expo-updates codesigning:configure --certificate-input-directory certs --key-input-directory keys"
/>

After this step, create a new build with a new runtime version. The code signing certificate will be embedded in this new build.

</Step>

<Step label="3">

Publish a signed update for your app:

<Terminal cmd={['eas update --private-key-path keys/private-key.pem']} />

During `eas update`, the EAS CLI automatically detects that code signing is configured for your app. It then verifies the integrity of the update and creates a digital signature using your private key. This process is performed locally so that your private key never leaves your machine. The generated signature is automatically sent to EAS to store alongside the update.

</Step>

<Step label="4">

Download the update on the client (this step is done automatically by the library). The build from
step (2) that is configured for code signing checks if there is a new update available. The server
responds with the update published in step (3) and its generated signature. After being downloaded
but before being applied, the update is verified against the embedded certificate and included
signature. The update is applied if the certificate and signature are valid, and rejected
otherwise.

</Step>

## Key Rotation

Key rotation is the process by which the key pair used for signing updates is changed. This is most commonly done in a few cases:

- Key expiration. In step (1) from the section above, we set `certificate-validity-duration-years` to 10 years (though it can be configured to any value). This means that after 10 years, updates signed with the private key corresponding to the certificate will no longer be applied after being downloaded by the app. Updates downloaded prior to the expiration of their signing certificate will continue to function normally. Rotating keys well before the certificate expires helps to preempt any potential key expiration issues and helps to guarantee all users are using the new certificate before the old certificate expires.
- Private key compromise. If the private key used to sign updates is accidentally exposed to the public, it can no longer be considered secure and therefore can no longer guarantee integrity of updates it signed. For example, a malicious actor could craft a malicious update and sign it with the leaked private key.
- Key rotation for security best practices. It is best practice to rotate keys periodically to ensure that a system is resilient to manual key rotation in response to one of the other reasons above.

In any of these cases, the procedure is similar:

1. Backup the old key and certificate that were generated in step (1) above.
2. Generate a new key by following the steps above. To assist in debugging, you may wish to change the `keyid` of the new key by modifying the `updates.codeSigningMetadata.keyid` field in your app config (**app.json**).
3. The certificate is part of the app's runtime, so a new runtime version should be set for builds using this certificate to ensure that only updates signed with the new key run in the new build.
4. Publish signed updates using the new key by following step (3) above.

## Removing Code Signing

The process of removing code signing from an app is similar to [key rotation](#key-rotation) and can be thought of as a key rotation to a `null` key.

1. Backup the old key and certificate that were generated in step (1) above.
2. Remove the `updates.codeSigningMetadata` field from your app config (**app.json**).
3. The new certificate-less app is a new distinct runtime, so a new runtime version should be set for builds to ensure that only unsigned updates run in the new build.
