

# Keyrings
<a name="keyrings"></a>


****  

|  | 
| --- |
| Our client-side encryption library was renamed to the AWS Database Encryption SDK. This developer guide still provides information on the [DynamoDB Encryption Client](legacy-dynamodb-encryption-client.md). | 

The AWS Database Encryption SDK uses *keyrings* to perform [envelope encryption](concepts.md#envelope-encryption). Keyrings generate, encrypt, and decrypt data keys. Keyrings determine the source of the unique data keys that protect each encrypted record, and the [wrapping keys](concepts.md#wrapping-key) that encrypt that data key. You specify a keyring when encrypting and the same or a different keyring when decrypting.

You can use each keyring individually or combine keyrings into a [multi-keyring](use-multi-keyring.md). Although most keyrings can generate, encrypt, and decrypt data keys, you might create a keyring that performs only one particular operation, such as a keyring that only generates data keys, and use that keyring in combination with others.

We recommend that you use a keyring that protects your wrapping keys and performs cryptographic operations within a secure boundary, such as the AWS KMS keyring, which uses AWS KMS keys that never leave [AWS Key Management Service](https://docs.aws.amazon.com/kms/latest/developerguide/) (AWS KMS) unencrypted. You can also write a keyring that uses wrapping keys that are stored in your hardware security modules (HSMs) or protected by other master key services.

Your keyring determines the wrapping keys that protect your data keys, and ultimately, your data. Use the most secure wrapping keys that are practical for your task. Whenever possible use wrapping keys that are protected by a hardware security module (HSM) or a key management infrastructure, such as KMS keys in [AWS Key Management Service](https://docs.aws.amazon.com/kms/latest/developerguide/) (AWS KMS) or encryption keys in [AWS CloudHSM](https://docs.aws.amazon.com/cloudhsm/latest/userguide/).

The AWS Database Encryption SDK provides several keyrings and keyring configurations, and you can create your own custom keyrings. You can also create a [multi-keyring](use-multi-keyring.md) that includes one or more keyrings of the same or a different type.

**Topics**
+ [How keyrings work](#using-keyrings)
+ [AWS KMS keyrings](use-kms-keyring.md)
+ [AWS KMS Hierarchical keyrings](use-hierarchical-keyring.md)
+ [AWS KMS ECDH keyrings](use-kms-ecdh-keyring.md)
+ [Raw AES keyrings](use-raw-aes-keyring.md)
+ [Raw RSA keyrings](use-raw-rsa-keyring.md)
+ [Raw ECDH keyrings](use-raw-ecdh-keyring.md)
+ [Multi-keyrings](use-multi-keyring.md)

## How keyrings work
<a name="using-keyrings"></a>


****  

|  | 
| --- |
| Our client-side encryption library was renamed to the AWS Database Encryption SDK. This developer guide still provides information on the [DynamoDB Encryption Client](legacy-dynamodb-encryption-client.md). | 

When you encrypt and sign a field in your database, the AWS Database Encryption SDK asks the keyring for encryption materials. The keyring returns a plaintext data key, a copy of the data key that's encrypted by each of the wrapping keys in the keyring, and a MAC key that is associated with the data key. The AWS Database Encryption SDK uses the plaintext key to encrypt the data, and then removes the plaintext data key from memory as soon as possible. Then, the AWS Database Encryption SDK adds a [material description](concepts.md#material-description) that includes the encrypted data keys and other information, such as encryption and signing instructions. The AWS Database Encryption SDK uses the MAC key to calculate Hash-Based Message Authentication Codes (HMACs) over the canonicalization of the material description and all fields marked `ENCRYPT_AND_SIGN` or `SIGN_ONLY`.

When you decrypt data, you can use the same keyring that you used to encrypt the data, or a different one. To decrypt the data, a decryption keyring must have access to at least one wrapping key in the encryption keyring.

The AWS Database Encryption SDK passes the encrypted data keys from the material description to the keyring, and asks the keyring to decrypt any one of them. The keyring uses its wrapping keys to decrypt one of the encrypted data keys and returns a plaintext data key. The AWS Database Encryption SDK uses the plaintext data key to decrypt the data. If none of the wrapping keys in the keyring can decrypt any of the encrypted data keys, the decrypt operation fails.

You can use a single keyring or also combine keyrings of the same type or a different type into a [multi-keyring](use-multi-keyring.md). When you encrypt data, the multi-keyring returns a copy of the data key encrypted by all of the wrapping keys in all of the keyrings that comprise the multi-keyring and a MAC key that is associated with the data key. You can decrypt the data using a keyring with any one of the wrapping keys in the multi-keyring.

# AWS KMS keyrings
<a name="use-kms-keyring"></a>


****  

|  | 
| --- |
| Our client-side encryption library was renamed to the AWS Database Encryption SDK. This developer guide still provides information on the [DynamoDB Encryption Client](legacy-dynamodb-encryption-client.md). | 

An AWS KMS keyring uses symmetric encryption or asymmetric RSA [AWS KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#master_keys) to generate, encrypt, and decrypt data keys. AWS Key Management Service (AWS KMS) protects your KMS keys and performs cryptographic operations within the FIPS boundary. We recommend that you use a AWS KMS keyring, or a keyring with similar security properties, whenever possible.

You can also use a symmetric multi-Region KMS key in an AWS KMS keyring. For more details and examples using multi-Region AWS KMS keys, see [Using multi-Region AWS KMS keys](#config-mrks). For information about multi-Region keys, see [Using multi-Region keys](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html) in the *AWS Key Management Service Developer Guide*.

AWS KMS keyrings can include two types of wrapping keys:
+ **Generator key**: Generates a plaintext data key and encrypts it. A keyring that encrypts data must have one generator key.
+ **Additional keys**: Encrypts the plaintext data key that the generator key generated. AWS KMS keyrings can have zero or more additional keys.

You must have a generator key to encrypt records. When an AWS KMS keyring has just one AWS KMS key, that key is used to generate and encrypt the data key. 

Like all keyrings, AWS KMS keyrings can be used independently or in a [multi-keyring](use-multi-keyring.md) with other keyrings of the same or a different type.

**Topics**
+ [Required permissions for AWS KMS keyrings](#kms-keyring-permissions)
+ [Identifying AWS KMS keys in an AWS KMS keyring](#kms-keyring-id)
+ [Creating an AWS KMS keyring](#kms-keyring-create)
+ [Using multi-Region AWS KMS keys](#config-mrks)
+ [Using an AWS KMS discovery keyring](#kms-keyring-discovery)
+ [Using an AWS KMS regional discovery keyring](#kms-keyring-regional)

## Required permissions for AWS KMS keyrings
<a name="kms-keyring-permissions"></a>

The AWS Database Encryption SDK doesn't require an AWS account and it doesn't depend on any AWS service. However, to use an AWS KMS keyring, you need an AWS account and the following minimum permissions on the AWS KMS keys in your keyring. 
+ To encrypt with an AWS KMS keyring, you need [kms:GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html) permission on the generator key. You need [kms:Encrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Encrypt.html) permission on all additional keys in the AWS KMS keyring.
+ To decrypt with an AWS KMS keyring, you need [kms:Decrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html) permission on at least one key in the AWS KMS keyring.
+ To encrypt with a multi-keyring comprised of AWS KMS keyrings, you need [kms:GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html) permission on the generator key in the generator keyring. You need [kms:Encrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Encrypt.html) permission on all other keys in all other AWS KMS keyrings. 
+ To encrypt with an asymmetric RSA AWS KMS keyring, you do not need [kms:GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html) or [kms:Encrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Encrypt.html) because you must specify the public key material that you want to use for encryption when you create the keyring. No AWS KMS calls are made when encrypting with this keyring. To decrypt with an asymmetric RSA AWS KMS keyring, you need [kms:Decrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html) permission.

For detailed information about permissions for AWS KMS keys, see [Authentication and access control](https://docs.aws.amazon.com/kms/latest/developerguide/control-access.html) in the *AWS Key Management Service Developer Guide*.

## Identifying AWS KMS keys in an AWS KMS keyring
<a name="kms-keyring-id"></a>

An AWS KMS keyring can include one or more AWS KMS keys. To specify an AWS KMS key in an AWS KMS keyring, use a supported AWS KMS key identifier. The key identifiers you can use to identify an AWS KMS key in a keyring vary with the operation and the language implementation. For details about the key identifiers for an AWS KMS key, see [Key Identifiers](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id) in the *AWS Key Management Service Developer Guide*.

As a best practice, use the most specific key identifier that is practical for your task.
+ To encrypt with an AWS KMS keyring, you can use a [key ID](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id-key-id), [key ARN](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id-key-ARN), [alias name](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id-alias-name), or [alias ARN](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id-alias-ARN) to encrypt data.
**Note**  
If you specify an alias name or alias ARN for a KMS key in an encryption keyring, the encrypt operation saves the key ARN currently associated with the alias in the metadata of the encrypted data key. It does not save the alias. Changes to the alias don't affect the KMS key used to decrypt your encrypted data keys.
+ To decrypt with an AWS KMS keyring, you must use a key ARN to identify AWS KMS keys. For details, see [Selecting wrapping keys](configure.md#config-keys).
+ In a keyring used for encryption and decryption, you must use a key ARN to identify AWS KMS keys.

When decrypting, the AWS Database Encryption SDK searches the AWS KMS keyring for an AWS KMS key that can decrypt one of the encrypted data keys. Specifically, the AWS Database Encryption SDK uses the following pattern for each encrypted data key in the material description.
+ The AWS Database Encryption SDK gets the key ARN of the AWS KMS key that encrypted the data key from the metadata of the material description.
+ The AWS Database Encryption SDK searches the decryption keyring for an AWS KMS key with a matching key ARN.
+ If it finds an AWS KMS key with a matching key ARN in the keyring, the AWS Database Encryption SDK asks AWS KMS to use the KMS key to decrypt the encrypted data key.
+ Otherwise, it skips to the next encrypted data key, if any. 

## Creating an AWS KMS keyring
<a name="kms-keyring-create"></a>

You can configure each AWS KMS keyring with a single AWS KMS key or multiple AWS KMS keys in the same or different AWS accounts and AWS Regions. The AWS KMS key must be a symmetric encryption key (`SYMMETRIC_DEFAULT`) or an asymmetric RSA KMS key. You can also use a symmetric encryption [multi-Region KMS key](#config-mrks). You can use one or more AWS KMS keyrings in a [multi-keyring](use-multi-keyring.md). 

You can create an AWS KMS keyring that encrypts and decrypts data, or you can create AWS KMS keyrings specifically for encrypting or decrypting. When you create an AWS KMS keyring to encrypt data, you must specify a *generator key*, which is an AWS KMS key that is used to generate a plaintext data key and encrypt it. The data key is mathematically unrelated to the KMS key. Then, if you choose, you can specify additional AWS KMS keys that encrypt the same plaintext data key. To decrypt an encrypted field protected by this keyring, the decryption keyring that you use must include at least one of the AWS KMS keys defined in the keyring, or no AWS KMS keys. (An AWS KMS keyring with no AWS KMS keys is known as an [AWS KMS discovery keyring](#kms-keyring-discovery).)

All wrapping keys in an encryption keyring or multi-keyring must be able to encrypt the data key. If any wrapping key fails to encrypt, the encrypt method fails. As a result, the caller must have the [required permissions](#kms-keyring-permissions) for all keys in the keyring. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt operation fails.

The following examples use the `CreateAwsKmsMrkMultiKeyring` method to create an AWS KMS keyring with a symmetric encryption KMS key. The `CreateAwsKmsMrkMultiKeyring` method automatically creates the AWS KMS client and ensures that the keyring will correctly handle both single-Region and multi-Region keys. These examples use a [key ARNs](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id-key-ARN) to identify the KMS keys. For details, see [Identifying AWS KMS keys in an AWS KMS keyring](#kms-keyring-id)

------
#### [ Java ]

```
final MaterialProviders matProv = MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();
final CreateAwsKmsMrkMultiKeyringInput keyringInput = CreateAwsKmsMrkMultiKeyringInput.builder()
        .generator(kmsKeyArn)
        .build();
final IKeyring kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput);
```

------
#### [ C\$1 / .NET ]

```
var matProv = new MaterialProviders(new MaterialProvidersConfig());
var createAwsKmsMrkMultiKeyringInput = new CreateAwsKmsMrkMultiKeyringInput
{
    Generator = kmsKeyArn
};
var awsKmsMrkMultiKeyring = matProv.CreateAwsKmsMrkMultiKeyring(createAwsKmsMrkMultiKeyringInput);
```

------
#### [ Rust ]

```
let provider_config = MaterialProvidersConfig::builder().build()?;
let mat_prov = client::Client::from_conf(provider_config)?;
let kms_keyring = mat_prov
    .create_aws_kms_mrk_multi_keyring()
    .generator(kms_key_id)
    .send()
    .await?;
```

------

The following examples use the `CreateAwsKmsRsaKeyring` method to create an AWS KMS keyring with an asymmetric RSA KMS key. To create an asymmetric RSA AWS KMS keyring, provide the following values.
+ `kmsClient`: create a new AWS KMS client
+ `kmsKeyID`: the key ARN that identifies your asymmetric RSA KMS key
+ `publicKey`: a ByteBuffer of a UTF-8 encoded PEM file that represents the public key of the key you passed to `kmsKeyID`
+ `encryptionAlgorithm`: the encryption algorithm must be `RSAES_OAEP_SHA_256` or `RSAES_OAEP_SHA_1`

------
#### [ Java ]

```
 final MaterialProviders matProv = MaterialProviders.builder()
    .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
    .build();
final CreateAwsKmsRsaKeyringInput createAwsKmsRsaKeyringInput =
    CreateAwsKmsRsaKeyringInput.builder()
        .kmsClient(KmsClient.create())
        .kmsKeyId(rsaKMSKeyArn)
        .publicKey(publicKey)
        .encryptionAlgorithm(EncryptionAlgorithmSpec.RSAES_OAEP_SHA_256)
        .build();
IKeyring awsKmsRsaKeyring = matProv.CreateAwsKmsRsaKeyring(createAwsKmsRsaKeyringInput);
```

------
#### [ C\$1 / .NET ]

```
var matProv = new MaterialProviders(new MaterialProvidersConfig());
var createAwsKmsRsaKeyringInput = new CreateAwsKmsRsaKeyringInput
{
    KmsClient = new AmazonKeyManagementServiceClient(),
    KmsKeyId = rsaKMSKeyArn,
    PublicKey = publicKey,
    EncryptionAlgorithm = EncryptionAlgorithmSpec.RSAES_OAEP_SHA_256
};
IKeyring awsKmsRsaKeyring = matProv.CreateAwsKmsRsaKeyring(createAwsKmsRsaKeyringInput);
```

------
#### [ Rust ]

```
let mpl_config = MaterialProvidersConfig::builder().build()?;
let mpl = mpl_client::Client::from_conf(mpl_config)?;
let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
let kms_rsa_keyring = mpl
    .create_aws_kms_rsa_keyring()
    .kms_key_id(rsa_kms_key_arn)
    .public_key(public_key)
    .encryption_algorithm(aws_sdk_kms::types::EncryptionAlgorithmSpec::RsaesOaepSha256)
    .kms_client(aws_sdk_kms::Client::new(&sdk_config))
    .send()
    .await?;
```

------

## Using multi-Region AWS KMS keys
<a name="config-mrks"></a>

You can use multi-Region AWS KMS keys as wrapping keys in the AWS Database Encryption SDK. If you encrypt with a multi-Region key in one AWS Region, you can decrypt using a related multi-Region key in a different AWS Region.

Multi-Region KMS keys are a set of AWS KMS keys in different AWS Regions that have the same key material and key ID. You can use these *related* keys as though they were the same key in different Regions. Multi-Region keys support common disaster recovery and backup scenarios that require encrypting in one Region and decrypting in a different Region without making a cross-Region call to AWS KMS. For information about multi-Region keys, see [Using multi-Region keys](https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html) in the *AWS Key Management Service Developer Guide*.

To support multi-Region keys, the AWS Database Encryption SDK includes AWS KMS multi-Region-aware keyrings. The `CreateAwsKmsMrkMultiKeyring` method supports both single-Region and multi-Region keys.
+ For single-Region keys, the multi-Region-aware symbol behaves just like the single-Region AWS KMS keyring. It attempts to decrypt ciphertext only with the single-Region key that encrypted the data. To simplify your AWS KMS keyring experience, we recommend using the `CreateAwsKmsMrkMultiKeyring` method whenever you use a symmetric encryption KMS key.
+ For multi-Region keys, the multi-Region-aware symbol attempts to decrypt ciphertext with the same multi-Region key that encrypted the data or with the related multi-Region key in the Region you specify.

In the multi-Region-aware keyrings that take more than one KMS key, you can specify multiple single-Region and multi-Region keys. However, you can specify only one key from each set of related multi-Region keys. If you specify more than one key identifier with the same key ID, the constructor call fails.

The following examples create an AWS KMS keyring with a multi-Region KMS key. The examples specify a multi-Region key as the generator key and a single-Region key as the child key.

------
#### [ Java ]

```
final MaterialProviders matProv = MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();
final CreateAwsKmsMrkMultiKeyringInput createAwsKmsMrkMultiKeyringInput =
    CreateAwsKmsMrkMultiKeyringInput.builder()
            .generator(multiRegionKeyArn)
            .kmsKeyIds(Collections.singletonList(kmsKeyArn))
            .build();
IKeyring awsKmsMrkMultiKeyring = matProv.CreateAwsKmsMrkMultiKeyring(createAwsKmsMrkMultiKeyringInput);
```

------
#### [ C\$1 / .NET ]

```
var matProv = new MaterialProviders(new MaterialProvidersConfig());
var createAwsKmsMrkMultiKeyringInput = new CreateAwsKmsMrkMultiKeyringInput
{
    Generator = multiRegionKeyArn,
    KmsKeyIds = new List<String> { kmsKeyArn }
};
var awsKmsMrkMultiKeyring = matProv.CreateAwsKmsMrkMultiKeyring(createAwsKmsMrkMultiKeyringInput);
```

------
#### [ Rust ]

```
let mpl_config = MaterialProvidersConfig::builder().build()?;
let mpl = mpl_client::Client::from_conf(mpl_config)?;

let aws_kms_mrk_multi_keyring = mpl
    .create_aws_kms_mrk_multi_keyring()
    .generator(multiRegion_key_arn)
    .kms_key_ids(vec![key_arn.to_string()])
    .send()
    .await?;
```

------

When you use multi-Region AWS KMS keyrings, you can decrypt ciphertext in strict mode or discover mode. To decrypt the ciphertext in strict mode, instantiate the multi-Region-aware symbol with the key ARN of the related multi-Region key in the region you are decrypting the ciphertext. If you specify the key ARN of a related multi-Region key in a different Region (for example, the region where the record was encrypted), the multi-Region-aware symbol will make a cross-Region call for that AWS KMS key.

When decrypting in strict mode, the multi-Region-aware symbol requires a key ARN. It accepts only one key ARN from each set of related multi-Region keys.

You can also decrypt in *discovery mode* with AWS KMS multi-Region keys. When decrypting in discovery mode, you don't specify any AWS KMS keys. (For information about single-Region AWS KMS discovery keyrings, see [Using an AWS KMS discovery keyring](#kms-keyring-discovery).)

If you encrypted with a multi-Region key, the multi-Region-aware symbol in discovery mode will try to decrypt by using a related multi-Region key in the local Region. If none exists; the call fails. In discovery mode, the AWS Database Encryption SDK will not attempt a cross-Region call for the multi-Region key used for encryption. 

## Using an AWS KMS discovery keyring
<a name="kms-keyring-discovery"></a>

When decrypting, it's a best practice to specify the wrapping keys that the AWS Database Encryption SDK can use. To follow this best practice, use an AWS KMS decryption keyring that limits the AWS KMS wrapping keys to those that you specify. However, you can also create an *AWS KMS discovery keyring*, that is, an AWS KMS keyring that doesn't specify any wrapping keys. 

The AWS Database Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring for AWS KMS multi-Region keys. For information about using multi-Region keys with the AWS Database Encryption SDK, see [Using multi-Region AWS KMS keys](#config-mrks).

Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt operation fails.

When decrypting, a discovery keyring allows the AWS Database Encryption SDK to ask AWS KMS to decrypt any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or has access to that AWS KMS key. The call succeeds only when the caller has `kms:Decrypt` permission on the AWS KMS key.

**Important**  
If you include an AWS KMS discovery keyring in a decryption [multi-keyring](use-multi-keyring.md), the discovery keyring overrides all KMS key restrictions specified by other keyrings in the multi-keyring. The multi-keyring behaves like its least restrictive keyring. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt operation fails

The AWS Database Encryption SDK provides an AWS KMS discovery keyring for convenience. However, we recommend that you use a more limited keyring whenever possible for the following reasons.
+ **Authenticity** – An AWS KMS discovery keyring can use any AWS KMS key that was used to encrypt a data key in the material description, so long as the caller has permission to use that AWS KMS key to decrypt. This might not be the AWS KMS key that the caller intends to use. For example, one of the encrypted data keys might have been encrypted under a less secure AWS KMS key that anyone can use. 
+ **Latency and performance** – An AWS KMS discovery keyring might be perceptibly slower than other keyrings because the AWS Database Encryption SDK tries to decrypt all of the encrypted data keys, including those encrypted by AWS KMS keys in other AWS accounts and Regions, and AWS KMS keys that the caller doesn't have permission to use for decryption. 

If you use a discovery keyring, we recommend that you use a [*discovery filter*](configure.md#config-discovery) to limit the KMS keys that can be used to those in specified AWS accounts and [partitions](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html). For help finding your account ID and partition, see [Your AWS account identifiers](https://docs.aws.amazon.com/general/latest/gr/acct-identifiers.html) and [ARN format](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#arns-syntax) in the *AWS General Reference*.

The following code examples instantiate an AWS KMS discovery keyring with a discovery filter that limits the KMS keys that the AWS Database Encryption SDK can use to those in the `aws` partition and `111122223333` example account. 

Before using this code, replace the example AWS account and partition values with valid values for your AWS account and partition. If your KMS keys are in China Regions, use the `aws-cn` partition value. If your KMS keys are in AWS GovCloud (US) Regions, use the `aws-us-gov` partition value. For all other AWS Regions, use the `aws` partition value.

------
#### [ Java ]

```
// Create discovery filter
DiscoveryFilter discoveryFilter = DiscoveryFilter.builder()
        .partition("aws")
        .accountIds(111122223333)
        .build();
// Create the discovery keyring
CreateAwsKmsMrkDiscoveryMultiKeyringInput createAwsKmsMrkDiscoveryMultiKeyringInput = CreateAwsKmsMrkDiscoveryMultiKeyringInput.builder()
        .discoveryFilter(discoveryFilter)
        .build();
IKeyring decryptKeyring = matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(createAwsKmsMrkDiscoveryMultiKeyringInput);
```

------
#### [ C\$1 / .NET ]

```
// Create discovery filter
var discoveryFilter = new DiscoveryFilter
{
    Partition = "aws",
    AccountIds = 111122223333
};
// Create the discovery keyring
var createAwsKmsMrkDiscoveryMultiKeyringInput = new CreateAwsKmsMrkDiscoveryMultiKeyringInput
{
    DiscoveryFilter = discoveryFilter
};
var decryptKeyring = matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(createAwsKmsMrkDiscoveryMultiKeyringInput);
```

------
#### [ Rust ]

```
// Create discovery filter
let discovery_filter = DiscoveryFilter::builder()
    .partition("aws")
    .account_ids(111122223333)
    .build()?;

// Create the discovery keyring
let decrypt_keyring = mpl
    .create_aws_kms_mrk_discovery_multi_keyring()
    .discovery_filter(discovery_filter)
    .send()
    .await?;
```

------

## Using an AWS KMS regional discovery keyring
<a name="kms-keyring-regional"></a>

An *AWS KMS regional discovery keyring* is a keyring that doesn't specify the ARNs of KMS keys. Instead, it allows the AWS Database Encryption SDK to decrypt using only the KMS keys in particular AWS Regions. 

When decrypting with an AWS KMS regional discovery keyring, the AWS Database Encryption SDK decrypts any encrypted data key that was encrypted under an AWS KMS key in the specified AWS Region. To succeed, the caller must have `kms:Decrypt` permission on at least one of the AWS KMS keys in the specified AWS Region that encrypted a data key.

Like other discovery keyrings, the regional discovery keyring has no effect on encryption. It works only when decrypting encrypted fields. If you use a regional discovery keyring in a multi-keyring that is used for encrypting and decrypting, it is effective only when decrypting. If you use a multi-Region discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt operation fails.

**Important**  
If you include an AWS KMS regional discovery keyring in a decryption [multi-keyring](use-multi-keyring.md), the regional discovery keyring overrides all KMS key restrictions specified by other keyrings in the multi-keyring. The multi-keyring behaves like its least restrictive keyring. An AWS KMS discovery keyring has no effect on encryption when used by itself or in a multi-keyring.

The regional discovery keyring in the AWS Database Encryption SDK attempts to decrypt only with KMS keys in the specified Region. When you use a discovery keyring, you configure the Region on the AWS KMS client. These AWS Database Encryption SDK implementations don't filter KMS keys by Region, but AWS KMS will fail a decrypt request for KMS keys outside of the specified Region.

If you use a discovery keyring, we recommend that you use a *discovery filter* to limit the KMS keys used in decryption to those in specified AWS accounts and partitions.

For example, the following code creates an AWS KMS regional discovery keyring with a discovery filter. This keyring limits the AWS Database Encryption SDK to KMS keys in account 111122223333 in the US West (Oregon) Region (us-west-2).

------
#### [ Java ]

```
// Create the discovery filter
DiscoveryFilter discoveryFilter = DiscoveryFilter.builder()
        .partition("aws")
        .accountIds(111122223333)
        .build();
// Create the discovery keyring
CreateAwsKmsMrkDiscoveryMultiKeyringInput createAwsKmsMrkDiscoveryMultiKeyringInput = CreateAwsKmsMrkDiscoveryMultiKeyringInput.builder()
        .discoveryFilter(discoveryFilter)
        .regions("us-west-2")
        .build();
IKeyring decryptKeyring = matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(createAwsKmsMrkDiscoveryMultiKeyringInput);
```

------
#### [ C\$1 / .NET ]

```
// Create discovery filter
var discoveryFilter = new DiscoveryFilter
{
    Partition = "aws",
    AccountIds = 111122223333
};
// Create the discovery keyring
var createAwsKmsMrkDiscoveryMultiKeyringInput = new CreateAwsKmsMrkDiscoveryMultiKeyringInput
{
    DiscoveryFilter = discoveryFilter,
    Regions = us-west-2
};
var decryptKeyring = matProv.CreateAwsKmsMrkDiscoveryMultiKeyring(createAwsKmsMrkDiscoveryMultiKeyringInput);
```

------
#### [ Rust ]

```
// Create discovery filter
let discovery_filter = DiscoveryFilter::builder()
    .partition("aws")
    .account_ids(111122223333)
    .build()?;

// Create the discovery keyring
let decrypt_keyring = mpl
    .create_aws_kms_mrk_discovery_multi_keyring()
    .discovery_filter(discovery_filter)
    .regions(us-west-2)
    .send()
    .await?;
```

------

# AWS KMS Hierarchical keyrings
<a name="use-hierarchical-keyring"></a>


****  

|  | 
| --- |
| Our client-side encryption library was renamed to the AWS Database Encryption SDK. This developer guide still provides information on the [DynamoDB Encryption Client](legacy-dynamodb-encryption-client.md). | 

**Note**  
As of July 24, 2023, branch keys created during the developer preview are not supported. Create new branch keys to continue using the key store that you created during the developer preview.

With the AWS KMS Hierarchical keyring, you can protect your cryptographic materials under a symmetric encryption KMS key without calling AWS KMS every time you encrypt or decrypt a record. It is a good choice for applications that need to minimize calls to AWS KMS, and applications that can reuse some cryptographic materials without violating their security requirements.

The Hierarchical keyring is a cryptographic materials caching solution that reduces the number of AWS KMS calls by using AWS KMS protected *branch keys* persisted in an Amazon DynamoDB table, and then locally caching branch key materials used in encrypt and decrypt operations. The DynamoDB table serves as the key store that manages and protects branch keys. It stores the active branch key and all previous versions of the branch key. The *active* branch key is the most recent branch key version. The Hierarchical keyring uses a unique data encryption key for each encrypt request and encrypts each data encryption key with a unique wrapping key derived from the active branch key. The Hierarchical keyring is dependent on the hierarchy established between active branch keys and their derived wrapping keys.

The Hierarchical keyring typically uses each branch key version to satisfy multiple requests. But you control the extent to which active branch keys are reused and determine how often the active branch key is rotated. The active version of the branch key remains active until you [rotate it](rotate-branch-key.md). Previous versions of the active branch key will not be used to perform encrypt operations, but they can still be queried and used in decrypt operations.

When you instantiate the Hierarchical keyring, it creates a local cache. You specify a [cache limit](#cache-limit) that defines the maximum amount of time that the branch key materials are stored within the local cache before they expire and are evicted from the cache. The Hierarchical keyring makes one AWS KMS call to decrypt the branch key and assemble the branch key materials the first time a `branch-key-id` is specified in an operation. Then, the branch key materials are stored in the local cache and reused for all encrypt and decrypt operations that specify that `branch-key-id` until the cache limit expires. Storing branch key materials in the local cache reduces AWS KMS calls. For example, consider a cache limit of 15 minutes. If you perform 10,000 encrypt operations within that cache limit, the [traditional AWS KMS keyring](use-kms-keyring.md) would need to make 10,000 AWS KMS calls to satisfy 10,000 encrypt operations. If you have one active `branch-key-id`, the Hierarchical keyring only needs to make one AWS KMS call to satisfy 10,000 encrypt operations.

The local cache separates encryption materials from decryption materials. The encryption materials are assembled from the active branch key and reused for all encrypt operations until the cache limit expires. The decryption materials are assembled from the branch key ID and version that is identified in the encrypted field's metadata, and they are reused for all decrypt operations related to the branch key ID and version until the cache limit expires. The local cache can store multiple versions of the same branch key at a time. When the local cache is configured to use a [branch key ID supplier](#branch-key-id-supplier), it can also store branch key materials from multiple active branch keys at a time.

**Note**  
All mentions of *Hierarchical keyring* in the AWS Database Encryption SDK refer to the AWS KMS Hierarchical keyring.

**Topics**
+ [How it works](#how-hierarchical-keyring-works)
+ [Prerequisites](#hierarchical-keyring-prereqs)
+ [Required permissions](#hierarchical-keyring-permissions)
+ [Choose a cache](#hierarchical-keyring-caches)
+ [Create a Hierarchical keyring](#initialize-hierarchical-keyring)
+ [Using the Hierarchical keyring for searchable encryption](#searchable-encryption-hierarchical-keyrings)

## How it works
<a name="how-hierarchical-keyring-works"></a>

The following walkthroughs describe how the Hierarchical keyring assembles encryption and decryption materials, and the different calls that the keyring makes for encrypt and decrypt operations. For technical details on the wrapping key derivation and plaintext data key encryption processes, see [AWS KMS Hierarchical keyring technical details](reference.md#hierarchical-keyring-details).

**Encrypt and sign**  
The following walkthrough describes how the Hierarchical keyring assembles encryption materials and derives a unique wrapping key. 

1. The encryption method asks the Hierarchical keyring for encryption materials. The keyring generates a plaintext data key, then checks to see if there are valid branch key materials in the local cache to generate the wrapping key. If there are valid branch key materials, the keyring proceeds to **Step 4**. 

1. If there are no valid branch key materials, the Hierarchical keyring queries the key store for the active branch key.

   1. The key store calls AWS KMS to decrypt the active branch key and returns the plaintext active branch key. Data identifying the active branch key is serialized to provide additional authenticated data (AAD) in the decrypt call to AWS KMS. 

   1. The key store returns the plaintext branch key and data that identifies it, such as the branch key version.

1. The Hierarchical keyring assembles branch key materials (the plaintext branch key and branch key version) and stores a copy of them in the local cache.

1. The Hierarchical keyring derives a unique wrapping key from the plaintext branch key and a 16-byte random salt. It uses the derived wrapping key to encrypt a copy of the plaintext data key.

The encryption method uses the encryption materials to encrypt and sign the record. For more information on how records are encrypted and signed in the AWS Database Encryption SDK, see [Encrypt and sign](how-it-works.md#encrypt-and-sign).

**Decrypt and verify**  
The following walkthrough describes how the Hierarchical keyring assembles decryption materials and decrypts the encrypted data key.

1. The decryption method identifies the encrypted data key from the material description field of the encrypted record, and passes it to the Hierarchical keyring.

1. The Hierarchical keyring deserializes data identifying the encrypted data key, including the branch key version, the 16-byte salt, and other information describing how the data key was encrypted.

   For more information, see [AWS KMS Hierarchical keyring technical details](reference.md#hierarchical-keyring-details).

1. The Hierarchical keyring checks to see if there are valid branch key materials in the local cache that match the branch key version identified in **Step 2**. If there are valid branch key materials, the keyring proceeds to **Step 6**.

1. If there are no valid branch key materials, the Hierarchical keyring queries the key store for the branch key that matches the branch key version identified in **Step 2**.

   1. The key store calls AWS KMS to decrypt the branch key and returns the plaintext active branch key. Data identifying the active branch key is serialized to provide additional authenticated data (AAD) in the decrypt call to AWS KMS. 

   1. The key store returns the plaintext branch key and data that identifies it, such as the branch key version.

1. The Hierarchical keyring assembles branch key materials (the plaintext branch key and branch key version) and stores a copy of them in the local cache.

1. The Hierarchical keyring uses the assembled branch key materials and the 16-byte salt identified in **Step 2** to reproduce the unique wrapping key that encrypted the data key.

1. The Hierarchical keyring uses the reproduced wrapping key to decrypt the data key and returns the plaintext data key.

The decryption method uses the decryption materials and plaintext data key to decrypt and verify the record. For more information on how records are decrypted and verified in the AWS Database Encryption SDK, see [Decrypt and verify](how-it-works.md#decrypt-and-verify).

## Prerequisites
<a name="hierarchical-keyring-prereqs"></a>

Before you create and use a Hierarchical keyring, ensure the following prerequisites are met.
+ You, or your key store administrator, have [created a key store](create-keystore.md) and [created at least one active branch key](create-branch-keys.md).
+ You have [configured your key store actions](keystore-actions.md#config-keystore-actions).
**Note**  
How you configure your key store actions determines what operations you can perform and what KMS keys the Hierarchical keyring can use. For more information, see [Key store actions](keystore-actions.md).
+ You have the required AWS KMS permissions to access and use the key store and branch keys. For more information, see [Required permissions](#hierarchical-keyring-permissions).
+ You have reviewed the supported cache types and configured the cache type that best fits your needs. For more information, see [Choose a cache](#hierarchical-keyring-caches)

## Required permissions
<a name="hierarchical-keyring-permissions"></a>

The AWS Database Encryption SDK doesn't require an AWS account and it doesn't depend on any AWS service. However, to use an Hierarchical keyring, you need an AWS account and the following minimum permissions on the symmetric encryption AWS KMS key(s) in your key store. 
+ To encrypt and decrypt data with the Hierarchical keyring, you need [kms:Decrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html).
+ To [create](create-branch-keys.md) and [rotate](rotate-branch-key.md) branch keys, you need [kms:GenerateDataKeyWithoutPlaintext](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKeyWithoutPlaintext.html) and [kms:ReEncrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_ReEncrypt.html).

For more information on controlling access to your branch keys and key store, see [Implementing least privileged permissions](keystore-least-privilege.md).

## Choose a cache
<a name="hierarchical-keyring-caches"></a>

The Hierarchical keyring reduces the number of calls made to AWS KMS by locally caching the branch key materials used in encrypt and decrypt operations. Before you [create your Hierarchical keyring](#initialize-hierarchical-keyring), you need to decide what type of cache you want to use. You can use the default cache or customize the cache to best fits your needs.

The Hierarchical keyring supports the following cache types:
+ [Default cache](#cache-default)
+ [MultiThreaded cache](#cache-multithreaded)
+ [StormTracking cache](#cache-stormtracking)
+ [Shared cache](#cache-shared)

### Default cache
<a name="cache-default"></a>

For most users, the Default cache fulfills their threading requirements. The Default cache is designed to support heavily multithreaded environments. When a branch key materials entry expires, the Default cache prevents multiple threads from calling AWS KMS by notifying one thread that the branch key materials entry is going to expire 10 seconds in advance. This ensures that only one thread sends a request to AWS KMS to refresh the cache.

The Default and StormTracking caches support the same threading model, but you only need to specify the entry capacity to use the Default cache. For more granular cache customizations, use the [StormTracking cache](#cache-stormtracking).

Unless you want to customize the number of branch key materials entries that can be stored in the local cache, you do not need to specify a cache type when you create the Hierarchical keyring. If you do not specify a cache type, the Hierarchical keyring uses the Default cache type and sets the entry capacity to 1000. 

To customize the Default cache, specify the following values:
+ **Entry capacity**: limits the number of branch key materials entries that can be stored in the local cache.

------
#### [ Java ]

```
.cache(CacheType.builder()
        .Default(DefaultCache.builder()
        .entryCapacity(100)
        .build())
```

------
#### [ C\$1 / .NET ]

```
CacheType defaultCache = new CacheType
{
    Default = new DefaultCache{EntryCapacity = 100}
};
```

------
#### [ Rust ]

```
let cache: CacheType = CacheType::Default(
    DefaultCache::builder()
        .entry_capacity(100)
        .build()?,
);
```

------

### MultiThreaded cache
<a name="cache-multithreaded"></a>

The MultiThreaded cache is safe to use in multithreaded environments, but it does not provide any functionality to minimize AWS KMS or Amazon DynamoDB calls. As a result, when a branch key materials entry expires, all threads will be notified at the same time. This can result in multiple AWS KMS calls to refresh the cache.

To use the MultiThreaded cache, specify the following values:
+ **Entry capacity**: limits the number of branch key materials entries that can be stored in the local cache.
+ **Entry pruning tail size**: defines the number of entries to prune if the entry capacity is reached.

------
#### [ Java ]

```
.cache(CacheType.builder()
        .MultiThreaded(MultiThreadedCache.builder()
        .entryCapacity(100)
        .entryPruningTailSize(1)                                        
        .build())
```

------
#### [ C\$1 / .NET ]

```
CacheType multithreadedCache = new CacheType
{
    MultiThreaded = new MultiThreadedCache
    {
        EntryCapacity = 100,
        EntryPruningTailSize = 1
    }
};
```

------
#### [ Rust ]

```
CacheType::MultiThreaded(
            MultiThreadedCache::builder()
                    .entry_capacity(100)
                    .entry_pruning_tail_size(1)
                    .build()?)
```

------

### StormTracking cache
<a name="cache-stormtracking"></a>

The StormTracking cache is designed to support heavily multithreaded environments. When a branch key materials entry expires, the StormTracking cache prevents multiple threads from calling AWS KMS by notifying one thread that the branch key materials entry is going to expire in advance. This ensures that only one thread sends a request to AWS KMS to refresh the cache.



To use the StormTracking cache, specify the following values:
+ **Entry capacity**: limits the number of branch key materials entries that can be stored in the local cache.

  Default value: 1000 entries
+ **Entry pruning tail size**: defines the number of branch key materials entries to prune at a time.

  Default value: 1 entry
+ **Grace period**: defines the number of seconds before expiration that an attempt to refresh branch key materials is made.

  Default value: 10 seconds
+ **Grace interval**: defines the number of seconds between attempts to refresh the branch key materials.

  Default value: 1 seconds
+ **Fan out**: defines the number of simultaneous attempts that can be made to refresh the branch key materials.

  Default value: 20 attempts
+ **In flight time to live (TTL)**: defines the number of seconds until an attempt to refresh the branch key materials times out. Any time the cache returns `NoSuchEntry` in response to a `GetCacheEntry`, that branch key is considered to be *in flight* until the same key is written with a `PutCache` entry.

  Default value: 10 seconds
+ **Sleep**: defines the number of seconds that a thread should sleep if the `fanOut` is exceeded.

  Default value: 20 milliseconds

------
#### [ Java ]

```
.cache(CacheType.builder()
        .StormTracking(StormTrackingCache.builder()
        .entryCapacity(100)
        .entryPruningTailSize(1)
        .gracePeriod(10)
        .graceInterval(1)
        .fanOut(20) 
        .inFlightTTL(10)
        .sleepMilli(20)                                        
        .build())
```

------
#### [ C\$1 / .NET ]

```
CacheType stormTrackingCache = new CacheType
{
    StormTracking = new StormTrackingCache
    {
        EntryCapacity = 100,
        EntryPruningTailSize = 1,
        FanOut = 20,
        GraceInterval = 1,
        GracePeriod = 10,
        InFlightTTL = 10,
        SleepMilli = 20
    }
};
```

------
#### [ Rust ]

```
CacheType::StormTracking(
                StormTrackingCache::builder()
                    .entry_capacity(100)
                    .entry_pruning_tail_size(1)
                    .grace_period(10)
                    .grace_interval(1)
                    .fan_out(20)
                    .in_flight_ttl(10)
                    .sleep_milli(20)
                    .build()?)
```

------

### Shared cache
<a name="cache-shared"></a>

By default, the Hierarchical keyring creates a new local cache every time you instantiate the keyring. However, the Shared cache can help conserve memory by enabling you to share a cache across multiple Hierarchical keyrings. Rather than creating a new cryptographic materials cache for each Hierarchical keyring you instantiate, the Shared cache stores only one cache in memory, which can be used by all the Hierarchical keyrings that reference it. The Shared cache helps optimize memory usage by avoiding the duplication of cryptographic materials across keyrings. Instead, the Hierarchical keyrings can access the same underlying cache, reducing the overall memory footprint.

When you create your Shared cache, you still define the cache type. You can specify a [Default cache](#cache-default), [MultiThreaded cache](#cache-multithreaded), or [StormTracking cache](#cache-stormtracking) as the cache type, or substitute any compatible custom cache.



**Partitions**  
Multiple Hierarchical keyrings can use a single Shared cache. When you create a Hierarchical keyring with a Shared cache you can define an optional **partition ID**. The partition ID distinguishes which Hierarchical keyring is writing to the cache. If two Hierarchical keyrings reference the same partition ID, [logical key store name](create-keystore.md#logical-key-store-name), and branch key ID the two keyrings will share the same cache entries in the cache. If you create two Hierarchical keyrings with the same Shared cache, but different partition IDs, each keyring will only access the cache entries from its own designated partition within the Shared cache. The partitions act as logical divisions within the shared cache, allowing each Hierarchical keyring to operate independently on its own designated partition, without interfering with the data stored in the other partition.

If you intend to reuse or share the cache entries in a partition, you must define your own partition ID. When you pass the partition ID to your Hierarchical keyring, the keyring can reuse the cache entries that are already present in the Shared cache, rather than having to retrieve and re-authorize the branch key materials again. If you do not specify a partition ID, a unique partition ID is automatically assigned to the keyring each time you instantiate the Hierarchical keyring.

The following procedures demonstrate how to create a Shared cache with the [Default cache type](#cache-default) and pass it to a Hierarchical keyring.

1. Create a `CryptographicMaterialsCache` (CMC) using the [Material Providers Library](https://github.com/aws/aws-cryptographic-material-providers-library) (MPL).

------
#### [ Java ]

   ```
   // Instantiate the MPL
   final MaterialProviders matProv =
       MaterialProviders.builder()
           .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
           .build();
   
   // Create a CacheType object for the Default cache
   final CacheType cache =
       CacheType.builder() 
           .Default(DefaultCache.builder().entryCapacity(100).build())
           .build();
   
   // Create a CMC using the default cache
   final CreateCryptographicMaterialsCacheInput cryptographicMaterialsCacheInput =
       CreateCryptographicMaterialsCacheInput.builder()
           .cache(cache)
           .build();
   
   final ICryptographicMaterialsCache sharedCryptographicMaterialsCache =
       matProv.CreateCryptographicMaterialsCache(cryptographicMaterialsCacheInput);
   ```

------
#### [ C\$1 / .NET ]

   ```
   // Instantiate the MPL
   var materialProviders = new MaterialProviders(new MaterialProvidersConfig());
    
   // Create a CacheType object for the Default cache
   var cache = new CacheType { Default = new DefaultCache{EntryCapacity = 100} };
    
   // Create a CMC using the default cache
   var cryptographicMaterialsCacheInput = new CreateCryptographicMaterialsCacheInput {Cache = cache};
    
   var sharedCryptographicMaterialsCache = materialProviders.CreateCryptographicMaterialsCache(cryptographicMaterialsCacheInput);
   ```

------
#### [ Rust ]

   ```
   // Instantiate the MPL
   let mpl_config = MaterialProvidersConfig::builder().build()?;
   let mpl = mpl_client::Client::from_conf(mpl_config)?;
   
   // Create a CacheType object for the default cache
   let cache: CacheType = CacheType::Default(
       DefaultCache::builder()
           .entry_capacity(100)
           .build()?,
   );
   
   // Create a CMC using the default cache
   let shared_cryptographic_materials_cache: CryptographicMaterialsCacheRef = mpl.
       create_cryptographic_materials_cache()
       .cache(cache)
       .send()
       .await?;
   ```

------

1. Create a `CacheType` object for the Shared cache.

   Pass the `sharedCryptographicMaterialsCache` you created in **Step 1** to the new `CacheType` object.

------
#### [ Java ]

   ```
   // Create a CacheType object for the sharedCryptographicMaterialsCache
   final CacheType sharedCache =
       CacheType.builder()
           .Shared(sharedCryptographicMaterialsCache)
           .build();
   ```

------
#### [ C\$1 / .NET ]

   ```
   // Create a CacheType object for the sharedCryptographicMaterialsCache
   var sharedCache = new CacheType { Shared = sharedCryptographicMaterialsCache };
   ```

------
#### [ Rust ]

   ```
   // Create a CacheType object for the shared_cryptographic_materials_cache
   let shared_cache: CacheType = CacheType::Shared(shared_cryptographic_materials_cache);
   ```

------

1. Pass the `sharedCache` object from **Step 2** to your Hierarchical keyring.

   When you create a Hierarchical keyring with a Shared cache, you can optionally define a `partitionID` to share cache entries across multiple Hierarchical keyrings. If you do not specify a partition ID, the Hierarchical keyring automatically assigns the keyring a unique partition ID.
**Note**  
Your Hierarchical keyrings will share the same cache entries in a Shared cache if you create two or more keyrings that reference the same partition ID, [logical key store name](create-keystore.md#logical-key-store-name), and branch key ID. If you do not want multiple keyrings to share the same cache entries, you must use a unique partition ID for each Hierarchical keyring.

   The following example creates a Hierarchical keyring with a [branch key ID supplier](#branch-key-id-supplier), and a [cache limit](#cache-limit) of 600 seconds. For more information on the values defined in following Hierarchical keyring configuration, see [Create a Hierarchical keyring](#initialize-hierarchical-keyring).

------
#### [ Java ]

   ```
   // Create the Hierarchical keyring
   final CreateAwsKmsHierarchicalKeyringInput keyringInput =
       CreateAwsKmsHierarchicalKeyringInput.builder()
           .keyStore(keystore)
           .branchKeyIdSupplier(branchKeyIdSupplier)
           .ttlSeconds(600)
           .cache(sharedCache)
           .partitionID(partitionID)
           .build();        
   final IKeyring hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
   ```

------
#### [ C\$1 / .NET ]

   ```
   // Create the Hierarchical keyring        
   var createKeyringInput = new CreateAwsKmsHierarchicalKeyringInput
   {
      KeyStore = keystore,
      BranchKeyIdSupplier = branchKeyIdSupplier,
      Cache = sharedCache,
      TtlSeconds = 600,
      PartitionId = partitionID
   };
   var keyring = materialProviders.CreateAwsKmsHierarchicalKeyring(createKeyringInput);
   ```

------
#### [ Rust ]

   ```
   // Create the Hierarchical keyring
   let keyring1 = mpl
       .create_aws_kms_hierarchical_keyring()
       .key_store(key_store1)
       .branch_key_id(branch_key_id.clone())
       // CryptographicMaterialsCacheRef is an Rc (Reference Counted), so if you clone it to
       // pass it to different Hierarchical Keyrings, it will still point to the same
       // underlying cache, and increment the reference count accordingly.
       .cache(shared_cache.clone())
       .ttl_seconds(600)
       .partition_id(partition_id.clone())
       .send()
       .await?;
   ```

------

## Create a Hierarchical keyring
<a name="initialize-hierarchical-keyring"></a>

To create a Hierarchical keyring, you must provide the following values:
+ **A key store name**

  The name of the DynamoDB table you, or your key store administrator, created to serve as your key store.
+ 

  **A cache limit time to live (TTL)**

  The amount of time in seconds that a branch key materials entry within the local cache can be used before it expires. The cache limit TTL dictates how often the client calls AWS KMS to authorize use of the branch keys. This value must be greater than zero. After the cache limit TTL expires, the entry is never served, and will be evicted from the local cache.
+ **A branch key identifier**

  You can either statically configure the `branch-key-id` that identifies a single active branch key in your key store, or provide a branch key ID supplier.

  

  The *branch key ID supplier* uses the fields stored in the encryption context to determine which branch key is required to decrypt a record. By default, only the partition and sort keys are included in the encryption context. However, you can use the `SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT` [cryptographic action](concepts.md#crypt-actions) to include additional fields in the encryption context.

  We strongly recommend using a branch key ID supplier for multitenant databases where each tenant has their own branch key. You can use the branch key ID supplier to create a friendly name for your branch key IDs to make it easy to recognize the correct branch key ID for a specific tenant. For example, the friendly name lets you refer to a branch key as `tenant1` instead of `b3f61619-4d35-48ad-a275-050f87e15122`.

  For decrypt operations, you can either statically configure a single Hierarchical keyring to restrict decryption to a single tenant, or you can use the branch key ID supplier to identify which tenant is responsible for decrypting a record.
+ **(Optional) A cache**

  If you want to customize your cache type or the number of branch key materials entries that can be stored in the local cache, specify the cache type and entry capacity when you initialize the keyring.

  The Hierarchical keyring supports the following cache types: Default, MultiThreaded, StormTracking, and Shared. For more information and examples demonstrating how to define each cache type, see [Choose a cache](#hierarchical-keyring-caches).

  If you do not specify a cache, the Hierarchical keyring automatically uses the Default cache type and sets the entry capacity to 1000.
+ **(Optional) A partition ID**

  If you specify the [Shared cache](#cache-shared), you can optionally define a partition ID. The partition ID distinguishes which Hierarchical keyring is writing to the cache. If you intend to reuse or share the cache entries in a partition, you must define your own partition ID. You can specify any string for the partition ID. If you do not specify a partition ID, a unique partition ID is automatically assigned to the keyring at creation.

  For more information, see [Partitions](#shared-cache-partitions).
**Note**  
Your Hierarchical keyrings will share the same cache entries in a Shared cache if you create two or more keyrings that reference the same partition ID, [logical key store name](create-keystore.md#logical-key-store-name), and branch key ID. If you do not want multiple keyrings to share the same cache entries, you must use a unique partition ID for each Hierarchical keyring.
+ **(Optional) A list of Grant Tokens**

  If you control access to the KMS key in your Hierarchical keyring with [grants](https://docs.aws.amazon.com/kms/latest/developerguide/grants.html), you must provide all necessary grant tokens when you initialize the keyring.

### Create a Hierarchical keyring with a static branch key ID
<a name="static-branch-key-id-config"></a>

The following examples demonstrate how to create a Hierarchical keyring with a static branch key ID, the [Default cache](#cache-default), and a cache limit TTL of 600 seconds.

------
#### [ Java ]

```
final MaterialProviders matProv = MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();
final CreateAwsKmsHierarchicalKeyringInput keyringInput = CreateAwsKmsHierarchicalKeyringInput.builder()
        .keyStore(branchKeyStoreName)
        .branchKeyId(branch-key-id)
        .ttlSeconds(600)
        .build();
final Keyring hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
```

------
#### [ C\$1 / .NET ]

```
var matProv = new MaterialProviders(new MaterialProvidersConfig());
var keyringInput = new CreateAwsKmsHierarchicalKeyringInput
{
   KeyStore = keystore,
   BranchKeyIdSupplier = branchKeyIdSupplier,
   TtlSeconds = 600
};
var hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
```

------
#### [ Rust ]

```
let mpl_config = MaterialProvidersConfig::builder().build()?;
let mpl = mpl_client::Client::from_conf(mpl_config)?;

let hierarchical_keyring = mpl
    .create_aws_kms_hierarchical_keyring()
    .branch_key_id(branch_key_id)
    .key_store(branch_key_store_name)
    .ttl_seconds(600)
    .send()
    .await?;
```

------

### Create a Hierarchical keyring with a branch key ID supplier
<a name="branch-key-id-supplier-config"></a>

The following procedures demonstrate how to create a Hierarchical keyring with a branch key ID supplier.

1. Create a branch key ID supplier

   The following example creates friendly names for the two branch keys created in **Step 1**, and calls `CreateDynamoDbEncryptionBranchKeyIdSupplier` to create a branch key ID supplier with the AWS Database Encryption SDK for DynamoDB client.

------
#### [ Java ]

   ```
   // Create friendly names for each branch-key-id 
   class ExampleBranchKeyIdSupplier implements IDynamoDbKeyBranchKeyIdSupplier {
       private static String branchKeyIdForTenant1;
       private static String branchKeyIdForTenant2;
   
       public ExampleBranchKeyIdSupplier(String tenant1Id, String tenant2Id) {
           this.branchKeyIdForTenant1 = tenant1Id;
           this.branchKeyIdForTenant2 = tenant2Id;
       }
   // Create the branch key ID supplier    
   final DynamoDbEncryption ddbEnc = DynamoDbEncryption.builder()
           .DynamoDbEncryptionConfig(DynamoDbEncryptionConfig.builder().build())
           .build();
   final BranchKeyIdSupplier branchKeyIdSupplier = ddbEnc.CreateDynamoDbEncryptionBranchKeyIdSupplier(
       CreateDynamoDbEncryptionBranchKeyIdSupplierInput.builder()
               .ddbKeyBranchKeyIdSupplier(new ExampleBranchKeyIdSupplier(branch-key-ID-tenant1, branch-key-ID-tenant2))
               .build()).branchKeyIdSupplier();
   ```

------
#### [ C\$1 / .NET ]

   ```
   // Create friendly names for each branch-key-id
    class ExampleBranchKeyIdSupplier : DynamoDbKeyBranchKeyIdSupplierBase {
       private String _branchKeyIdForTenant1;
       private String _branchKeyIdForTenant2;
   
       public ExampleBranchKeyIdSupplier(String tenant1Id, String tenant2Id) {
           this._branchKeyIdForTenant1 = tenant1Id;
           this._branchKeyIdForTenant2 = tenant2Id;
       }    
   // Create the branch key ID supplier
   var ddbEnc = new DynamoDbEncryption(new DynamoDbEncryptionConfig());
   var branchKeyIdSupplier = ddbEnc.CreateDynamoDbEncryptionBranchKeyIdSupplier(
       new CreateDynamoDbEncryptionBranchKeyIdSupplierInput
       {
           DdbKeyBranchKeyIdSupplier = new ExampleBranchKeyIdSupplier(branch-key-ID-tenant1, branch-key-ID-tenant2)
       }).BranchKeyIdSupplier;
   ```

------
#### [ Rust ]

   ```
   // Create friendly names for each branch_key_id
   pub struct ExampleBranchKeyIdSupplier {
       branch_key_id_for_tenant1: String,
       branch_key_id_for_tenant2: String,
   }
   
   impl ExampleBranchKeyIdSupplier {
       pub fn new(tenant1_id: &str, tenant2_id: &str) -> Self {
           Self {
               branch_key_id_for_tenant1: tenant1_id.to_string(),
               branch_key_id_for_tenant2: tenant2_id.to_string(),
           }
       }
   }
   
   // Create the branch key ID supplier                                        
   let dbesdk_config = DynamoDbEncryptionConfig::builder().build()?;
   let dbesdk = dbesdk_client::Client::from_conf(dbesdk_config)?;
   let supplier = ExampleBranchKeyIdSupplier::new(tenant1_branch_key_id, tenant2_branch_key_id);
   
   let branch_key_id_supplier = dbesdk
       .create_dynamo_db_encryption_branch_key_id_supplier()
       .ddb_key_branch_key_id_supplier(supplier)
       .send()
       .await?
       .branch_key_id_supplier
       .unwrap();
   ```

------

1. Create a Hierarchical keyring

   The following examples initialize a Hierarchical keyring with the branch key ID supplier created in **Step 1**, a cache limit TLL of 600 seconds, and a maximum cache size of 1000.

------
#### [ Java ]

   ```
   final MaterialProviders matProv = MaterialProviders.builder()
           .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
           .build();
   final CreateAwsKmsHierarchicalKeyringInput keyringInput = CreateAwsKmsHierarchicalKeyringInput.builder()
           .keyStore(keystore)
           .branchKeyIdSupplier(branchKeyIdSupplier)
           .ttlSeconds(600)
           .cache(CacheType.builder() //OPTIONAL
                   .Default(DefaultCache.builder()
                   .entryCapacity(100)
                   .build())
           .build())
           .build();
   final Keyring hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
   ```

------
#### [ C\$1 / .NET ]

   ```
   var matProv = new MaterialProviders(new MaterialProvidersConfig());
   var keyringInput = new CreateAwsKmsHierarchicalKeyringInput
   {
      KeyStore = keystore,
      BranchKeyIdSupplier = branchKeyIdSupplier,
      TtlSeconds = 600, 
      Cache = new CacheType
      {
           Default = new DefaultCache { EntryCapacity = 100 }
      }
   };
   var hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
   ```

------
#### [ Rust ]

   ```
   let mpl_config = MaterialProvidersConfig::builder().build()?;
   let mpl = mpl_client::Client::from_conf(mpl_config)?;
   
   let hierarchical_keyring = mpl
       .create_aws_kms_hierarchical_keyring()
       .branch_key_id_supplier(branch_key_id_supplier)
       .key_store(key_store)
       .ttl_seconds(600)
       .send()
       .await?;
   ```

------

## Using the Hierarchical keyring for searchable encryption
<a name="searchable-encryption-hierarchical-keyrings"></a>

[Searchable encryption](searchable-encryption.md) enables you to search encrypted records without decrypting the entire database. This is accomplished by indexing the plaintext value of an encrypted field with a [beacon](beacons.md). To implement searchable encryption, you must use a Hierarchical keyring.

The key store `CreateKey` operation generates both a branch key and *beacon key*. The branch key is used in record encryption and decryption operations. The beacon key is used to generate beacons.

The branch key and beacon key are protected by the same AWS KMS key that you specify when creating your key store service. After the `CreateKey` operation calls AWS KMS to generate the branch key, it calls [kms:GenerateDataKeyWithoutPlaintext](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKeyWithoutPlaintext.html) a second time to generate the beacon key using the following request.

```
{
   "EncryptionContext": { 
      "branch-key-id" : "branch-key-id",
      "type" : type,
      "create-time" : "timestamp",
      "tablename" : "the logical table name for your key store",
      "kms-arn" : the KMS key ARN,
      "hierarchy-version" : 1
   },
   "KeyId": "the KMS key ARN",
   "NumberOfBytes": "32"
}
```

After generating both keys, the `CreateKey` operation calls [ddb:TransactWriteItems](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html) to write two new items that will persist the branch key and beacon key in your branch key store.

When you [configure a standard beacon](configure-beacons.md#config-standard-beacons), the AWS Database Encryption SDK queries the key store for the beacon key. Then, it uses an HMAC-based extract-and-expand key derivation function ([HKDF](https://en.wikipedia.org/wiki/HKDF)) to combine the beacon key with the name of the [standard beacon](beacons.md#standard-beacon-overview) to create the HMAC key for a given beacon.

Unlike branch keys, there is only one beacon key version per `branch-key-id` in a key store. The beacon key is never rotated.

### Defining your beacon key source
<a name="beacon-key-source"></a>

When you define the [beacon version](using-beacons.md#beacon-version) for your standard and compound beacons, you must identify the beacon key and define a cache limit time to live (TTL) for the beacon key materials. Beacon key materials are stored in a separate local cache from the branch keys. The following snippet demonstrates how to define the `keySource` for a single-tenant database. Identify your beacon key by the `branch-key-id` it is associated with. 

------
#### [ Java ]

```
keySource(BeaconKeySource.builder()
        .single(SingleKeyStore.builder()
                .keyId(branch-key-id)
                .cacheTTL(6000)
                .build())
        .build())
```

------
#### [ C\$1 / .NET ]

```
KeySource = new BeaconKeySource
{
    Single = new SingleKeyStore
    {
       KeyId = branch-key-id,
       CacheTTL = 6000
    }
}
```

------
#### [ Rust ]

```
 .key_source(BeaconKeySource::Single(
    SingleKeyStore::builder()
        // `keyId` references a beacon key.
        // For every branch key we create in the keystore,
        // we also create a beacon key.
        // This beacon key is not the same as the branch key,
        // but is created with the same ID as the branch key.
        .key_id(branch_key_id)
        .cache_ttl(6000)
        .build()?,
))
```

------

**Defining beacon source in a multitenant database**  
If you have a multitenant database, you must specify the following values when configuring the `keySource`.  
+ 

  **keyFieldName**

  Defines the name of the field that stores the `branch-key-id` associated with the beacon key used to generated beacons for a given tenant. The `keyFieldName` can be any string, but it must be unique to all other fields in your database. When you write new records to your database, the `branch-key-id` that identifies the beacon key used to generate any beacons for that record is stored in this field. You must include this field in your beacon queries and identify the appropriate beacon key materials required to recalculate the beacon. For more information, see [Querying beacons in a multitenant database](searchable-encryption-multitenant.md#query-multitenant-beacons).
+ **cacheTTL**

  The amount of time in seconds that a beacon key materials entry within the local beacon cache can be used before it expires. This value must be greater than zero. When the cache limit TTL expires, the entry is evicted from the local cache.
+ **(Optional) A cache**

  If you want to customize your cache type or the number of branch key materials entries that can be stored in the local cache, specify the cache type and entry capacity when you initialize the keyring.

  The Hierarchical keyring supports the following cache types: Default, MultiThreaded, StormTracking, and Shared. For more information and examples demonstrating how to define each cache type, see [Choose a cache](#hierarchical-keyring-caches).

  If you do not specify a cache, the Hierarchical keyring automatically uses the Default cache type and sets the entry capacity to 1000.
The following example creates a Hierarchical keyring with a branch key ID supplier a cache limit TLL of 600 seconds, and an entry capacity of 1000.  

```
final MaterialProviders matProv = MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();
final CreateAwsKmsHierarchicalKeyringInput keyringInput = CreateAwsKmsHierarchicalKeyringInput.builder()
        .keyStore(branchKeyStoreName)
        .branchKeyIdSupplier(branchKeyIdSupplier)
        .ttlSeconds(600)
        .cache(CacheType.builder() //OPTIONAL
                .Default(DefaultCache.builder()
                        .entryCapacity(1000)
                        .build())
                .build());
final IKeyring hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
```

```
var matProv = new MaterialProviders(new MaterialProvidersConfig());
var keyringInput = new CreateAwsKmsHierarchicalKeyringInput
{
   KeyStore = keystore,
   BranchKeyIdSupplier = branchKeyIdSupplier,
   TtlSeconds = 600, 
   Cache = new CacheType
   {
        Default = new DefaultCache { EntryCapacity = 1000 }
   }
};
var hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
```

```
let provider_config = MaterialProvidersConfig::builder().build()?;
    let mat_prov = client::Client::from_conf(provider_config)?;
    let kms_keyring = mat_prov
        .create_aws_kms_hierarchical_keyring()
        .branch_key_id(branch_key_id)
        .key_store(key_store)
        .ttl_seconds(600)
        .send()
        .await?;
```

# AWS KMS ECDH keyrings
<a name="use-kms-ecdh-keyring"></a>


****  

|  | 
| --- |
| Our client-side encryption library was renamed to the AWS Database Encryption SDK. This developer guide still provides information on the [DynamoDB Encryption Client](legacy-dynamodb-encryption-client.md). | 

**Important**  
The AWS KMS ECDH keyring is only available with version 1.5.0 or later of the Material Providers Library.

An AWS KMS ECDH keyring uses asymmetric key agreement [AWS KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/key-types.html) to derive a shared symmetric wrapping key between two parties. First, the keyring uses the Elliptic Curve Diffie-Hellman (ECDH) key agreement algorithm to derive a shared secret from the private key in the sender's KMS key pair and the recipient's public key. Then, the keyring uses the shared secret to derive the shared wrapping key that protects your data encryption keys. The key derivation function that the AWS Database Encryption SDK uses (`KDF_CTR_HMAC_SHA384`) to derive the shared wrapping key conforms to [NIST recommendations for key derivation](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-108r1-upd1.pdf).

The key derivation function returns 64 bytes of keying material. To ensure that both parties use the correct keying material, the AWS Database Encryption SDK uses the first 32 bytes as a commitment key and the last 32 bytes as the shared wrapping key. On decrypt, if the keyring cannot reproduce the same commitment key and shared wrapping key that is stored in the material description field of the encrypted record, the operation fails. For example, if you encrypt a record with a keyring configured with **Alice's** private key and **Bob's** public key, a keyring configured with **Bob's** private key and **Alice's** public key will reproduce the same commitment key and shared wrapping key and be able to decrypt the record. If Bob's public key is not from a KMS key pair, then Bob can create a [Raw ECDH keyring](use-raw-ecdh-keyring.md) to decrypt the record.

The AWS KMS ECDH keyring encrypts records with a symmetric key using AES-GCM. The data key is then envelope encrypted with the derived shared wrapping key using AES-GCM. Each AWS KMS ECDH keyring can have only one shared wrapping key, but you can include multiple AWS KMS ECDH keyrings, alone or with other keyrings, in a [multi-keyring](use-multi-keyring.md).

**Topics**
+ [Required permissions for AWS KMS ECDH keyrings](#kms-ecdh-permissions)
+ [Creating an AWS KMS ECDH keyring](#kms-ecdh-create)
+ [Creating an AWS KMS ECDH discovery keyring](#kms-ecdh-discovery)

## Required permissions for AWS KMS ECDH keyrings
<a name="kms-ecdh-permissions"></a>

The AWS Database Encryption SDK doesn't require an AWS account and it doesn't depend on any AWS service. However, to use an AWS KMS ECDH keyring, you need an AWS account and the following minimum permissions on the AWS KMS keys in your keyring. The permissions vary based on which key agreement schema you use.
+ To encrypt and decrypt records using the `KmsPrivateKeyToStaticPublicKey` key agreement schema, you need [kms:GetPublicKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GetPublicKey.html) and [kms:DeriveSharedSecret](https://docs.aws.amazon.com/kms/latest/APIReference/API_DeriveSharedSecret.html) on the *sender's* asymmetric KMS key pair. If you directly provide the sender's DER-encoded public key when you instantiate your keyring, you only need [kms:DeriveSharedSecret](https://docs.aws.amazon.com/kms/latest/APIReference/API_DeriveSharedSecret.html) permission on the sender's asymmetric KMS key pair.
+ To decrypt records using the `KmsPublicKeyDiscovery` key agreement schema, you need [kms:DeriveSharedSecret](https://docs.aws.amazon.com/kms/latest/APIReference/API_DeriveSharedSecret.html) and [kms:GetPublicKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GetPublicKey.html) permissions on the specified asymmetric KMS key pair.

## Creating an AWS KMS ECDH keyring
<a name="kms-ecdh-create"></a>

To create an AWS KMS ECDH keyring that encrypts and decrypts data, you must use the `KmsPrivateKeyToStaticPublicKey` key agreement schema. To initialize an AWS KMS ECDH keyring with the `KmsPrivateKeyToStaticPublicKey` key agreement schema, provide the following values:
+ **Sender's AWS KMS key ID**

  Must identify an asymmetric NIST-recommended elliptic curve (ECC)KMS key pair with a `KeyUsage` value of `KEY_AGREEMENT`. The sender's private key is used to derive the shared secret.
+ **(Optional) Sender's public key**

  Must be a DER-encoded X.509 public key, also known as `SubjectPublicKeyInfo` (SPKI), as defined in [RFC 5280](https://tools.ietf.org/html/rfc5280).

  The AWS KMS [GetPublicKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GetPublicKey.html) operation returns the public key of an asymmetric KMS key pair in the required DER-encoded format.

  To reduce the number of AWS KMS calls that your keyring makes, you can directly provide the sender's public key. If no value is provided for the sender's public key, the keyring calls AWS KMS to retrieve the sender's public key.
+ **Recipient's public key**

  You must provide the recipient's DER-encoded X.509 public key, also known as `SubjectPublicKeyInfo` (SPKI), as defined in [RFC 5280](https://tools.ietf.org/html/rfc5280).

  The AWS KMS [GetPublicKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GetPublicKey.html) operation returns the public key of an asymmetric KMS key pair in the required DER-encoded format.
+ **Curve specification**

  Identifies the elliptic curve specification in the specified key pairs. Both the sender and recipient's key pairs must have the same curve specification.

  Valid values: `ECC_NIST_P256`, `ECC_NIS_P384`, `ECC_NIST_P512`
+ **(Optional) A list of Grant Tokens**

  If you control access to the KMS key in your AWS KMS ECDH keyring with [grants](https://docs.aws.amazon.com/kms/latest/developerguide/grants.html), you must provide all necessary grant tokens when you initialize the keyring.

------
#### [ C\$1 / .NET ]

The following example creates an AWS KMS ECDH keyring with the with the sender's KMS key, the sender's public key, and the recipient's public key. This example uses the optional `senderPublicKey` parameter to provide the sender's public key. If you do not provide the sender's public key, the keyring calls AWS KMS to retrieve the sender's public key. Both the sender and recipient's key pairs are on the `ECC_NIST_P256` curve. 

```
// Instantiate material providers
var materialProviders = new MaterialProviders(new MaterialProvidersConfig());

// Must be DER-encoded X.509 public keys
var BobPublicKey = new MemoryStream(new byte[] { });
var AlicePublicKey = new MemoryStream(new byte[] { });

// Create the AWS KMS ECDH static keyring
var staticConfiguration = new KmsEcdhStaticConfigurations
{
    KmsPrivateKeyToStaticPublicKey = new KmsPrivateKeyToStaticPublicKeyInput
    {
        SenderKmsIdentifier = "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab",
        SenderPublicKey = BobPublicKey,
        RecipientPublicKey = AlicePublicKey
    }
};
	    
var createKeyringInput = new CreateAwsKmsEcdhKeyringInput
{
    CurveSpec = ECDHCurveSpec.ECC_NIST_P256,
    KmsClient = new AmazonKeyManagementServiceClient(),
    KeyAgreementScheme = staticConfiguration
};

var keyring = materialProviders.CreateAwsKmsEcdhKeyring(createKeyringInput);
```

------
#### [ Java ]

The following example creates an AWS KMS ECDH keyring with the with the sender's KMS key, the sender's public key, and the recipient's public key. This example uses the optional `senderPublicKey` parameter to provide the sender's public key. If you do not provide the sender's public key, the keyring calls AWS KMS to retrieve the sender's public key. Both the sender and recipient's key pairs are on the `ECC_NIST_P256` curve. 

```
// Retrieve public keys
// Must be DER-encoded X.509 public keys                                
ByteBuffer BobPublicKey = getPublicKeyBytes("arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab");
        ByteBuffer AlicePublicKey = getPublicKeyBytes("arn:aws:kms:us-west-2:111122223333:key/0987dcba-09fe-87dc-65ba-ab0987654321"); 

// Create the AWS KMS ECDH static keyring
        final CreateAwsKmsEcdhKeyringInput senderKeyringInput =
          CreateAwsKmsEcdhKeyringInput.builder()
            .kmsClient(KmsClient.create())
            .curveSpec(ECDHCurveSpec.ECC_NIST_P256)
            .KeyAgreementScheme(
              KmsEcdhStaticConfigurations.builder()
                .KmsPrivateKeyToStaticPublicKey(
                  KmsPrivateKeyToStaticPublicKeyInput.builder()
                    .senderKmsIdentifier("arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab")
                    .senderPublicKey(BobPublicKey)
                    .recipientPublicKey(AlicePublicKey)
                    .build()).build()).build();
```

------
#### [ Rust ]

The following example creates an AWS KMS ECDH keyring with the with the sender's KMS key, the sender's public key, and the recipient's public key. This example uses the optional `sender_public_key` parameter to provide the sender's public key. If you do not provide the sender's public key, the keyring calls AWS KMS to retrieve the sender's public key.

```
// Retrieve public keys
// Must be DER-encoded X.509 keys
let public_key_file_content_sender = std::fs::read_to_string(Path::new(EXAMPLE_KMS_ECC_PUBLIC_KEY_FILENAME_SENDER))?;
let parsed_public_key_file_content_sender = parse(public_key_file_content_sender)?;
let public_key_sender_utf8_bytes = parsed_public_key_file_content_sender.contents();

let public_key_file_content_recipient = std::fs::read_to_string(Path::new(EXAMPLE_KMS_ECC_PUBLIC_KEY_FILENAME_RECIPIENT))?;
let parsed_public_key_file_content_recipient = parse(public_key_file_content_recipient)?;
let public_key_recipient_utf8_bytes = parsed_public_key_file_content_recipient.contents();

// Create KmsPrivateKeyToStaticPublicKeyInput
let kms_ecdh_static_configuration_input =
    KmsPrivateKeyToStaticPublicKeyInput::builder()
        .sender_kms_identifier(arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab)
        // Must be a UTF8 DER-encoded X.509 public key
        .sender_public_key(public_key_sender_utf8_bytes)
        // Must be a UTF8 DER-encoded X.509 public key
        .recipient_public_key(public_key_recipient_utf8_bytes)
        .build()?;

let kms_ecdh_static_configuration = KmsEcdhStaticConfigurations::KmsPrivateKeyToStaticPublicKey(kms_ecdh_static_configuration_input);

// Instantiate the material providers library
let mpl_config = MaterialProvidersConfig::builder().build()?;
let mpl = mpl_client::Client::from_conf(mpl_config)?;

// Create AWS KMS ECDH keyring
let kms_ecdh_keyring = mpl
    .create_aws_kms_ecdh_keyring()
    .kms_client(kms_client)
    .curve_spec(ecdh_curve_spec)
    .key_agreement_scheme(kms_ecdh_static_configuration)
    .send()
    .await?;
```

------

## Creating an AWS KMS ECDH discovery keyring
<a name="kms-ecdh-discovery"></a>

When decrypting, it's a best practice to specify the keys that the AWS Database Encryption SDK can use. To follow this best practice, use an AWS KMS ECDH keyring with the `KmsPrivateKeyToStaticPublicKey` key agreement schema. However, you can also create an AWS KMS ECDH discovery keyring, that is, an AWS KMS ECDH keyring that can decrypt any record where the public key of the specified KMS key pair matches the *recipient's* public key stored in the material description field of the encrypted record.

**Important**  
When you decrypt records using the `KmsPublicKeyDiscovery` key agreement schema, you accept all public keys, regardless of who owns it.

To initialize an AWS KMS ECDH keyring with the `KmsPublicKeyDiscovery` key agreement schema, provide the following values:
+ **Recipient's AWS KMS key ID**

  Must identify an asymmetric NIST-recommended elliptic curve (ECC)KMS key pair with a `KeyUsage` value of `KEY_AGREEMENT`.
+ **Curve specification**

  Identifies the elliptic curve specification in the recipient's KMS key pair.

  Valid values: `ECC_NIST_P256`, `ECC_NIS_P384`, `ECC_NIST_P512`
+ **(Optional) A list of Grant Tokens**

  If you control access to the KMS key in your AWS KMS ECDH keyring with [grants](https://docs.aws.amazon.com/kms/latest/developerguide/grants.html), you must provide all necessary grant tokens when you initialize the keyring.

------
#### [ C\$1 / .NET ]

The following example creates an AWS KMS ECDH discovery keyring with a KMS key pair on the `ECC_NIST_P256` curve. You must have [kms:GetPublicKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GetPublicKey.html) and [kms:DeriveSharedSecret](https://docs.aws.amazon.com/kms/latest/APIReference/API_DeriveSharedSecret.html) permissions on the specified KMS key pair. This keyring can decrypt any record where the public key of the specified KMS key pair matches the recipient's public key stored in the material description field of the encrypted record.

```
// Instantiate material providers
var materialProviders = new MaterialProviders(new MaterialProvidersConfig());

// Create the AWS KMS ECDH discovery keyring
var discoveryConfiguration = new KmsEcdhStaticConfigurations
{
    KmsPublicKeyDiscovery = new KmsPublicKeyDiscoveryInput
    {
        RecipientKmsIdentifier = "arn:aws:kms:us-west-2:111122223333:key/0987dcba-09fe-87dc-65ba-ab0987654321"
    }
		    
};
var createKeyringInput = new CreateAwsKmsEcdhKeyringInput
{
    CurveSpec = ECDHCurveSpec.ECC_NIST_P256,
    KmsClient = new AmazonKeyManagementServiceClient(),
    KeyAgreementScheme = discoveryConfiguration
};
var keyring = materialProviders.CreateAwsKmsEcdhKeyring(createKeyringInput);
```

------
#### [ Java ]

The following example creates an AWS KMS ECDH discovery keyring with a KMS key pair on the `ECC_NIST_P256` curve. You must have [kms:GetPublicKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GetPublicKey.html) and [kms:DeriveSharedSecret](https://docs.aws.amazon.com/kms/latest/APIReference/API_DeriveSharedSecret.html) permissions on the specified KMS key pair. This keyring can decrypt any record where the public key of the specified KMS key pair matches the recipient's public key stored in the material description field of the encrypted record.

```
// Create the AWS KMS ECDH discovery keyring
final CreateAwsKmsEcdhKeyringInput recipientKeyringInput =
  CreateAwsKmsEcdhKeyringInput.builder()
    .kmsClient(KmsClient.create())
    .curveSpec(ECDHCurveSpec.ECC_NIST_P256)
    .KeyAgreementScheme(
      KmsEcdhStaticConfigurations.builder()
        .KmsPublicKeyDiscovery(
          KmsPublicKeyDiscoveryInput.builder()
            .recipientKmsIdentifier("arn:aws:kms:us-west-2:111122223333:key/0987dcba-09fe-87dc-65ba-ab0987654321").build()
        ).build())
    .build();
```

------
#### [ Rust ]

```
// Create KmsPublicKeyDiscoveryInput
let kms_ecdh_discovery_static_configuration_input =
    KmsPublicKeyDiscoveryInput::builder()
        .recipient_kms_identifier(ecc_recipient_key_arn)
        .build()?;

let kms_ecdh_discovery_static_configuration = KmsEcdhStaticConfigurations::KmsPublicKeyDiscovery(kms_ecdh_discovery_static_configuration_input);

// Instantiate the material providers library
let mpl_config = MaterialProvidersConfig::builder().build()?;
let mpl = mpl_client::Client::from_conf(mpl_config)?;

// Create AWS KMS ECDH discovery keyring
let kms_ecdh_discovery_keyring = mpl
    .create_aws_kms_ecdh_keyring()
    .kms_client(kms_client.clone())
    .curve_spec(ecdh_curve_spec)
    .key_agreement_scheme(kms_ecdh_discovery_static_configuration)
    .send()
    .await?;
```

------

# Raw AES keyrings
<a name="use-raw-aes-keyring"></a>


****  

|  | 
| --- |
| Our client-side encryption library was renamed to the AWS Database Encryption SDK. This developer guide still provides information on the [DynamoDB Encryption Client](legacy-dynamodb-encryption-client.md). | 

The AWS Database Encryption SDK lets you use an AES symmetric key that you provide as a wrapping key that protects your data key. You need to generate, store, and protect the key material, preferably in a hardware security module (HSM) or key management system. Use a Raw AES keyring when you need to provide the wrapping key and encrypt the data keys locally or offline.

The Raw AES keyring encrypts data by using the AES-GCM algorithm and a wrapping key that you specify as a byte array. You can specify only one wrapping key in each Raw AES keyring, but you can include multiple Raw AES keyrings, alone or with other keyrings, in a [multi-keyring](use-multi-keyring.md). 

**Key namespaces and names**

To identify the AES key in a keyring, the Raw AES keyring uses a *key namespace* and *key name* that you provide. These values are not secret. They appear in plain text in the [material description](concepts.md#material-description) that the AWS Database Encryption SDK adds to the record. We recommend using a key namespace your HSM or key management system and a key name that identifies the AES key in that system.

**Note**  
The key namespace and key name are equivalent to the *Provider ID* (or *Provider*) and *Key ID* fields in the `JceMasterKey`.

If you construct different keyrings to encrypt and decrypt a given field, the namespace and name values are critical. If the key namespace and key name in the decryption keyring isn't an exact, case-sensitive match for the key namespace and key name in the encryption keyring, the decryption keyring isn't used, even if the key material bytes are identical.

For example, you might define a Raw AES keyring with key namespace `HSM_01` and key name `AES_256_012`. Then, you use that keyring to encrypt some data. To decrypt that data, construct a Raw AES keyring with the same key namespace, key name, and key material.

The following examples show how to create a Raw AES keyring. The `AESWrappingKey` variable represents the key material you provide.

------
#### [ Java ]

```
final CreateRawAesKeyringInput keyringInput = CreateRawAesKeyringInput.builder()
        .keyName("AES_256_012")
        .keyNamespace("HSM_01")
        .wrappingKey(AESWrappingKey)
        .wrappingAlg(AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16)
        .build();
final MaterialProviders matProv = MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();
IKeyring rawAesKeyring = matProv.CreateRawAesKeyring(keyringInput);
```

------
#### [ C\$1 / .NET ]

```
var keyNamespace = "HSM_01";
var keyName = "AES_256_012";

// This example uses the key generator in Bouncy Castle to generate the key material.
// In production, use key material from a secure source.
var aesWrappingKey = new MemoryStream(GeneratorUtilities.GetKeyGenerator("AES256").GenerateKey());

// Create the keyring
var keyringInput = new CreateRawAesKeyringInput
{
    KeyNamespace = keyNamespace,
    KeyName = keyName,
    WrappingKey = AESWrappingKey,
    WrappingAlg = AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16
};

var matProv = new MaterialProviders(new MaterialProvidersConfig());
IKeyring rawAesKeyring = matProv.CreateRawAesKeyring(keyringInput);
```

------
#### [ Rust ]

```
let mpl_config = MaterialProvidersConfig::builder().build()?;
let mpl = mpl_client::Client::from_conf(mpl_config)?;
let raw_aes_keyring = mpl
    .create_raw_aes_keyring()
    .key_name("AES_256_012")
    .key_namespace("HSM_01")
    .wrapping_key(aes_key_bytes)
    .wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16)
    .send()
    .await?;
```

------

# Raw RSA keyrings
<a name="use-raw-rsa-keyring"></a>


****  

|  | 
| --- |
| Our client-side encryption library was renamed to the AWS Database Encryption SDK. This developer guide still provides information on the [DynamoDB Encryption Client](legacy-dynamodb-encryption-client.md). | 

The Raw RSA keyring performs asymmetric encryption and decryption of data keys in local memory with an RSA public and private keys that you provide. You need to generate, store, and protect the private key, preferably in a hardware security module (HSM) or key management system. The encryption function encrypts the data key under the RSA public key. The decryption function decrypts the data key using the private key. You can select from among the several RSA padding modes.

A Raw RSA keyring that encrypts and decrypts must include an asymmetric public key and private key pair. However, you can encrypt data with a Raw RSA keyring that has only a public key, and you can decrypt data with a Raw RSA keyring that has only a private key. You can include any Raw RSA keyring in a [multi-keyring](use-multi-keyring.md). If you configure a Raw RSA keyring with a public and private key, be sure that they are part of the same key pair.

 The Raw RSA keyring is equivalent to and interoperates with the [JceMasterKey](https://aws.github.io/aws-encryption-sdk-java/com/amazonaws/encryptionsdk/jce/JceMasterKey.html) in the AWS Encryption SDK for Java when they are used with RSA asymmetric encryption keys.

**Note**  
The Raw RSA keyring does not support asymmetric KMS keys. To use asymmetric RSA KMS keys, construct an [AWS KMS keyring](use-kms-keyring.md).

**Namespaces and names**

To identify the RSA key material in a keyring, the Raw RSA keyring uses a *key namespace* and *key name* that you provide. These values are not secret. They appear in plain text in the [material description](concepts.md#material-description) that the AWS Database Encryption SDK adds to the record. We recommend using the key namespace and key name that identifies the RSA key pair (or its private key) in your HSM or key management system.

**Note**  
The key namespace and key name are equivalent to the *Provider ID* (or *Provider*) and *Key ID* fields in the `JceMasterKey`.

If you construct different keyrings to encrypt and decrypt a given record, the namespace and name values are critical. If the key namespace and key name in the decryption keyring isn't an exact, case-sensitive match for the key namespace and key name in the encryption keyring, the decryption keyring isn't used, even if the keys are from the same key pair.

The key namespace and key name of the key material in the encryption and decryption keyrings must be same whether the keyring contains the RSA public key, the RSA private key, or both keys in the key pair. For example, suppose you encrypt data with a Raw RSA keyring for an RSA public key with key namespace `HSM_01` and key name `RSA_2048_06`. To decrypt that data, construct a Raw RSA keyring with the private key (or key pair), and the same key namespace and name.

**Padding mode**

You must specify a padding mode for Raw RSA keyrings used for encryption and decryption, or use features of your language implementation that specify it for you.

The AWS Encryption SDK supports the following padding modes, subjects to the constraints of each language. We recommend an [OAEP](https://tools.ietf.org/html/rfc8017#section-7.1) padding mode, particularly OAEP with SHA-256 and MGF1 with SHA-256 Padding. The [PKCS1](https://tools.ietf.org/html/rfc8017#section-7.2) padding mode is supported only for backward compatibility.
+ OAEP with SHA-1 and MGF1 with SHA-1 Padding
+ OAEP with SHA-256 and MGF1 with SHA-256 Padding
+ OAEP with SHA-384 and MGF1 with SHA-384 Padding
+ OAEP with SHA-512 and MGF1 with SHA-512 Padding
+ PKCS1 v1.5 Padding 

The following Java example shows how to create a Raw RSA keyring with the public and private key of an RSA key pair and the OAEP with SHA-256 and MGF1 with SHA-256 padding mode. The `RSAPublicKey` and `RSAPrivateKey` variables represent the key material you provide.

------
#### [ Java ]

```
final CreateRawRsaKeyringInput keyringInput = CreateRawRsaKeyringInput.builder()
        .keyName("RSA_2048_06")
        .keyNamespace("HSM_01")
        .paddingScheme(PaddingScheme.OAEP_SHA256_MGF1)
        .publicKey(RSAPublicKey)
        .privateKey(RSAPrivateKey)
        .build();
final MaterialProviders matProv = MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();
IKeyring rawRsaKeyring = matProv.CreateRawRsaKeyring(keyringInput);
```

------
#### [ C\$1 / .NET ]

```
var keyNamespace = "HSM_01";
var keyName = "RSA_2048_06";

// Get public and private keys from PEM files
var publicKey = new MemoryStream(System.IO.File.ReadAllBytes("RSAKeyringExamplePublicKey.pem"));
var privateKey = new MemoryStream(System.IO.File.ReadAllBytes("RSAKeyringExamplePrivateKey.pem"));

// Create the keyring input
var keyringInput = new CreateRawRsaKeyringInput
{
    KeyNamespace = keyNamespace,
    KeyName = keyName,
    PaddingScheme = PaddingScheme.OAEP_SHA512_MGF1,
    PublicKey = publicKey,
    PrivateKey = privateKey
};

// Create the keyring
var matProv = new MaterialProviders(new MaterialProvidersConfig());
var rawRsaKeyring = matProv.CreateRawRsaKeyring(keyringInput);
```

------
#### [ Rust ]

```
let mpl_config = MaterialProvidersConfig::builder().build()?;
let mpl = mpl_client::Client::from_conf(mpl_config)?;
let raw_rsa_keyring = mpl
    .create_raw_rsa_keyring()
    .key_name("RSA_2048_06")
    .key_namespace("HSM_01")
    .padding_scheme(PaddingScheme::OaepSha256Mgf1)
    .public_key(RSA_public_key)
    .private_key(RSA_private_key)
    .send()
    .await?;
```

------

# Raw ECDH keyrings
<a name="use-raw-ecdh-keyring"></a>


****  

|  | 
| --- |
| Our client-side encryption library was renamed to the AWS Database Encryption SDK. This developer guide still provides information on the [DynamoDB Encryption Client](legacy-dynamodb-encryption-client.md). | 

**Important**  
The Raw ECDH keyring is only available with version 1.5.0 of the Material Providers Library.

The Raw ECDH keyring uses the elliptic curve public-private key pairs that you provide to derive a shared wrapping key between two parties. First, the keyring derives a shared secret using the sender's private key, the recipient's public key, and the Elliptic Curve Diffie-Hellman (ECDH) key agreement algorithm. Then, the keyring uses the shared secret to derive the shared wrapping key that protects your data encryption keys. The key derivation function that the AWS Database Encryption SDK uses (`KDF_CTR_HMAC_SHA384`) to derive the shared wrapping key conforms to [NIST recommendations for key derivation](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-108r1-upd1.pdf).

The key derivation function returns 64 bytes of keying material. To ensure that both parties use the correct keying material, the AWS Database Encryption SDK uses the first 32 bytes as a commitment key and the last 32 bytes as the shared wrapping key. On decrypt, if the keyring cannot reproduce the same commitment key and shared wrapping key that is stored in the material description field of the encrypted record, the operation fails. For example, if you encrypt a record with a keyring configured with **Alice's** private key and **Bob's** public key, a keyring configured with **Bob's** private key and **Alice's** public key will reproduce the same commitment key and shared wrapping key and be able to decrypt the record. If Bob's public key is from an AWS KMS key pair, then Bob can create an [AWS KMS ECDH keyring](use-kms-ecdh-keyring.md) to decrypt the record.

The Raw ECDH keyring encrypts records with a symmetric key using AES-GCM. The data key is then envelope encrypted with the derived shared wrapping key using AES-GCM. Each Raw ECDH keyring can have only one shared wrapping key, but you can include multiple Raw ECDH keyrings, alone or with other keyrings, in a [multi-keyring](use-multi-keyring.md).

You are responsible for generating, storing, and protecting your private keys, preferably in a hardware security module (HSM) or key management system. The sender and recipient's key pairs much be on the same elliptic curve. The AWS Database Encryption SDK supports the following elliptic cuve specifications:
+ `ECC_NIST_P256`
+ `ECC_NIST_P384`
+ `ECC_NIST_P512`

## Creating a Raw ECDH keyring
<a name="raw-ecdh-create"></a>

The Raw ECDH keyring supports three key agreement schemas: `RawPrivateKeyToStaticPublicKey`, `EphemeralPrivateKeyToStaticPublicKey`, and `PublicKeyDiscovery`. The key agreement schema that you select determines which cryptographic operations you can perform and how the keying materials are assembled.

**Topics**
+ [RawPrivateKeyToStaticPublicKey](#raw-ecdh-RawPrivateKeyToStaticPublicKey)
+ [EphemeralPrivateKeyToStaticPublicKey](#raw-ecdh-EphemeralPrivateKeyToStaticPublicKey)
+ [PublicKeyDiscovery](#raw-ecdh-PublicKeyDiscovery)

### RawPrivateKeyToStaticPublicKey
<a name="raw-ecdh-RawPrivateKeyToStaticPublicKey"></a>

Use the `RawPrivateKeyToStaticPublicKey` key agreement schema to statically configure the sender's private key and the recipient's public key in the keyring. This key agreement schema can encrypt and decrypt records.

To initialize a Raw ECDH keyring with the `RawPrivateKeyToStaticPublicKey` key agreement schema, provide the following values:
+ **Sender's private key**

  You must provide the sender's PEM-encoded private key (PKCS \$18 PrivateKeyInfo structures), as defined in [RFC 5958](https://tools.ietf.org/html/rfc5958#section-2).
+ **Recipient's public key**

  You must provide the recipient's DER-encoded X.509 public key, also known as `SubjectPublicKeyInfo` (SPKI), as defined in [RFC 5280](https://tools.ietf.org/html/rfc5280).

  You can specify the public key of an asymmetric key agreement KMS key pair or the public key from a key pair generated outside of AWS.
+ **Curve specification**

  Identifies the elliptic curve specification in the specified key pairs. Both the sender and recipient's key pairs must have the same curve specification.

  Valid values: `ECC_NIST_P256`, `ECC_NIS_P384`, `ECC_NIST_P512`

------
#### [ C\$1 / .NET ]

```
// Instantiate material providers
var materialProviders = new MaterialProviders(new MaterialProvidersConfig());
	    var BobPrivateKey = new MemoryStream(new byte[] { });
	    var AlicePublicKey = new MemoryStream(new byte[] { });

	    // Create the Raw ECDH static keyring
	    var staticConfiguration = new RawEcdhStaticConfigurations()
	    {
		    RawPrivateKeyToStaticPublicKey = new RawPrivateKeyToStaticPublicKeyInput
		    {
			    SenderStaticPrivateKey = BobPrivateKey,
			    RecipientPublicKey = AlicePublicKey
		    }
	    };
	    
	    var createKeyringInput = new CreateRawEcdhKeyringInput() 
	    {
		    CurveSpec = ECDHCurveSpec.ECC_NIST_P256,
		    KeyAgreementScheme = staticConfiguration 
	    };

	    var keyring = materialProviders.CreateRawEcdhKeyring(createKeyringInput);
```

------
#### [ Java ]

The following Java example uses the `RawPrivateKeyToStaticPublicKey` key agreement schema to statically configure the sender's private key and the recipient's public key. Both key pairs are on the `ECC_NIST_P256` curve.

```
private static void StaticRawKeyring() {
    // Instantiate material providers
    final MaterialProviders materialProviders =
      MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();

    KeyPair senderKeys = GetRawEccKey();
    KeyPair recipient = GetRawEccKey();

    // Create the Raw ECDH static keyring
    final CreateRawEcdhKeyringInput rawKeyringInput =
      CreateRawEcdhKeyringInput.builder()
        .curveSpec(ECDHCurveSpec.ECC_NIST_P256)
        .KeyAgreementScheme(
          RawEcdhStaticConfigurations.builder()
            .RawPrivateKeyToStaticPublicKey(
                RawPrivateKeyToStaticPublicKeyInput.builder()
                  // Must be a PEM-encoded private key
                  .senderStaticPrivateKey(ByteBuffer.wrap(senderKeys.getPrivate().getEncoded()))
                  // Must be a DER-encoded X.509 public key
                  .recipientPublicKey(ByteBuffer.wrap(recipient.getPublic().getEncoded()))
                  .build()
            )
            .build()
        ).build();

    final IKeyring staticKeyring = materialProviders.CreateRawEcdhKeyring(rawKeyringInput);
}
```

------
#### [ Rust ]

The following Python example uses the `raw_ecdh_static_configuration` key agreement schema to statically configure the sender's private key and the recipient's public key. Both key pairs must be on the same curve.

```
// Create keyring input
let raw_ecdh_static_configuration_input =
    RawPrivateKeyToStaticPublicKeyInput::builder()
        // Must be a UTF8 PEM-encoded private key
        .sender_static_private_key(private_key_sender_utf8_bytes)
        // Must be a UTF8 DER-encoded X.509 public key
        .recipient_public_key(public_key_recipient_utf8_bytes)
        .build()?;

let raw_ecdh_static_configuration = RawEcdhStaticConfigurations::RawPrivateKeyToStaticPublicKey(raw_ecdh_static_configuration_input);

// Instantiate the material providers library
let mpl_config = MaterialProvidersConfig::builder().build()?;
let mpl = mpl_client::Client::from_conf(mpl_config)?;

// Create raw ECDH static keyring
let raw_ecdh_keyring = mpl
    .create_raw_ecdh_keyring()
    .curve_spec(ecdh_curve_spec)
    .key_agreement_scheme(raw_ecdh_static_configuration)
    .send()
    .await?;
```

------

### EphemeralPrivateKeyToStaticPublicKey
<a name="raw-ecdh-EphemeralPrivateKeyToStaticPublicKey"></a>

Keyrings configured with the `EphemeralPrivateKeyToStaticPublicKey` key agreement schema create a new key pair locally and derive a unique shared wrapping key for each encrypt call.

This key agreement schema can only encrypt records. To decrypt records encrypted with the `EphemeralPrivateKeyToStaticPublicKey` key agreement schema, you must use a discovery key agreement schema configured with the same recipient's public key. To decrypt, you can use a Raw ECDH keyring with the [`PublicKeyDiscovery`](#raw-ecdh-PublicKeyDiscovery) key agreement algorithm, or, if the recipient's public key is from an asymmetric key agreement KMS key pair, you can use an AWS KMS ECDH keyring with the [KmsPublicKeyDiscovery](use-kms-ecdh-keyring.md#kms-ecdh-discovery) key agreement schema. 

To initialize a Raw ECDH keyring with the `EphemeralPrivateKeyToStaticPublicKey` key agreement schema, provide the following values:
+ **Recipient's public key**

  You must provide the recipient's DER-encoded X.509 public key, also known as `SubjectPublicKeyInfo` (SPKI), as defined in [RFC 5280](https://tools.ietf.org/html/rfc5280).

  You can specify the public key of an asymmetric key agreement KMS key pair or the public key from a key pair generated outside of AWS.
+ **Curve specification**

  Identifies the elliptic curve specification in the specified public key.

  On encrypt, the keyring creates a new key pair on the specified curve and uses the new private key and specified public key to derive a shared wrapping key.

  Valid values: `ECC_NIST_P256`, `ECC_NIS_P384`, `ECC_NIST_P512`

------
#### [ C\$1 / .NET ]

The following example creates a Raw ECDH keyring with the `EphemeralPrivateKeyToStaticPublicKey` key agreement schema. On encrypt, the keyring will create a new key pair locally on the specified `ECC_NIST_P256` curve. 

```
// Instantiate material providers
var materialProviders = new MaterialProviders(new MaterialProvidersConfig());
	    var AlicePublicKey = new MemoryStream(new byte[] { });

	    // Create the Raw ECDH ephemeral keyring
	    var ephemeralConfiguration = new RawEcdhStaticConfigurations()
	    {
		    EphemeralPrivateKeyToStaticPublicKey = new EphemeralPrivateKeyToStaticPublicKeyInput
		    {
			    RecipientPublicKey = AlicePublicKey
		    }
	    };
	    
	    var createKeyringInput = new CreateRawEcdhKeyringInput() 
	    {
		    CurveSpec = ECDHCurveSpec.ECC_NIST_P256,
		    KeyAgreementScheme = ephemeralConfiguration
	    };

	    var keyring = materialProviders.CreateRawEcdhKeyring(createKeyringInput);
```

------
#### [ Java ]

The following example creates a Raw ECDH keyring with the `EphemeralPrivateKeyToStaticPublicKey` key agreement schema. On encrypt, the keyring will create a new key pair locally on the specified `ECC_NIST_P256` curve.

```
private static void EphemeralRawEcdhKeyring() {
    // Instantiate material providers
    final MaterialProviders materialProviders =
      MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();

    ByteBuffer recipientPublicKey = getPublicKeyBytes();

    // Create the Raw ECDH ephemeral keyring
    final CreateRawEcdhKeyringInput ephemeralInput =
      CreateRawEcdhKeyringInput.builder()
        .curveSpec(ECDHCurveSpec.ECC_NIST_P256)
        .KeyAgreementScheme(
          RawEcdhStaticConfigurations.builder()
            .EphemeralPrivateKeyToStaticPublicKey(
              EphemeralPrivateKeyToStaticPublicKeyInput.builder()
                .recipientPublicKey(recipientPublicKey)
                .build()
            )
            .build()
        ).build();

    final IKeyring ephemeralKeyring = materialProviders.CreateRawEcdhKeyring(ephemeralInput);
}
```

------
#### [ Rust ]

The following example creates a Raw ECDH keyring with the `ephemeral_raw_ecdh_static_configuration` key agreement schema. On encrypt, the keyring will create a new key pair locally on the specified curve.

```
// Create EphemeralPrivateKeyToStaticPublicKeyInput
let ephemeral_raw_ecdh_static_configuration_input =
    EphemeralPrivateKeyToStaticPublicKeyInput::builder()
        // Must be a UTF8 DER-encoded X.509 public key
        .recipient_public_key(public_key_recipient_utf8_bytes)
        .build()?;

let ephemeral_raw_ecdh_static_configuration =
    RawEcdhStaticConfigurations::EphemeralPrivateKeyToStaticPublicKey(ephemeral_raw_ecdh_static_configuration_input);

// Instantiate the material providers library
let mpl_config = MaterialProvidersConfig::builder().build()?;
let mpl = mpl_client::Client::from_conf(mpl_config)?;

// Create raw ECDH ephemeral private key keyring
let ephemeral_raw_ecdh_keyring = mpl
    .create_raw_ecdh_keyring()
    .curve_spec(ecdh_curve_spec)
    .key_agreement_scheme(ephemeral_raw_ecdh_static_configuration)
    .send()
    .await?;
```

------

### PublicKeyDiscovery
<a name="raw-ecdh-PublicKeyDiscovery"></a>

When decrypting, it's a best practice to specify the wrapping keys that the AWS Database Encryption SDK can use. To follow this best practice, use an ECDH keyring that specifies both a sender's private key and recipient's public key. However, you can also create a Raw ECDH discovery keyring, that is, a Raw ECDH keyring that can decrypt any record where the specified key's public key matches the recipient's public key stored in the material description field of the encrypted record. This key agreement schema can only decrypt records.

**Important**  
When you decrypt records using the `PublicKeyDiscovery` key agreement schema, you accept all public keys, regardless of who owns it.

To initialize a Raw ECDH keyring with the `PublicKeyDiscovery` key agreement schema, provide the following values:
+ **Recipient's static private key**

  You must provide the recipient's PEM-encoded private key (PKCS \$18 PrivateKeyInfo structures), as defined in [RFC 5958](https://tools.ietf.org/html/rfc5958#section-2).
+ **Curve specification**

  Identifies the elliptic curve specification in the specified private key. Both the sender and recipient's key pairs must have the same curve specification.

  Valid values: `ECC_NIST_P256`, `ECC_NIS_P384`, `ECC_NIST_P512`

------
#### [ C\$1 / .NET ]

The following example creates a Raw ECDH keyring with the `PublicKeyDiscovery` key agreement schema. This keyring can decrypt any record where the public key of the specified private key matches the recipient's public key stored in the material description field of the encrypted record.

```
// Instantiate material providers
var materialProviders = new MaterialProviders(new MaterialProvidersConfig());
	    var AlicePrivateKey = new MemoryStream(new byte[] { });

	    // Create the Raw ECDH discovery keyring
	    var discoveryConfiguration = new RawEcdhStaticConfigurations()
	    {
		    PublicKeyDiscovery = new PublicKeyDiscoveryInput
		    {
			    RecipientStaticPrivateKey = AlicePrivateKey
		    }
	    };
	    
	    var createKeyringInput = new CreateRawEcdhKeyringInput() 
	    {
		    CurveSpec = ECDHCurveSpec.ECC_NIST_P256,
		    KeyAgreementScheme = discoveryConfiguration 
	    };

	    var keyring = materialProviders.CreateRawEcdhKeyring(createKeyringInput);
```

------
#### [ Java ]

The following example creates a Raw ECDH keyring with the `PublicKeyDiscovery` key agreement schema. This keyring can decrypt any record where the public key of the specified private key matches the recipient's public key stored in the material description field of the encrypted record.

```
private static void RawEcdhDiscovery() {
    // Instantiate material providers
    final MaterialProviders materialProviders =
      MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();

    KeyPair recipient = GetRawEccKey();

    // Create the Raw ECDH discovery keyring
    final CreateRawEcdhKeyringInput rawKeyringInput =
      CreateRawEcdhKeyringInput.builder()
        .curveSpec(ECDHCurveSpec.ECC_NIST_P256)
        .KeyAgreementScheme(
          RawEcdhStaticConfigurations.builder()
            .PublicKeyDiscovery(
              PublicKeyDiscoveryInput.builder()
                // Must be a PEM-encoded private key
                .recipientStaticPrivateKey(ByteBuffer.wrap(sender.getPrivate().getEncoded()))
                .build()
            )
            .build()
        ).build();

    final IKeyring publicKeyDiscovery  = materialProviders.CreateRawEcdhKeyring(rawKeyringInput);
}
```

------
#### [ Rust ]

The following example creates a Raw ECDH keyring with the `discovery_raw_ecdh_static_configuration` key agreement schema. This keyring can decrypt any message where the public key of the specified private key matches the recipient's public key stored on the message ciphertext.

```
// Create PublicKeyDiscoveryInput
let discovery_raw_ecdh_static_configuration_input =
    PublicKeyDiscoveryInput::builder()
        // Must be a UTF8 PEM-encoded private key
        .recipient_static_private_key(private_key_recipient_utf8_bytes)
        .build()?;

let discovery_raw_ecdh_static_configuration =
    RawEcdhStaticConfigurations::PublicKeyDiscovery(discovery_raw_ecdh_static_configuration_input);

// Create raw ECDH discovery private key keyring
let discovery_raw_ecdh_keyring = mpl
    .create_raw_ecdh_keyring()
    .curve_spec(ecdh_curve_spec)
    .key_agreement_scheme(discovery_raw_ecdh_static_configuration)
    .send()
    .await?;
```

------

# Multi-keyrings
<a name="use-multi-keyring"></a>


****  

|  | 
| --- |
| Our client-side encryption library was renamed to the AWS Database Encryption SDK. This developer guide still provides information on the [DynamoDB Encryption Client](legacy-dynamodb-encryption-client.md). | 

You can combine keyrings into a multi-keyring. A *multi-keyring* is a keyring that consists of one or more individual keyrings of the same or a different type. The effect is like using several keyrings in a series. When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its keyrings can decrypt that data.

When you create a multi-keyring to encrypt data, you designate one of the keyrings as the *generator keyring*. All other keyrings are known as *child keyrings*. The generator keyring generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext key and one encrypted data key for each wrapping key in the multi-keyring. If the generator keyring is a [KMS keyring](use-kms-keyring.md), the generator key in the AWS KMS keyring generates and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. 

When decrypting, the AWS Database Encryption SDK uses the keyrings to try to decrypt one of the encrypted data keys. The keyrings are called in the order that they are specified in the multi-keyring. Processing stops as soon as any key in any keyring can decrypt an encrypted data key. 

To create a multi-keyring, first instantiate the child keyrings. In this example, we use an AWS KMS keyring and a Raw AES keyring, but you can combine any supported keyrings in a multi-keyring.

------
#### [ Java ]

```
// 1. Create the raw AES keyring.
final MaterialProviders matProv = MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();
final CreateRawAesKeyringInput createRawAesKeyringInput = CreateRawAesKeyringInput.builder()
        .keyName("AES_256_012")
        .keyNamespace("HSM_01")
        .wrappingKey(AESWrappingKey)
        .wrappingAlg(AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16)
        .build();
IKeyring rawAesKeyring = matProv.CreateRawAesKeyring(createRawAesKeyringInput);

// 2. Create the AWS KMS keyring.
final CreateAwsKmsMrkMultiKeyringInput createAwsKmsMrkMultiKeyringInput = CreateAwsKmsMrkMultiKeyringInput.builder()
        .generator(kmsKeyArn)
        .build();
IKeyring awsKmsMrkMultiKeyring = matProv.CreateAwsKmsMrkMultiKeyring(createAwsKmsMrkMultiKeyringInput);
```

------
#### [ C\$1 / .NET ]

```
// 1. Create the raw AES keyring.
var keyNamespace = "HSM_01";
var keyName = "AES_256_012";
                    
var matProv = new MaterialProviders(new MaterialProvidersConfig());
var createRawAesKeyringInput = new CreateRawAesKeyringInput
{
    KeyName = "keyName",
    KeyNamespace = "myNamespaces",
    WrappingKey = AESWrappingKey,
    WrappingAlg = AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16
};
var rawAesKeyring = matProv.CreateRawAesKeyring(createRawAesKeyringInput);
                
// 2. Create the AWS KMS keyring.
//    We create a MRK multi keyring, as this interface also supports
//    single-region KMS keys,
//    and creates the KMS client for us automatically.
var createAwsKmsMrkMultiKeyringInput = new CreateAwsKmsMrkMultiKeyringInput
{
    Generator = keyArn
};
var awsKmsMrkMultiKeyring = matProv.CreateAwsKmsMrkMultiKeyring(createAwsKmsMrkMultiKeyringInput);
```

------
#### [ Rust ]

```
// 1. Create the raw AES keyring
let mpl_config = MaterialProvidersConfig::builder().build()?;
let mpl = mpl_client::Client::from_conf(mpl_config)?;

let raw_aes_keyring = mpl
    .create_raw_aes_keyring()
    .key_name("AES_256_012")
    .key_namespace("HSM_01")
    .wrapping_key(aes_key_bytes)
    .wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16)
    .send()
    .await?;                
                
// 2. Create the AWS KMS keyring
let aws_kms_mrk_multi_keyring = mpl
    .create_aws_kms_mrk_multi_keyring()
    .generator(key_arn)
    .send()
    .await?;
```

------

Next, create the multi-keyring and specify its generator keyring, if any. In this example, we create a multi-keyring in which the AWS KMS keyring is the generator keyring and the AES keyring is the child keyring.

------
#### [ Java ]

The Java `CreateMultiKeyringInput` constructor lets you define a generator keyring and child keyrings. The resulting `createMultiKeyringInput` object is immutable.

```
final CreateMultiKeyringInput createMultiKeyringInput = CreateMultiKeyringInput.builder()
        .generator(awsKmsMrkMultiKeyring)
        .childKeyrings(Collections.singletonList(rawAesKeyring))
        .build();
IKeyring multiKeyring = matProv.CreateMultiKeyring(createMultiKeyringInput);
```

------
#### [ C\$1 / .NET ]

 The .NET `CreateMultiKeyringInput` constructor lets you define a generator keyring and child keyrings. The resulting `CreateMultiKeyringInput` object is immutable.

```
var createMultiKeyringInput = new CreateMultiKeyringInput
{
    Generator = awsKmsMrkMultiKeyring,
    ChildKeyrings = new List<IKeyring> { rawAesKeyring }
};
var multiKeyring = matProv.CreateMultiKeyring(createMultiKeyringInput);
```

------
#### [ Rust ]

```
let multi_keyring = mpl
    .create_multi_keyring()
    .generator(aws_kms_mrk_multi_keyring)
    .child_keyrings(vec![raw_aes_keyring.clone()])
    .send()
    .await?;
```

------

Now, you can use the multi-keyring to encrypt and decrypt data. 