Secrets

IG uses the ForgeRock Commons Secrets Service to manage secrets, such as passwords and cryptographic keys.

Repositories of secrets are managed through secret stores, provided to the configuration by the SecretsProvider object or secrets object. For more information about these objects and the types of secret stores provided in IG, see "SecretsProvider" and Secret Stores.

Secret Names and Types

The following terms are used to describe secrets:

  • Secret ID: A label to indicate the purpose of a secret. A secret ID is generally associated with one or more aliases of a key in a keystore or HSM.

  • Stable ID: A label to identify a secret. The stable ID corresponds to the following values in each type of secret store:

    • Base64EncodedSecretStore: The value of secret-id in the "secret-id": "string" pair.

    • FileSystemSecretStore: The filename of a file in the specified directory, without the prefix/suffix defined in the store configuration.

    • HsmSecretStore: The value of an alias in a secret-id/aliases mapping.

    • JwkSetSecretStore: The value of the kid of a JWK stored in a JwkSetSecretStore.

    • KeyStoreSecretStore: The value of an alias in a secret-id/aliases mapping.

    • SystemAndEnvSecretStore: The name of a system property or environment. variable

  • Valid secret: A secret whose purpose matches the secret ID.

  • Named secret: A valid secret that a secret store can find by using a secret ID and stable ID.

  • Active secret: One of the valid secrets that is considered eligible at the time of use.

Validating the Signature of Signed Tokens

IG validates the signature of signed tokens as follows:

  • Named secret resolution:

    • If the JWT contains a kid, IG queries the secret stores declared in secretsProvider or secrets to find a named secret, identified by a secret ID and stable ID.

    • If a named secret is found, IG then uses the named secret to try to validate the signature. If the named secret can't validate the signature, the token is considered as invalid.

    • If a named secret isn't found, IG tries valid secret resolution.

  • Valid secret resolution:

    • IG uses the value of verificationSecretId as the secret ID, and queries the declared secret stores to find all secrets that match the provided secret ID.

    • All matching secrets are returned as valid secrets, in the order that the secret stores are declared, and for KeyStoreSecretStore and HsmSecretStore, in the order defined by the mappings.

    • IG tries to verify the signature with each valid secret, starting with the first valid secret, and stopping when it succeeds.

    • If no valid secrets are returned, or if none of the valid secrets can verify the signature, the token is considered as invalid.

Validating the Signature of Signed Tokens by Using a KeyStoreSecretStore

In the following example, a StatelessAccessTokenResolver validates a signed access_token by using a KeyStoreSecretStore:

"accessTokenResolver": {
  "type": "StatelessAccessTokenResolver",
  "config": {
    "secretsProvider": {
      "type": "KeyStoreSecretStore",
      "config": {
        "file": "IG_keystore.p12",
        "storeType": "PKCS12",
        "storePassword": "keystore.secret.id",
        "keyEntryPassword": "keystore.secret.id",
        "mappings": [{
          "secretId": "verification.secret.id",
          "aliases": [ "verification.key.1", "verification.key.2" ]
        }]
      },
      "issuer": "http://openam.example.com:8088/openam/oauth2",
      "verificationSecretId": "verification.secret.id"
    }
  }
}

The JWT signature is validated as follows:

  • If the JWT contains a kid with a mapped value, for example verification.key.1:

    • The secrets provider queries the KeyStoreSecretStore for a named secret with the secret ID verification.secret.id and the stable ID verification.key.1.

    • Because the KeyStoreSecretStore contains that mapping, the KeyStoreSecretStore returns a named secret.

    • The StatelessAccessTokenResolver tries to validate the JWT signature with the named secret. If it fails, the token is considered as invalid.

  • If the JWT contains a kid with an unmapped value, for example, verification.key.3:

    • The secrets provider queries the KeyStoreSecretStore for a named secret with the secret ID verification.secret.id and the stable ID verification.key.3.

    • Because the KeyStoreSecretStore doesn't contain that mapping, named secret resolution fails. IG tries valid secret resolution in the same way as when the JWT doesn't contain a kid.

  • If the JWT doesn't contain a kid:

    • The secrets provider queries the KeyStoreSecretStore for all valid secrets, whose alias is mapped to the secret ID verification.secret.id. There are two valid secrets, with aliases verification.key.1 and verification.key.2.

    • The StatelessAccessTokenResolver first tries to verify the signature with verification.key.1. If that fails, it tries verification.key.2.

    • If neither of the valid secrets can verify the signature, the token is considered as invalid.


