---
title: Rotating keys
description: The following sections give an overview of how to manage rotation of encryption keys and signing keys, and include examples for key rotation based on use cases from the Gateway guide.
component: pinggateway
version: 7.2
page_id: pinggateway:maintenance-guide:rotate-keys
canonical_url: https://docs.pingidentity.com/pinggateway/2026/maintenance-guide/rotate-keys.html
llms_txt: https://docs.pingidentity.com/pinggateway/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
revdate: 2024-09-18T12:36:34Z
superseded_by: https://docs.pingidentity.com/pinggateway/2026/maintenance-guide/rotate-keys.html
section_ids:
  rotating-keys-about: About key rotation
  why_and_when_to_rotate_keys: Why and when to rotate keys
  steps_for_rotating_symmetric_keys: Steps for rotating symmetric keys
  steps_for_rotating_asymmetric_keys: Steps for rotating asymmetric keys
  key_rotation_for_keys_in_a_jwk_set: Key rotation for keys in a JWK set
  rotating-keys-signed-sat: Rotating keys for stateless access tokens signed with a KeyStoreSecretStore
  rotating-keys-jwtsession: Rotating keys in a shared JWT session
---

# Rotating keys

The following sections give an overview of how to manage rotation of encryption keys and signing keys, and include examples for key rotation based on use cases from the [Gateway guide](../gateway-guide/preface.html#preface).

## About key rotation

Key rotation is the process of generating a new version of a key, assigning that version as the *active* key to encrypt or sign new messages, or as a *valid* key to decrypt or validate messages, and then deprovisioning the old key.

### Why and when to rotate keys

Regular key rotation is a security consideration that is sometimes required for internal business compliance. Regularly rotate keys to:

* Limit the amount of data protected by a single key.

* Reduce dependence on specific keys, making it easier to migrate to stronger algorithms.

* Prepare for when a key is compromised. The first time you try key rotation shouldn't be during a real-time recovery.

Key revocation is a type of key rotation, done exceptionally if you suspect that a key has been compromised. To decide when to revoke a key, consider the following points:

* If limited use of the old keys can be tolerated, provision the new keys and then deprovision the old keys. Messages produced before the new keys are provisioned are impacted.

* If use of the old keys cannot be tolerated, deprovision the old keys before you provision the new keys. The system is unusable until new keys are provisioned.

### Steps for rotating symmetric keys

The following steps outline key rotation and revocation for symmetric keys managed by a KeyStoreSecretStore. For an example, see [Rotating keys in a shared JWT session](#rotating-keys-jwtsession).

1. Using OpenSSL, Keytool, or another key creation mechanism, create the new symmetric key. The keystore should contain the old key and the new key.

2. Provision the new key.

   1. In the `mappings` property of KeyStoreSecretStore, add the alias for the new key after the alias for the old key. The new key is now valid. Because the old key is the first key in the list, it is the active key.

   2. Move the new key to be the first key in the list. The new key is now the active key.

3. Deprovision the old key.

   To ensure that no messages or users are impacted, wait until messages encrypted or signed with the old key are out of the system before you deprovision the old key.

   1. In the `mappings` property of KeyStoreSecretStore, delete the alias for the old key. The old key can no longer be used.

   2. Using OpenSSL, Keytool, or another key creation mechanism, delete the old symmetric key.

### Steps for rotating asymmetric keys

The following steps outline the process for key rotation and revocation for asymmetric keys managed by a KeyStoreSecretStore or HsmSecretStore. For an example, see [Rotating keys for stateless access tokens signed with a KeyStoreSecretStore](#rotating-keys-signed-sat).

1. Create new asymmetric keys for signing and encryption, using OpenSSL, Keytool, or another key creation mechanism.

2. Provision the message consumer with the private portion of the new encryption key, and the public portion of the new signing key.

   The message consumer can now decrypt and verify messages with the old key and the new key.

3. Provision the message producer, with the public portion of the new encryption key, and the private portion of the signing key. The message producer starts encrypting and signing messages with the new key, and stops using the old key.

4. Deprovision the message consumer with the private portion of the old encryption key, and the public portion of the old signing key. The message consumer can no longer decrypt and verify messages with the old key.

   To ensure that no messages or users are impacted, wait until messages encrypted or signed with the corresponding old key are out of the system before you deprovision the old key.

5. Deprovision the message producer, with the public portion of the old encryption key, and the private portion of old signing key.

### Key rotation for keys in a JWK set

When keys are provided by a JWK Set from AM, the key rotation is transparent to IG. AM generates a key ID (`kid`) for each key it exposes at the `jwk_uri`. For more information, see [Mapping and rotating secrets](https://docs.pingidentity.com/pingam/7.2/security-guide/secret-mapping.html) in AM's *Security guide*.

When IG processes a request with a JWT containing a `kid`, IG uses the `kid` to identify the key in the JWK Set. If the `kid` is available at the `jwk_uri` on AM, IG processes the request. Otherwise, IG tries all compatible secrets from the JWK Set. If none of the secrets work, the JWT is rejected.

## Rotating keys for stateless access tokens signed with a KeyStoreSecretStore

This example extends the example in [Validate signed access tokens with the StatelessAccessTokenResolver and KeyStoreSecretStore](../gateway-guide/oauth2-rs.html#oauth2-rs-stateless-signed-ksss) to rotate the keys that sign an access token and verify the signature.

Rotate Keys For Stateless Access Tokens Signed With a KeyStoreSecretStore

Before you start, set up and test the example in [Validate signed access tokens with the StatelessAccessTokenResolver and KeyStoreSecretStore](../gateway-guide/oauth2-rs.html#oauth2-rs-stateless-signed-ksss).

1. Set up the new keys:

   1. Generate a new private key called `signature-key-new`, and a corresponding public certificate called `x509certificate-new.pem`:

      ```bash
      $ openssl req -x509 \
      -newkey rsa:2048 \
      -nodes \
      -subj "/CN=ig.example.com/OU=example/O=com/L=fr/ST=fr/C=fr" \
      -keyout keystore_directory/signature-key-new.key \
      -out keystore_directory/x509certificate-new.pem \
      -days 365

      ... writing new private key to 'keystore_directory/signature-key-new.key'
      ```

   2. Convert the private key and certificate files into a new PKCS12 keystore file:

      ```bash
      $ openssl pkcs12 \
      -export \
      -in keystore_directory/x509certificate-new.pem \
      -inkey keystore_directory/signature-key-new.key \
      -out keystore_directory/keystore-new.p12 \
      -passout pass:password \
      -name signature-key-new
      ```

   3. List the keys in the new keystore:

      ```bash
      $ keytool -list \
      -keystore "keystore_directory/keystore-new.p12" \
      -storepass "password" \
      -storetype PKCS12

      ...
      Your keystore contains 1 entry
      Alias name: signature-key-new
      ```

   4. Import the new keystore into `keystore.p12`, so that `keystore.p12` contains both keys:

      ```bash
      $ keytool -importkeystore
      -srckeystore keystore_directory/keystore-new.p12
      -srcstoretype pkcs12
      -srcstorepass password
      -destkeystore keystore_directory/keystore.p12
      -deststoretype pkcs12
      -deststorepass password

      Entry for alias signature-key-new successfully imported ...
      ```

   5. List the keys in `keystore.p12`, to make sure that it contains the new and old keys:

      ```bash
      $ keytool -list \
      -keystore "keystore_directory/keystore.p12" \
      -storepass "password" \
      -storetype PKCS12

      ...
      Your keystore contains 2 entries
      Alias name: signature-key
      Alias name: signature-key-new
      ```

2. Set up AM:

   1. Copy the updated keystore to AM:

      1. Copy `keystore.p12` to AM:

         ```bash
         $ cp keystore_directory/keystore.p12 am_keystore_directory/AM_keystore.p12
         ```

      2. List the keys in the updated AM keystore:

         ```bash
         $ keytool -list \
         -keystore "am_keystore_directory/AM_keystore.p12" \
         -storepass "password" \
         -storetype PKCS12

         ...
         Your keystore contains 2 entries
         Alias name: signature-key
         Alias name: signature-key-new
         ```

      3. Restart AM to update the keystore cache.

   2. Update the KeyStoreSecretStore on AM:

      1. In AM, select [icon: eye-slash, set=fa]Secret Stores > keystoresecretstore.

      2. Select the Mappings tab, and in `am.services.oauth2.stateless .signing.RSA` add the alias `signature-key-new`.

         The mapping now contains two aliases, but the alias `signature-key` is still the active alias. AM still uses `signature-key` to sign tokens.

      3. Drag `signature-key-new` above `signature-key`.

         AM now uses `signature-key-new` to sign tokens.

3. Set up IG:

   1. Import the public certificate to the IG keystore, with the alias `verification-key-new`:

      ```bash
      $ keytool -import \
      -trustcacerts \
      -rfc \
      -alias verification-key-new \
      -file "keystore_directory/x509certificate-new.pem" \
      -keystore "ig_keystore_directory/IG_keystore.p12" \
      -storetype PKCS12 \
      -storepass "password"

      ...
      Trust this certificate? [no]:  yes
      Certificate was added to keystore
      ```

   2. List the keys in the IG keystore:

      ```bash
      $ keytool -list \
      -keystore "ig_keystore_directory/IG_keystore.p12" \
      -storepass "password" \
      -storetype PKCS12

      ...
      Your keystore contains 2 entries
      Alias name: verification-key
      Alias name: verification-key-new
      ```

   3. In `rs-stateless-signed-ksss.json`, edit the KeyStoreSecretStore mapping with the new verification key:

      ```json
      "mappings": [
        {
          "secretId": "stateless.access.token.verification.key",
          "aliases": [ "verification-key", "verification-key-new" ]
        }
      ]
      ```

      If the Router `scanInterval` is disabled, restart IG to reload the route.

      IG can now check the authenticity of access tokens signed with `verification-key`, the old key, and `verification-key-new`, the new key. However, AM signs with the old key.

4. Test the setup:

   1. Get an access token for the demo user, using the scope `myscope`:

      ```bash
      $ mytoken=$(curl -s \
      --user "client-application:password" \
      --data "grant_type=password&username=demo&password=Ch4ng31t&scope=myscope" \
      http://am.example.com:8088/openam/oauth2/access_token | jq -r ".access_token")
      ```

   2. Display the token:

      ```bash
      $ echo ${mytoken}
      ```

   3. Access the route by providing the token returned in the previous step:

      ```bash
      $ curl -v http://ig.example.com:8080/rs-stateless-signed-ksss --header "Authorization: Bearer ${mytoken}"

      ...
      Decoded access_token: {
      sub=demo,
      cts=OAUTH2_STATELESS_GRANT,
      ...
      ```

Deprovision Old Keys

1. Remove `signature-key` from the AM keystore:

   1. Delete the key from the keystore:

      ```bash
      $ keytool -delete \
      -keystore "am_keystore_directory/AM_keystore.p12" \
      -storepass "password" \
      -alias signature-key
      ```

   2. List the keys in the AM keystore to make sure that `signature-key` is removed:

      ```bash
      $ keytool -list \
      -keystore "am_keystore_directory/AM_keystore-new.p12" \
      -storepass "password" \
      -storetype PKCS12
      ```

   3. Restart AM.

2. Remove `verification-key` from the IG keystore:

   1. Delete the key from the keystore:

      ```bash
      $ keytool -delete \
      -keystore "ig_keystore_directory/IG_keystore.p12" \
      -storepass "password" \
      -alias verification-key
      ```

   2. List the keys in the IG keystore to make sure that `verification-key` is removed:

      ```bash
      $ keytool -list \
      -keystore "ig_keystore_directory/IG_keystore.p12" \
      -storepass "password" \
      -storetype PKCS12
      ```

3. In AM, delete the mapping for `signature-key` from `keystoresecretstore`.

4. In IG, delete the mapping for `verification-key` from the route `rs-stateless-signed-ksss.json`. If the Router `scanInterval` is disabled, restart IG to reload the route.

## Rotating keys in a shared JWT session

This section builds on the example in [Share JWT Session Between Multiple Instances of IG](../installation-guide/jwtsession-using.html#jwtsession-sharesecrets) to rotate a key used in a shared JWT session.

When a JWT session is shared between multiple instances of IG, the instances are able to share the session information for load balancing and failover.

Before you start, set up the example in [Set Up Shared Secrets for Multiple Instances of IG](../installation-guide/jwtsession-using.html#proc-jwtsession-sharesecrets), where three instances of IG share a JwtSession and use the same authenticated encryption key. Instance 1 acts as a load balancer, and generates a session. instances 2 and 3 access the session information.

1. Test the setup with the existing key, `symmetric-key`:

   1. Access instance 1 to generate a session:

      ```bash
      $ curl -v http://ig.example.com:8001/log-in-and-generate-session

      GET /log-in-and-generate-session HTTP/1.1
      ...

      HTTP/1.1 200 OK
      Content-Length: 84
      Set-Cookie: IG=eyJ...HyI; Path=/; Domain=.example.com; HttpOnly
      ...
      Sam Carter logged IN. (JWT session generated)
      ```

   2. Using the JWT cookie returned in the previous step, access instance 2:

      ```bash
      $ curl -v http://ig.example.com:8001/webapp/browsing?one --header "cookie:IG=<JWT cookie>"

      GET /webapp/browsing?one HTTP/1.1
      ...
      cookie: IG=eyJ...QHyI
      ...
      HTTP/1.1 200 OK
      ...
      Hello, Sam Carter !! (instance2)
      ```

      Note that instance 2 can access the session info.

   3. Using the JWT cookie again, access instance 3:

      ```bash
      $ curl -v http://ig.example.com:8001/webapp/browsing?two --header "Cookie:IG=<JWT cookie>"

      GET /webapp/browsing?two HTTP/1.1
      ...
      cookie: IG=eyJ...QHyI
      ...
      HTTP/1.1 200 OK
      ...
      Hello, Sam Carter !! (instance3)
      ```

      Note that instance 3 can access the session info.

2. Commission a new key:

   1. Generate a new encryption key, called `symmetric-key-new`, in the existing keystore:

      ```bash
      $ keytool \
      -genseckey \
      -alias symmetric-key-new
      -keystore /path/to/secrets/jwtsessionkeystore.pkcs12 \
      -storepass password \
      -storetype PKCS12 \
      -keyalg HmacSHA512 \
      -keysize 512
      ```

   2. Make sure that the keystore contains the old key and the new key:

      ```bash
      $ keytool \
      -list \
      -keystore /path/to/secrets/jwtsessionkeystore.pkcs12 \
      -storepass password \
      -storetype PKCS12

      ...
      Your keystore contains 2 entries
      symmetric-key, ...
      symmetric-key-new ...
      ```

   3. Add the key alias to `instance1-loadbalancer.json`, `instance2-retrieve-session-username.json`, and `instance3-retrieve-session-username.json`, for each IG instance, as follows:

      ```json
      "mappings": [{
        "secretId": "jwtsession.encryption.secret.id",
        "aliases": ["symmetric-key", "symmetric-key-new"]
      }]
      ```

      If the Router `scanInterval` is disabled, restart IG to reload the route.

      The active key is `symmetric-key`, and the valid key is `symmetric-key-new`.

   4. Test the setup again, as described in step 1, and make sure that instances 2 and 3 can still access the session information.

3. Make the new key the active key for generating sessions:

   1. In `instance1-loadbalancer.json`, change the order of the keys to make `symmetric-key-new` the active key, and `symmetric-key` the valid key:

      ```json
      "mappings": [{
        "secretId": "jwtsession.encryption.secret.id",
        "aliases": ["symmetric-key-new", "symmetric-key"]
      }]
      ```

      Don't change `instance2-retrieve-session-username.json` or `instance3-retrieve-session-username.json`.

   2. Test the setup again, as described in step 1, and make sure that instances 2 and 3 can still access the session information.

      Instance 1 creates the session using the new active key, `symmetric-key-new`.

      Because `symmetric-key-new` is declared as a valid key in instances 2 and 3, the instances can still access the session. It isn't necessary to make `symmetric-key-new` the active key.

4. Decommission the old key:

   1. Remove the old key from all of the routes, as follows:

      ```json
      "mappings": [{
        "secretId": "jwtsession.encryption.secret.id",
        "aliases": ["symmetric-key-new"]
      }]
      ```

      Key `symmetric-key-new` is the only key in the routes.

   2. Remove the old key, `symmetric-key`, from the keystore:

      1. Delete `symmetric-key`:

         ```bash
         $ keytool \
         -delete \
         -alias symmetric-key \
         -keystore /path/to/secrets/jwtsessionkeystore.pkcs12 \
         -storepass password \
         -storetype PKCS12 \
         -keypass password
         ```

      2. Make sure that the keystore contains only `symmetric-key-new`:

         ```bash
         $ keytool \
         -list \
         -keystore /path/to/secrets/jwtsessionkeystore.pkcs12 \
         -storepass password \
         -storetype PKCS12

         ...
         Your keystore contains 1 entry
         symmetric-key-new ...
         ```

   3. Test the setup again, as described in step 1, and make sure that instances 2 and 3 can still access the session information.
