Identity Gateway 2024.3

KeyStoreSecretStore

Manages a secret store for cryptographic keys and certificates, based on a standard Java keystore.

Legacy keystore types such as JKS and JCEKS are supported but are not secure. Consider using the PKCS#12 keystore type.

This Secret store can only manage secrets of the CryptoKey type.

The secrets provider queries the KeyStoreSecretStore for a named secret, identified by a secret ID and a stable ID, corresponding to the secret-id/aliases mapping. The KeyStoreSecretStore returns a secret that exactly matches the name, and whose purpose matches the secret ID and any purpose constraints.

The secrets provider builds the secret, checking that the secret’s constraints are met, and returns a unique secret. If the secret’s constraints are not met, the secrets provider cannot build the secret and the secret query fails.

For a description of how secrets are managed, refer to About secrets

Usage

{
  "name": string,
  "type": "KeyStoreSecretStore",
  "config": {
    "file": configuration expression<string>,
    "storeType": configuration expression<string>,
    "storePasswordSecretId": configuration expression<string>,
    "entryPasswordSecretId": configuration expression<string>,
    "secretsProvider": SecretsProvider reference,
    "mappings": [ object, ... ],
    "leaseExpiry": configuration expression<duration>,
    "autoRefresh": object,
    "storePassword": configuration expression<string>, //deprecated
    "keyEntryPassword": configuration expression<string> //deprecated
   }
}

Properties

"file": configuration expression<string>, required

The path to the KeyStore file.

"storeType": configuration expression<string>, optional

The secret store type.

Default: PKCS12

"storePasswordSecretId": configuration expression<secret-id>, optional

The secret ID of the password to access the KeyStore.

This secret ID must point to a GenericSecret.

IG searches for the value of the password until it finds it, first locally, then in parent routes, then in config.json.

To create a store password, add a file containing the password. The filename must corresponds to the secret ID, and the file content must contain only the password, with no trailing spaces or carriage returns.

Default: None; the KeyStore is not password-protected

"entryPasswordSecretId": configuration expression<secret-id>, optional

The secret ID of the password to access entries in the KeyStore.

This secret ID must point to a GenericSecret.

To create an entry password, add a file containing the password. The filename must correspond to the secret ID, and the file content must contain only the password, with no trailing spaces or carriage returns.

When this property is used, the password must be the same for all entries in the KeyStore. If the KeyStore uses different passwords for entries, entryPasswordSecretId doesn’t work.

Default: The value of storePasswordSecretId

"secretsProvider": SecretsProvider reference, required

The SecretsProvider to query for the keystore password and key entry password.

"mappings": array of objects, required

One or more mappings of one secret ID to one or more aliases.

"mappings" : {
  "secretId": configuration expression<secret-id>,
  "aliases": array of configuration expression<string>, //use aliases or
  "aliasesMatching": [ string, ... ] //aliasesMatching but not both
}
"secretId": configuration expression<secret-id>, required

The secret ID of the key.

"aliases": array of configuration expression<strings>, required if aliasesMatching is not used

One or more key aliases. Named aliases are mapped to the secret ID.

Use aliases or aliasesMatching but not both.

"aliasesMatching": array of <strings>, required if aliases is not used

One or more regular expressions to match key aliases. Aliases that match the expressions are mapped to the secret ID.

Use aliases or aliasesMatching but not both.

Some KeyStores, such as a global Java TrustStore, can contain hundreds of valid certificates. Use this property to map multiple aliases to a secret ID without listing them all in the mapping.

The secret store uses the mappings as follows:

  • When the secret is used to create signatures or encrypt values, the secret store uses the active secret, the first alias in the list.

  • When the secret is used to verify signatures or decrypt data, the secret store tries all of the mapped aliases in the list, starting with the first, and stopping when it finds a secret that can successfully verify signature or decrypt the data.

The following example maps the named aliases to the named secret IDs:

"mappings": [
  {
    "secretId": "id.key.for.signing.jwt",
    "aliases": [ "signingkeyalias", "anothersigningkeyalias" ]
  },
  {
    "secretId": "id.key.for.encrypting.jwt",
    "aliases": ["encryptionkeyalias"]
  }
]

The following example maps aliases that match the regular expression .* to the named secret ID:

"mappings": [
  {
    "secretId": "id.key.for.signing.jwt",
    "aliasesMatching": [".*"]
  }
]
secretId: configuration expression<secret-id>, required

The ID of the secret used in your configuration.

aliases: array of configuration expression<strings>, required

One or more aliases for the secret ID. :leveloffset: +2

"leaseExpiry": configuration expression<duration>, optional

The amount of time that secrets produced by this store can be cached before they must be refreshed.

If the duration is zero or unlimited, IG issues a warning, and uses the default value.

Default: 5 minutes

"autoRefresh": object, optional

Automatically reload the KeystoreSecretStore when the keystore is edited or deleted.

{
  "enabled": configuration expression<boolean>,
  "executor": ScheduledExecutorService reference
}
enabled: configuration expression<boolean>, optional

Flag to enable or disable automatic reload:

  • true: Enable

  • false: Disable

Default: true

"executor": ScheduledExecutorService reference, optional

A ScheduledExecutorService to monitor the keystore.

Default: The default ScheduledExecutorService in the heap

"storePassword": configuration expression<secret-id>, required
This property is deprecated. If the KeyStore is password-protected, use storePasswordSecretId. For more information, refer to the Deprecated section of the Release Notes.

The secret ID of the password to access the KeyStore.

This secret ID must point to a GenericSecret.

IG searches for the value of the password until it finds it, first locally, then in parent routes, then in config.json.

To create a store password, add a file containing the password. The filename must corresponds to the secret ID, and the file content must contain only the password, with no trailing spaces or carriage returns.

"keyEntryPassword": configuration expression<secret-id>, optional
This property is deprecated; use the entryPasswordSecretId instead. For more information, refer to the Deprecated section of the Release Notes.

The secret ID of the password to access entries in the KeyStore.

This secret ID must point to a GenericSecret.

To create an entry password, add a file containing the password. The filename must correspond to the secret ID, and the file content must contain only the password, with no trailing spaces or carriage returns.

When this property is used, the password must be the same for all entries in the keystore. If the keystore uses different passwords for entries, keyEntryPassword doesn’t work.

Default: The value of storePassword

Log level

To facilitate debugging secrets for the KeyStoreSecretStore, in logback.xml add a logger defined by the fully qualified package name of the KeyStoreSecretStore. The following line in logback.xml sets the log level to ALL:

<logger name="org.forgerock.secrets.keystore" level="ALL">

Example

For examples of routes that use KeyStoreSecretStore, see the examples in JwtBuilderFilter.

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",
        "storePasswordSecretId": "keystore.secret.id",
        "entryPasswordSecretId": "keystore.secret.id",
        "mappings": [{
          "secretId": "verification.secret.id",
          "aliases": [ "verification.key.1", "verification.key.2" ]
        }]
      },
      "issuer": "http://am.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.