See: Description
Interface | Description |
---|---|
SecretStore<T extends Secret> |
A backend storage mechanism for certain kinds of secrets.
|
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 secret.
|
SecretsProvider |
The secrets provider is used to get hold of active, named or valid secret objects.
|
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 | Description |
---|---|
NoSuchSecretException |
Indicates that no secret was configured for the given purpose, or the named secret is not available.
|
SecretsProvider
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.
SecretsProvider
API are designed to be asynchronous, using
Promise
s to return results or exceptions.
SecretsProvider
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)
retire(purpose, oldSecretId)
revoke(secretId)
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.
GenericSecret
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).
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
and DataDecryptionKey
for encrypting and decrypting application data and messages;KeyEncryptionKey
and KeyDecryptionKey
for encrypting and decrypting other keys;SigningKey
and VerificationKey
for
signing and verifying digital signatures, including message authentication codes (MACs);KeyAgreementKey
for Diffie-Hellman or ECDH key agreement.SecretReference
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.Copyright © 2010-2018, ForgeRock All Rights Reserved.