Validating the Signature of Signed Tokens With a JwkSetSecretStore

In the following example, a StatelessAccessTokenResolver validates a signed access_token by using a JwkSetSecretStore:

"accessTokenResolver": {
  "type": "StatelessAccessTokenResolver",
  "config": {
    "secretsProvider": {
      "type": "JwkSetSecretStore",
      "config": {
        "jwkUrl": "http://openam.example.com:8088/openam/oauth2/connect/jwk_uri"
      },
      "issuer": "http://openam.example.com:8088/openam/oauth2",
      "verificationSecretId": "verification.secret.id"
    }
  }
}

The JWT signature is validated as follows:

  • If the JWT contains a kid with a matching secret in the JWK set:

    • The secrets provider queries the JwkSetSecretStore for a named secret.

    • The JwkSetSecretStore returns the matching secret, identified by a stable ID.

    • The StatelessAccessTokenResolver tries to validate the signature with that named secret. If it fails, the token is considered as invalid.

    In the route, note that the property verificationSecretId must be configured but is not used in named secret resolution.

  • If the JWT contains a kid without a matching secret in the JWK set:

    • The secrets provider queries the JwkSetSecretStore for a named secret.

    • Because the referenced JWK set doesn't contain a matching secret, named secret resolution fails. IG tries valid secret resolution in the same way as when the JWT doesn't contain a kid.

  • If the JWT doesn't contain a kid:

    • The secrets provider queries the JwkSetSecretStore for list of valid secrets, whose secret ID is verification.secret.id.

    • The JwkSetSecretStore returns all secrets in the JWK set whose purpose is signature verification. For example, signature verification keys can have the following JWK parameters:

      {
        "use": "sig"
      }

      {
        "key_opts": [ "verify" ]
      }

      Secrets are returned in the order that they are listed in the JWK set.

    • The StatelessAccessTokenResolver tries to validate the signature with each secret sequentially, starting with the first, and stopping when it succeeds.

    • If none of the valid secrets can verify the signature, the token is considered as invalid.


Using Multiple Secret Stores in a Configuration

When multiple secrets stores are provided in a configuration, the secrets stores are queried in the following order:

  • Locally in the route, starting with the first secret store in the list, up to the last.

  • In ascending parent routes, starting with the first secret store in each list, up to the last.

  • In config.json, starting with the first secret store in the list, up to the last.

  • If a secrets store is not configured in config.json, the secret is queried in a default SystemAndEnvSecretStore, and a base64-encoded value is expected.

  • If a secret is not resolved, an error is produced.

Secrets stores defined in admin.json can be accessed only by heap objects in admin.json.

Algorithms for Elliptic Curve Digital Signatures

When the Elliptic Curve Digital Signature Algorithm (ECDSA) is used for signing, and both of the following conditions are met, JWTs are signed with a deterministic ECDSA:

  • Bouncy Castle is installed.

  • The system property org.forgerock.secrets.preferDeterministicEcdsa is true, which is its default value.

Otherwise, when ECDSA is used for signing, JWTs are signed with a non-deterministic ECDSA.

A non-deterministic ECDSA signature can be verified by the equivalent deterministic algorithm.

For information about deterministic ECDSA, see RFC 6979. For information about Bouncy Castle, see The Legion of the Bouncy Castle.

Read a different version of :