Package org.forgerock.secrets
Provides a unified API for accessing secrets of various kinds. Secrets here means system credentials such
as connection passwords and API keys, and also cryptographic key material such as encryption keys or digital
signature keys. The API has been designed to be simple to use for common operations, whilst allowing the platform
to support a number of core requirements around secret management:
- support for rotating secrets, with a grace period during which the previous secret/key is still valid;
- support for pluggable secure backends for storing secrets, including support for Hardware Security Modules (HSMs) and dev-ops friendly options such as Docker or Kubernetes Secrets, and more sophisticated secret management systems such as Hashicorp Vault;
- ability to separate ("externalize") secrets from other configuration, such that configuration can be shared between environments while secrets are not shared.
Secrets Providers
The entry point for getting hold of secrets in an application is theSecretsProvider
class. This API is organized around the concept of Secret
s being used for
specific Purpose
s. A purpose is simply a string name together with an indication of
the type of secret that is required for that purpose (e.g., a SigningKey
is needed
for signing messages). The purpose system is designed to be extensible, and application developers are encouraged
to define their own Purpose instances for application-specific usage. For example, IG might define a purpose for
decrypting password replay messages:
private static final Purpose<DataDecryptionKey> PASSWORD_REPLAY_DECRYPTION
= purpose("password_replay", DataDecryptionKey.class);
This can then be used to locate the current key to use for decryption:
SecretsProvider provider = ...; // Application-specific
DataDecryptionKey key = provider.getActiveSecret(PASSWORD_REPLAY_DECRYPTION).getOrThrow();
byte[] decrypted = key.getCipher(algorithm).doFinal(encryptedPassword);
Alternatively, to support key rotation you can try to decrypt with all valid keys for the given purpose:
Optional<byte[]> decrypted = provider.getValidSecrets(PASSWORD_REPLAY_DECRYPTION)
.getOrThrow()
.map(key -> tryDecrypt(key, encryptedPassword))
.filter(Objects::nonNull)
.findAny();
Finally, if you know the specific key/secret that was used for a particular operation then you can lookup using a
stable identifier:
DataDecryptionKey key = provider.getNamedSecret(PASSWORD_REPLAY_DECRYPTION, keyId).getOrThrow();
Typically an application would use the first method when producing data using a particular secret
(possibly using Secret.getStableId()
to record the particular secret used,
for instance as a JWT "kid" claim), while the second and third methods would be used when consuming data.
Asynchronous API
All of the methods in theSecretsProvider
API are designed to be asynchronous, using
Promise
s to return results or exceptions.
Secret Stores
A singleSecretsProvider
may have multiple back-end
SecretStore
s configured to handle storage of secrets for different purposes. The
provider will route requests for secrets to the appropriate store. As with secrets, stores themselves may be rotated,
allowing for migration from one secret store to another. For instance, migrating from a file-based store to a
network-attached HSM.
As well as methods to query secrets for different purposes, stores may optionally also support three basic management functions:
rotate(purpose, newActiveId)
- This method allows to rotate the secret for a given purpose, installing the secret with the given stable id as the new active secret for that purpose. The previous active secret for that purpose will still be available for named/valid secret queries.
retire(purpose, oldSecretId)
- This method allows to retire a secret for a given purpose when it is no longer to be used.
revoke(secretId)
- This method immediately revokes the given secret for all purposes and prevents it being used any more in any capacity. As such this method is the "break glass in emergency" button that can be used in case of suspected secret compromise.
UnsupportedOperationException
when calling them. They are all synchronous methods to enable the
caller to be sure when the change has taken effect. They should therefore be performed in a background thread if
asynchronous operation is desired.
Generic Secrets
Generic secrets can be used to store arbitrary binary or text data that should be kept confidential. Examples would include connection passwords or OAuth 2.0 client secrets and other API keys. The mechanism for accessing these kinds of secrets is theGenericSecret
class, which provides a number of
methods for revealing the secret data in a controlled fashion. In each case, the consumer of the secret material
supplies a callback function that will be called with the secret material and can use it to e.g. initialise an
LDAP bind request:
BindRequest bind = provider.getActiveSecret(LDAP_BIND_PASSWORD)
.revealAsUtf8(password -> Requests.newSimpleBindRequest(username, password));
Code should take care not to assume the availability of the secret beyond the end of the request, and should take
a defensive copy if the secret must be retained beyond that scope (but consider storing the Secret object directly
instead in this case).
Cryptographic Keys
The API provides extensive support for operations using cryptographic keys for various purposes, including:- data encryption and decryption;
- key encryption ("wrapping") and decryption ("unwrapping");
- digital signature signing and verification;
- key agreement (e.g., Elliptic Curve Diffie-Hellman) and key derivation.
Key
object. Nevertheless, the API has been
designed to guide towards safe usage of keys. For instance, providing convenience methods that correctly
initialise cryptographic services with safe defaults. In some cases it is also possible to perform operations
without specifying the algorithm to use, in which case the API will attempt to select a suitable secure default.
The root of the cryptographic key hierarchy is the CryptoKey
abstract class.
Typically this would not be used directly, but instead one of the concrete classes:
DataEncryptionKey
andDataDecryptionKey
for encrypting and decrypting application data and messages;KeyEncryptionKey
andKeyDecryptionKey
for encrypting and decrypting other keys;SigningKey
andVerificationKey
for signing and verifying digital signatures, including message authentication codes (MACs);KeyAgreementKey
for Diffie-Hellman or ECDH key agreement.
Secret References
For the case where an application wants to configure the specification of a secret once and then not worry about refreshing it, aSecretReference
can be used to store a long-lived reference to the
active secret for a given purpose. The reference will ensure that the secret is refreshed periodically to ensure
that the current active secret is always used. This ensures that key rotations are picked up in a timely fashion.-
Interface Summary Interface Description SecretConstraint<T extends Secret> Interface for constraints on a secret that must be satisfied for a givenPurpose
.SecretDecoder Specifies how data retrieved from aSecretStore
should be decoded into a secret object.SecretStore<T extends Secret> A backend storage mechanism for certain kinds of secrets. -
Class Summary Class Description GenericSecret A generic secret represented as an opaque blob of bytes, such as a password or API key.Purpose<T extends Secret> A purpose encapsulates both a name for a function that requires access to secrets, together with a hint as to the intended usage of those secrets.Secret A secret is any piece of data that should be kept confidential.SecretBuilder Provides a uniform way for secrets providers to construct secrets and keys.SecretReference<T extends Secret> A long-lived reference to an active or named secret.SecretsKeyManager AnX509ExtendedKeyManager
implementation that gets keys and certificates from aSecretsProvider
.SecretsKeyStoreProvider A Java security provider that exposes a KeyStore view of a secret store.SecretsLoadStoreParameter Class used to initialise the keystore when it is initialised via the standard Java interfaces.SecretsProvider The secrets provider is used to get hold of active, named or valid secret objects.SecretsTrustManager Provides an implementation of a standard Java TLSX509ExtendedTrustManager
that will retrieve trusted certificates from the Secrets API.ThreadPoolSecretStore<T extends Secret> A secret store that wraps another secret store and performs all query operations in a background thread using a thread pool. -
Exception Summary Exception Description NoSuchSecretException Indicates that no secret was configured for the given purpose, or the named secret is not available.