From c320fe5f245aef56c1bcf1b44bc64ae79080203c Mon Sep 17 00:00:00 2001 From: Jose Corella Date: Fri, 8 Nov 2024 13:06:45 -0800 Subject: [PATCH] address feedback --- .../keyring/KmsEcdhKeyringExample.java | 318 +++++++++----- .../keyring/RawEcdhKeyringExample.java | 396 +++++++++--------- .../keyring/TestKmsEcdhKeyringExample.java | 6 +- .../keyring/TestRawEcdhKeyringExample.java | 3 + .../net/src/keyring/KmsEcdhKeyringExample.cs | 268 ++++++++---- .../net/src/keyring/RawEcdhKeyringExample.cs | 381 +++++++++-------- 6 files changed, 783 insertions(+), 589 deletions(-) diff --git a/Examples/runtimes/java/DynamoDbEncryption/src/main/java/software/amazon/cryptography/examples/keyring/KmsEcdhKeyringExample.java b/Examples/runtimes/java/DynamoDbEncryption/src/main/java/software/amazon/cryptography/examples/keyring/KmsEcdhKeyringExample.java index 4bc016197..325b0aa19 100644 --- a/Examples/runtimes/java/DynamoDbEncryption/src/main/java/software/amazon/cryptography/examples/keyring/KmsEcdhKeyringExample.java +++ b/Examples/runtimes/java/DynamoDbEncryption/src/main/java/software/amazon/cryptography/examples/keyring/KmsEcdhKeyringExample.java @@ -50,6 +50,21 @@ import software.amazon.cryptography.materialproviders.model.MaterialProvidersConfig; import software.amazon.cryptography.primitives.model.ECDHCurveSpec; +/* + These examples set up DynamoDb Encryption for the AWS SDK client + using the AWS KMS ECDH Keyring. This keyring, depending on its KeyAgreement scheme, + takes in the sender's KMS ECC Key ARN, and the recipient's ECC Public Key to derive a shared secret. + The keyring uses the shared secret to derive a data key to protect the + data keys that encrypt and decrypt DynamoDb table items. + + + Running these examples require access to the DDB Table whose name + is provided in CLI arguments. + This table must be configured with the following + primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (S) + */ public class KmsEcdhKeyringExample { public static String EXAMPLE_ECC_PUBLIC_KEY_SENDER_FILENAME = @@ -57,17 +72,53 @@ public class KmsEcdhKeyringExample { public static String EXAMPLE_ECC_PUBLIC_KEY_RECIPIENT_FILENAME = "KmsEccKeyringKeyringExamplePublicKeyRecipient.pem"; + /* + This example takes in the sender's KMS ECC key ARN, the sender's public key, + the recipient's public key, and the algorithm definition where the ECC keys lie. + The eccKeyArn parameter takes in the sender's KMS ECC key ARN, + the eccPublicKeySenderFileName parameter takes in the sender's public key that corresponds to the + eccKeyArn, the eccPublicKeyRecipientFileName parameter takes in the recipient's public key, + and the Curve Specification where the keys lie. + + Both public keys MUST be UTF8 PEM-encoded X.509 public key, also known as SubjectPublicKeyInfo (SPKI), + + This example encrypts a test item using the provided ECC keys and puts the + encrypted item to the provided DynamoDb table. Then, it gets the + item from the table and decrypts it. + + Running this example requires access to the DDB Table whose name + is provided in CLI arguments. + This table must be configured with the following + primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (S) + This example also requires access to a KMS ECC key. + Our tests provide a KMS ECC Key ARN that anyone can use, but you + can also provide your own KMS ECC key. + To use your own KMS ECC key, you must have either: + - Its public key downloaded in a UTF-8 encoded PEM file + - kms:GetPublicKey permissions on that key. + If you do not have the public key downloaded, running this example + through its main method will download the public key for you + by calling kms:GetPublicKey. + You must also have kms:DeriveSharedSecret permissions on the KMS ECC key. + This example also requires a recipient ECC Public Key that lies on the same + curve as the sender public key. This examples uses another distinct + KMS ECC Public Key, it does not have to be a KMS key; it can be a + valid SubjectPublicKeyInfo (SPKI) Public Key. + */ public static void KmsEcdhKeyringGetItemPutItem( String ddbTableName, String eccKeyArn, String eccPublicKeySenderFileName, String eccPublicKeyRecipientFileName ) { - // 1. Load UTF-8 encoded public key PEM files as DER encoded bytes. - // You may have an ECC public key file already defined. - // If not, the main method in this class will call - // the KMS ECC key, retrieve its public key, and store it - // in a PEM file for example use. + // Load UTF-8 encoded public key PEM files as DER encoded bytes. + // You may provide your own PEM files to use here. If you provide this, it MUST + // be a key on curve P256. + // If not, the main method in this class will call + // the KMS ECC key, retrieve its public key, and store it + // in a PEM file for example use. ByteBuffer publicKeyRecipientByteBuffer = loadPublicKeyBytes( eccPublicKeyRecipientFileName ); @@ -75,19 +126,24 @@ public static void KmsEcdhKeyringGetItemPutItem( eccPublicKeySenderFileName ); - // 2. Create a KMS ECDH keyring. - // This keyring takes in: - // - kmsClient - // - kmsKeyId: Must be an ARN representing a KMS ECC key meant for KeyAgreement - // - curveSpec: The curve name where the public keys lie - // - senderPublicKey: A ByteBuffer of a UTF-8 encoded public - // key for the key passed into kmsKeyId in DER format - // - recipientPublicKey: A ByteBuffer of a UTF-8 encoded public key - // for the recipient public key. - final MaterialProviders matProv = MaterialProviders - .builder() - .MaterialProvidersConfig(MaterialProvidersConfig.builder().build()) - .build(); + // Create a KMS ECDH keyring. + // This keyring uses the KmsPrivateKeyToStaticPublicKey configuration. This configuration calls for both of + // the keys to be on the same curve (P256, P384, P521). + // On encrypt, the keyring calls AWS KMS to derive the shared from the sender's KMS ECC Key ARN and the recipient's public key. + // For this example, on decrypt, the keyring calls AWS KMS to derive the shared from the sender's KMS ECC Key ARN and the recipient's public key; + // however, on decrypt the recipient can construct a keyring such that the shared secret is calculated with + // the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. + // For more information on this configuration see: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-create + // The DynamoDb encryption client uses this to encrypt and decrypt items. + // This keyring takes in: + // - kmsClient + // - kmsKeyId: Must be an ARN representing a KMS ECC key meant for KeyAgreement + // - curveSpec: The curve name where the public keys lie + // - senderPublicKey: A ByteBuffer of a UTF-8 encoded public + // key for the key passed into kmsKeyId in DER format + // - recipientPublicKey: A ByteBuffer of a UTF-8 encoded public + // key for the key passed into kmsKeyId in DER format final CreateAwsKmsEcdhKeyringInput createAwsKmsEcdhKeyringInput = CreateAwsKmsEcdhKeyringInput .builder() @@ -100,13 +156,20 @@ public static void KmsEcdhKeyringGetItemPutItem( KmsPrivateKeyToStaticPublicKeyInput .builder() .senderKmsIdentifier(eccKeyArn) + // Must be a DER-encoded X.509 public key .senderPublicKey(publicKeySenderByteBuffer) + // Must be a DER-encoded X.509 public key .recipientPublicKey(publicKeyRecipientByteBuffer) .build() ) .build() ) .build(); + + final MaterialProviders matProv = MaterialProviders + .builder() + .MaterialProvidersConfig(MaterialProvidersConfig.builder().build()) + .build(); IKeyring kmsEcdhKeyring = matProv.CreateAwsKmsEcdhKeyring( createAwsKmsEcdhKeyringInput ); @@ -114,19 +177,44 @@ public static void KmsEcdhKeyringGetItemPutItem( PutGetItemWithKeyring(kmsEcdhKeyring, ddbTableName); } + /* + This example takes in the recipient's KMS ECC key ARN, + and the algorithm definition where the ECC keys lie. + The eccRecipientKeyArn parameter takes in the sender's KMS ECC key ARN + + This example attempts to decrypt a test item using the provided eccRecipientKeyArn, + it does so by checking if the message header contains the recipient's public key. + + Running this example requires access to the DDB Table whose name + is provided in CLI arguments. + This table must be configured with the following + primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (S) + This example also requires access to a KMS ECC key. + Our tests provide a KMS ECC Key ARN that anyone can use, but you + can also provide your own KMS ECC key. + To use your own KMS ECC key, you must have: + - kms:GetPublicKey permissions on that key. + This example will call kms:GetPublicKey on keyring creation. + You must also have kms:DeriveSharedSecret permissions on the KMS ECC key. + */ public static void KmsEcdhDiscoveryGetItem( String ddbTableName, String eccRecipientKeyArn ) { - // 1. Create a KMS ECDH keyring. - // This keyring takes in: - // - kmsClient - // - recipientKmsIdentifier: Must be an ARN representing a KMS ECC key meant for KeyAgreement - // - curveSpec: The curve name where the public keys lie - final MaterialProviders matProv = MaterialProviders - .builder() - .MaterialProvidersConfig(MaterialProvidersConfig.builder().build()) - .build(); + // Create a KMS ECDH keyring. + // This keyring uses the KmsPublicKeyDiscovery configuration. + // On encrypt, the keyring will fail as it is not allowed to encrypt data under this configuration. + // On decrypt, the keyring will check if its corresponding public key is stored in the message header. It + // will AWS KMS to derive the shared from the recipient's KMS ECC Key ARN and the sender's public key; + // For more information on this configuration see: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-discovery + // The DynamoDb encryption client uses this to encrypt and decrypt items. + // This keyring takes in: + // - kmsClient + // - recipientKmsIdentifier: Must be an ARN representing a KMS ECC key meant for KeyAgreement + // - curveSpec: The curve name where the public keys lie final CreateAwsKmsEcdhKeyringInput createAwsKmsEcdhKeyringInput = CreateAwsKmsEcdhKeyringInput .builder() @@ -144,6 +232,11 @@ public static void KmsEcdhDiscoveryGetItem( .build() ) .build(); + + final MaterialProviders matProv = MaterialProviders + .builder() + .MaterialProvidersConfig(MaterialProvidersConfig.builder().build()) + .build(); IKeyring kmsEcdhKeyring = matProv.CreateAwsKmsEcdhKeyring( createAwsKmsEcdhKeyringInput ); @@ -155,50 +248,50 @@ private static void GetItemWithKeyring( IKeyring kmsEcdhKeyring, String ddbTableName ) { - // 2. Configure which attributes are encrypted and/or signed when writing new items. - // For each attribute that may exist on the items we plan to write to our DynamoDbTable, - // we must explicitly configure how they should be treated during item encryption: - // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature - // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature - // - DO_NOTHING: The attribute is not encrypted and not included in the signature + // Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature final Map attributeActions = new HashMap<>(); attributeActions.put("partition_key", CryptoAction.SIGN_ONLY); // Our partition attribute must be SIGN_ONLY attributeActions.put("sort_key", CryptoAction.SIGN_ONLY); // Our sort attribute must be SIGN_ONLY attributeActions.put("sensitive_data", CryptoAction.ENCRYPT_AND_SIGN); - // 3. Configure which attributes we expect to be included in the signature - // when reading items. There are two options for configuring this: + // Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: // - // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: - // When defining your DynamoDb schema and deciding on attribute names, - // choose a distinguishing prefix (such as ":") for all attributes that - // you do not want to include in the signature. - // This has two main benefits: - // - It is easier to reason about the security and authenticity of data within your item - // when all unauthenticated data is easily distinguishable by their attribute name. - // - If you need to add new unauthenticated attributes in the future, - // you can easily make the corresponding update to your `attributeActions` - // and immediately start writing to that new attribute, without - // any other configuration update needed. - // Once you configure this field, it is not safe to update it. + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActions` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. // - // - Configure `allowedUnsignedAttributes`: You may also explicitly list - // a set of attributes that should be considered unauthenticated when encountered - // on read. Be careful if you use this configuration. Do not remove an attribute - // name from this configuration, even if you are no longer writing with that attribute, - // as old items may still include this attribute, and our configuration needs to know - // to continue to exclude this attribute from the signature scope. - // If you add new attribute names to this field, you must first deploy the update to this - // field to all readers in your host fleet before deploying the update to start writing - // with that new attribute. + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. // - // For this example, we currently authenticate all attributes. To make it easier to - // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. + // For this example, we currently authenticate all attributes. To make it easier to + // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. final String unsignAttrPrefix = ":"; - // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. - // Note: To use the KMS RSA keyring, your table config must specify an algorithmSuite - // that does not use asymmetric signing. + // Create the DynamoDb Encryption configuration for the table we will be writing to. + // Note: To use the KMS RSA keyring, your table config must specify an algorithmSuite + // that does not use asymmetric signing. final Map tableConfigs = new HashMap<>(); final DynamoDbTableEncryptionConfig config = DynamoDbTableEncryptionConfig @@ -218,7 +311,7 @@ private static void GetItemWithKeyring( .build(); tableConfigs.put(ddbTableName, config); - // 5. Create the DynamoDb Encryption Interceptor + // Create the DynamoDb Encryption Interceptor DynamoDbEncryptionInterceptor encryptionInterceptor = DynamoDbEncryptionInterceptor .builder() @@ -230,7 +323,7 @@ private static void GetItemWithKeyring( ) .build(); - // 6. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above + // Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above final DynamoDbClient ddbClient = DynamoDbClient .builder() .overrideConfiguration( @@ -241,9 +334,9 @@ private static void GetItemWithKeyring( ) .build(); - // 7. Get the item back from our table using the client. - // The client will decrypt the item client-side using the ECDH keyring - // and return the original item. + // Get the item back from our table using the client. + // The client will decrypt the item client-side using the ECDH keyring + // and return the original item. final HashMap keyToGet = new HashMap<>(); keyToGet.put( "partition_key", @@ -272,50 +365,50 @@ private static void PutGetItemWithKeyring( IKeyring awsKmsEcdhKeyring, String ddbTableName ) { - // 3. Configure which attributes are encrypted and/or signed when writing new items. - // For each attribute that may exist on the items we plan to write to our DynamoDbTable, - // we must explicitly configure how they should be treated during item encryption: - // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature - // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature - // - DO_NOTHING: The attribute is not encrypted and not included in the signature + // Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature final Map attributeActions = new HashMap<>(); attributeActions.put("partition_key", CryptoAction.SIGN_ONLY); // Our partition attribute must be SIGN_ONLY attributeActions.put("sort_key", CryptoAction.SIGN_ONLY); // Our sort attribute must be SIGN_ONLY attributeActions.put("sensitive_data", CryptoAction.ENCRYPT_AND_SIGN); - // 4. Configure which attributes we expect to be included in the signature - // when reading items. There are two options for configuring this: + // Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: // - // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: - // When defining your DynamoDb schema and deciding on attribute names, - // choose a distinguishing prefix (such as ":") for all attributes that - // you do not want to include in the signature. - // This has two main benefits: - // - It is easier to reason about the security and authenticity of data within your item - // when all unauthenticated data is easily distinguishable by their attribute name. - // - If you need to add new unauthenticated attributes in the future, - // you can easily make the corresponding update to your `attributeActions` - // and immediately start writing to that new attribute, without - // any other configuration update needed. - // Once you configure this field, it is not safe to update it. + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActions` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. // - // - Configure `allowedUnsignedAttributes`: You may also explicitly list - // a set of attributes that should be considered unauthenticated when encountered - // on read. Be careful if you use this configuration. Do not remove an attribute - // name from this configuration, even if you are no longer writing with that attribute, - // as old items may still include this attribute, and our configuration needs to know - // to continue to exclude this attribute from the signature scope. - // If you add new attribute names to this field, you must first deploy the update to this - // field to all readers in your host fleet before deploying the update to start writing - // with that new attribute. + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. // - // For this example, we currently authenticate all attributes. To make it easier to - // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. + // For this example, we currently authenticate all attributes. To make it easier to + // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. final String unsignAttrPrefix = ":"; - // 5. Create the DynamoDb Encryption configuration for the table we will be writing to. - // Note: To use the KMS RSA keyring, your table config must specify an algorithmSuite - // that does not use asymmetric signing. + // Create the DynamoDb Encryption configuration for the table we will be writing to. + // Note: To use the KMS RSA keyring, your table config must specify an algorithmSuite + // that does not use asymmetric signing. final Map tableConfigs = new HashMap<>(); final DynamoDbTableEncryptionConfig config = DynamoDbTableEncryptionConfig @@ -335,7 +428,7 @@ private static void PutGetItemWithKeyring( .build(); tableConfigs.put(ddbTableName, config); - // 6. Create the DynamoDb Encryption Interceptor + // Create the DynamoDb Encryption Interceptor DynamoDbEncryptionInterceptor encryptionInterceptor = DynamoDbEncryptionInterceptor .builder() @@ -347,7 +440,7 @@ private static void PutGetItemWithKeyring( ) .build(); - // 7. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above + // Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above final DynamoDbClient ddbClient = DynamoDbClient .builder() .overrideConfiguration( @@ -358,9 +451,9 @@ private static void PutGetItemWithKeyring( ) .build(); - // 8. Put an item into our table using the above client. - // Before the item gets sent to DynamoDb, it will be encrypted - // client-side, according to our configuration. + // Put an item into our table using the above client. + // Before the item gets sent to DynamoDb, it will be encrypted + // client-side, according to our configuration. final HashMap item = new HashMap<>(); item.put( "partition_key", @@ -383,9 +476,9 @@ private static void PutGetItemWithKeyring( // Demonstrate that PutItem succeeded assert 200 == putResponse.sdkHttpResponse().statusCode(); - // 9. Get the item back from our table using the client. - // The client will decrypt the item client-side using the RSA keyring - // and return the original item. + // Get the item back from our table using the client. + // The client will decrypt the item client-side using the RSA keyring + // and return the original item. final HashMap keyToGet = new HashMap<>(); keyToGet.put( "partition_key", @@ -460,6 +553,15 @@ public static void KmsEcdhKeyringGetItemPutItem( ); } + /* + To run the KmsEcdhKeyringGetItemPutItem as a standalone example you may provide + the following arguments in the following order. + - The DynamoDB Table Name you wish to read and write items to + - The sender's KMS ECC Key Arn, you MUST have kms:DeriveSharedSecret permission on the key. + - The sender's public key file name + - The recipient's public key file name + - The recipient's KMS ECC Key Arn, you must have kms:GetPublicKey permission on the key. + */ public static void main(final String[] args) { if (args.length <= 1) { throw new IllegalArgumentException( diff --git a/Examples/runtimes/java/DynamoDbEncryption/src/main/java/software/amazon/cryptography/examples/keyring/RawEcdhKeyringExample.java b/Examples/runtimes/java/DynamoDbEncryption/src/main/java/software/amazon/cryptography/examples/keyring/RawEcdhKeyringExample.java index 30032bbbe..41a2a8204 100644 --- a/Examples/runtimes/java/DynamoDbEncryption/src/main/java/software/amazon/cryptography/examples/keyring/RawEcdhKeyringExample.java +++ b/Examples/runtimes/java/DynamoDbEncryption/src/main/java/software/amazon/cryptography/examples/keyring/RawEcdhKeyringExample.java @@ -55,24 +55,14 @@ import software.amazon.cryptography.primitives.model.ECDHCurveSpec; /* - This example sets up DynamoDb Encryption for the AWS SDK client - using the raw ECDH Keyring. This keyring takes in the sender's ECC - private key and the recipient's ECC Public Key to derive a shared secret. + These examples set up DynamoDb Encryption for the AWS SDK client + using the raw ECDH Keyring. This keyring, depending on its KeyAgreement scheme, + takes in the sender's ECC private key, and the recipient's ECC Public Key to derive a shared secret. The keyring uses the shared secret to derive a data key to protect the data keys that encrypt and decrypt DynamoDb table items. - This example takes in the sender's private key, the recipient's - public key, and the algorithm definition where the ECC keys lie. - This parameter takes in the sender's private key as a - UTF8 PEM-encoded (PKCS #8 PrivateKeyInfo structures), the recipient's - DER-encoded X.509 public key, also known as SubjectPublicKeyInfo (SPKI), - and the Curve Specification where the keys lie. - This example encrypts a test item using the provided ECC keys and puts the - encrypted item to the provided DynamoDb table. Then, it gets the - item from the table and decrypts it. - - Running this example requires access to the DDB Table whose name + Running these examples require access to the DDB Table whose name is provided in CLI arguments. This table must be configured with the following primary key configuration: @@ -88,14 +78,36 @@ public class RawEcdhKeyringExample { public static String EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT = "RawEcdhKeyringExamplePublicKeyRecipient.pem"; + /* + This example takes in the sender's private key as a + UTF8 PEM-encoded (PKCS #8 PrivateKeyInfo structures) + located at the file location defined in EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER, + the recipient's public key as a UTF8 PEM-encoded X.509 public key, also known as SubjectPublicKeyInfo (SPKI), + located at the file location defined in EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT, + and the Curve Specification where the keys lie. + + This example encrypts a test item using the provided ECC keys and puts the + encrypted item to the provided DynamoDb table. Then, it gets the + item from the table and decrypts it. + + This examples creates a RawECDH keyring with the RawPrivateKeyToStaticPublicKey key agreement scheme. + For more information on this configuration see: + https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-RawPrivateKeyToStaticPublicKey + + On encrypt, the shared secret is derived from the sender's private key and the recipient's public key. + On decrypt, the shared secret is derived from the sender's private key and the recipient's public key; + however, on decrypt the recipient can construct a keyring such that the shared secret is calculated with + the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. + */ public static void RawEcdhKeyringGetItemPutItem( String ddbTableName, ECDHCurveSpec curveSpec ) { - // 1. Load key pair from UTF-8 encoded PEM files. - // You may provide your own PEM files to use here. - // If you do not, the main method in this class will generate PEM - // files for example use. Do not use these files for any other purpose. + // Load key pair from UTF-8 encoded PEM files. + // You may provide your own PEM files to use here. If you provide this, it MUST + // be a key on curve P256. + // If you do not, the main method in this class will generate PEM + // files for example use. Do not use these files for any other purpose. ByteBuffer privateKeyUtf8EncodedByteBuffer; try { privateKeyUtf8EncodedByteBuffer = @@ -145,10 +157,14 @@ public static void RawEcdhKeyringGetItemPutItem( throw new RuntimeException(e); } - // 2. Create the keyring. - // This keyring uses static sender and recipient keys. This configuration calls for the keys - // to be - // The DynamoDb encryption client uses this to encrypt and decrypt items. + // Create the keyring. + // This keyring uses static sender and recipient keys. This configuration calls for both of + // the keys to be on the same curve (P256, P384, P521). + // On encrypt, the shared secret is derived from the sender's private key and the recipient's public key. + // For this example, on decrypt, the shared secret is derived from the sender's private key and the recipient's public key; + // however, on decrypt the recipient can construct a keyring such that the shared secret is calculated with + // the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. + // The DynamoDb encryption client uses this to encrypt and decrypt items. final CreateRawEcdhKeyringInput keyringInput = CreateRawEcdhKeyringInput .builder() .curveSpec(curveSpec) @@ -177,14 +193,25 @@ public static void RawEcdhKeyringGetItemPutItem( PutGetExampleWithKeyring(rawEcdhKeyring, ddbTableName); } + /* + This example takes in the recipient's public key located at EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT + as a UTF8 PEM-encoded (PKCS #8 PrivateKeyInfo structures), and the Curve Specification where the key lies. + + This examples creates a RawECDH keyring with the EphemeralPrivateKeyToStaticPublicKey key agreement scheme. + This configuration will always create a new key pair as the sender key pair for the key agreement operation. + The ephemeral configuration can only encrypt data and CANNOT decrypt messages. + For more information on this configuration see: + https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-EphemeralPrivateKeyToStaticPublicKey + */ public static void EphemeralRawEcdhKeyringPutItem( String ddbTableName, ECDHCurveSpec ecdhCurveSpec ) { - // 1. Load public key from UTF-8 encoded PEM files into a DER encoded public key. - // You may provide your own PEM files to use here. - // If you do not, the main method in this class will generate PEM - // files for example use. Do not use these files for any other purpose. + // Load public key from UTF-8 encoded PEM files into a DER encoded public key. + // You may provide your own PEM files to use here. If you provide this, it MUST + // be a key on curve P256. + // If you do not, the main method in this class will generate PEM + // files for example use. Do not use these files for any other purpose. ByteBuffer publicKeyByteBuffer; try { ByteBuffer publicKeyUtf8EncodedByteBuffer = ByteBuffer.wrap( @@ -221,11 +248,11 @@ public static void EphemeralRawEcdhKeyringPutItem( throw new RuntimeException(e); } - // 2. Create the keyring. - // This keyring uses an ephemeral configuration. This configuration will always create a new - // key pair as the sender key pair for the key agreement operation. The ephemeral configuration can only - // encrypt data and CANNOT decrypt messages. - // The DynamoDb encryption client uses this to encrypt and decrypt items. + // Create the keyring. + // This keyring uses an ephemeral configuration. This configuration will always create a new + // key pair as the sender key pair for the key agreement operation. The ephemeral configuration can only + // encrypt data and CANNOT decrypt messages. + // The DynamoDb encryption client uses this to encrypt and decrypt items. final CreateRawEcdhKeyringInput keyringInput = CreateRawEcdhKeyringInput .builder() .curveSpec(ecdhCurveSpec) @@ -254,14 +281,25 @@ public static void EphemeralRawEcdhKeyringPutItem( PutExampleWithKeyring(rawEcdhKeyring, ddbTableName); } + /* + This example takes in the recipient's private key located at EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT + as a UTF8 PEM-encoded X.509 public key, also known as SubjectPublicKeyInfo (SPKI), + and the Curve Specification where the key lies. + + This examples creates a RawECDH keyring with the PublicKeyDiscovery key agreement scheme. + This scheme is only available on decrypt. + For more information on this configuration see: + https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-PublicKeyDiscovery + */ public static void DiscoveryRawEcdhKeyringGetItem( String ddbTableName, ECDHCurveSpec ecdhCurveSpec ) { - // 1. Load key pair from UTF-8 encoded PEM files. - // You may provide your own PEM files to use here. - // If you do not, the main method in this class will generate PEM - // files for example use. Do not use these files for any other purpose. + // Load key pair from UTF-8 encoded PEM files. + // You may provide your own PEM files to use here. If you provide this, it MUST + // be a key on curve P256. + // If you do not, the main method in this class will generate PEM + // files for example use. Do not use these files for any other purpose. ByteBuffer privateKeyUtf8EncodedByteBuffer; try { privateKeyUtf8EncodedByteBuffer = @@ -277,11 +315,11 @@ public static void DiscoveryRawEcdhKeyringGetItem( ); } - // 2. Create the keyring. - // This keyring uses a discovery configuration. This configuration will check on decrypt - // if it is meant to decrypt the message by checking if the configured public key is stored on the message. - // The discovery configuration can only decrypt messages and CANNOT encrypt messages. - // The DynamoDb encryption client uses this to encrypt and decrypt items. + // Create the keyring. + // This keyring uses a discovery configuration. This configuration will check on decrypt + // if it is meant to decrypt the message by checking if the configured public key is stored on the message. + // The discovery configuration can only decrypt messages and CANNOT encrypt messages. + // The DynamoDb encryption client uses this to encrypt and decrypt items. final CreateRawEcdhKeyringInput keyringInput = CreateRawEcdhKeyringInput .builder() .curveSpec(ecdhCurveSpec) @@ -313,12 +351,12 @@ public static void PutGetExampleWithKeyring( IKeyring rawEcdhKeyring, String ddbTableName ) { - // 3. Configure which attributes are encrypted and/or signed when writing new items. - // For each attribute that may exist on the items we plan to write to our DynamoDbTable, - // we must explicitly configure how they should be treated during item encryption: - // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature - // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature - // - DO_NOTHING: The attribute is not encrypted and not included in the signature + // Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature final Map attributeActionsOnEncrypt = new HashMap<>(); attributeActionsOnEncrypt.put("partition_key", CryptoAction.SIGN_ONLY); // Our partition attribute must be SIGN_ONLY attributeActionsOnEncrypt.put("sort_key", CryptoAction.SIGN_ONLY); // Our sort attribute must be SIGN_ONLY @@ -327,37 +365,37 @@ public static void PutGetExampleWithKeyring( CryptoAction.ENCRYPT_AND_SIGN ); - // 4. Configure which attributes we expect to be included in the signature - // when reading items. There are two options for configuring this: + // Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: // - // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: - // When defining your DynamoDb schema and deciding on attribute names, - // choose a distinguishing prefix (such as ":") for all attributes that - // you do not want to include in the signature. - // This has two main benefits: - // - It is easier to reason about the security and authenticity of data within your item - // when all unauthenticated data is easily distinguishable by their attribute name. - // - If you need to add new unauthenticated attributes in the future, - // you can easily make the corresponding update to your `attributeActionsOnEncrypt` - // and immediately start writing to that new attribute, without - // any other configuration update needed. - // Once you configure this field, it is not safe to update it. + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActionsOnEncrypt` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. // - // - Configure `allowedUnsignedAttributes`: You may also explicitly list - // a set of attributes that should be considered unauthenticated when encountered - // on read. Be careful if you use this configuration. Do not remove an attribute - // name from this configuration, even if you are no longer writing with that attribute, - // as old items may still include this attribute, and our configuration needs to know - // to continue to exclude this attribute from the signature scope. - // If you add new attribute names to this field, you must first deploy the update to this - // field to all readers in your host fleet before deploying the update to start writing - // with that new attribute. + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. // - // For this example, we currently authenticate all attributes. To make it easier to - // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. + // For this example, we currently authenticate all attributes. To make it easier to + // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. final String unsignAttrPrefix = ":"; - // 5. Create the DynamoDb Encryption configuration for the table we will be writing to. + // Create the DynamoDb Encryption configuration for the table we will be writing to. final Map tableConfigs = new HashMap<>(); final DynamoDbTableEncryptionConfig config = DynamoDbTableEncryptionConfig @@ -371,7 +409,7 @@ public static void PutGetExampleWithKeyring( .build(); tableConfigs.put(ddbTableName, config); - // 6. Create the DynamoDb Encryption Interceptor + // Create the DynamoDb Encryption Interceptor DynamoDbEncryptionInterceptor encryptionInterceptor = DynamoDbEncryptionInterceptor .builder() @@ -383,7 +421,7 @@ public static void PutGetExampleWithKeyring( ) .build(); - // 7. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above + // Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above final DynamoDbClient ddb = DynamoDbClient .builder() .overrideConfiguration( @@ -394,9 +432,9 @@ public static void PutGetExampleWithKeyring( ) .build(); - // 8. Put an item into our table using the above client. - // Before the item gets sent to DynamoDb, it will be encrypted - // client-side, according to our configuration. + // Put an item into our table using the above client. + // Before the item gets sent to DynamoDb, it will be encrypted + // client-side, according to our configuration. final HashMap item = new HashMap<>(); item.put( "partition_key", @@ -419,9 +457,9 @@ public static void PutGetExampleWithKeyring( // Demonstrate that PutItem succeeded assert 200 == putResponse.sdkHttpResponse().statusCode(); - // 9. Get the item back from our table using the same client. - // The client will decrypt the item client-side, and return - // back the original item. + // Get the item back from our table using the same client. + // The client will decrypt the item client-side, and return + // back the original item. final HashMap keyToGet = new HashMap<>(); keyToGet.put( "partition_key", @@ -450,12 +488,12 @@ public static void PutExampleWithKeyring( IKeyring rawEcdhKeyring, String ddbTableName ) { - // 3. Configure which attributes are encrypted and/or signed when writing new items. - // For each attribute that may exist on the items we plan to write to our DynamoDbTable, - // we must explicitly configure how they should be treated during item encryption: - // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature - // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature - // - DO_NOTHING: The attribute is not encrypted and not included in the signature + // Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature final Map attributeActionsOnEncrypt = new HashMap<>(); attributeActionsOnEncrypt.put("partition_key", CryptoAction.SIGN_ONLY); // Our partition attribute must be SIGN_ONLY attributeActionsOnEncrypt.put("sort_key", CryptoAction.SIGN_ONLY); // Our sort attribute must be SIGN_ONLY @@ -464,37 +502,37 @@ public static void PutExampleWithKeyring( CryptoAction.ENCRYPT_AND_SIGN ); - // 4. Configure which attributes we expect to be included in the signature - // when reading items. There are two options for configuring this: + // Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: // - // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: - // When defining your DynamoDb schema and deciding on attribute names, - // choose a distinguishing prefix (such as ":") for all attributes that - // you do not want to include in the signature. - // This has two main benefits: - // - It is easier to reason about the security and authenticity of data within your item - // when all unauthenticated data is easily distinguishable by their attribute name. - // - If you need to add new unauthenticated attributes in the future, - // you can easily make the corresponding update to your `attributeActionsOnEncrypt` - // and immediately start writing to that new attribute, without - // any other configuration update needed. - // Once you configure this field, it is not safe to update it. + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActionsOnEncrypt` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. // - // - Configure `allowedUnsignedAttributes`: You may also explicitly list - // a set of attributes that should be considered unauthenticated when encountered - // on read. Be careful if you use this configuration. Do not remove an attribute - // name from this configuration, even if you are no longer writing with that attribute, - // as old items may still include this attribute, and our configuration needs to know - // to continue to exclude this attribute from the signature scope. - // If you add new attribute names to this field, you must first deploy the update to this - // field to all readers in your host fleet before deploying the update to start writing - // with that new attribute. + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. // - // For this example, we currently authenticate all attributes. To make it easier to - // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. + // For this example, we currently authenticate all attributes. To make it easier to + // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. final String unsignAttrPrefix = ":"; - // 5. Create the DynamoDb Encryption configuration for the table we will be writing to. + // Create the DynamoDb Encryption configuration for the table we will be writing to. final Map tableConfigs = new HashMap<>(); final DynamoDbTableEncryptionConfig config = DynamoDbTableEncryptionConfig @@ -508,7 +546,7 @@ public static void PutExampleWithKeyring( .build(); tableConfigs.put(ddbTableName, config); - // 6. Create the DynamoDb Encryption Interceptor + // Create the DynamoDb Encryption Interceptor DynamoDbEncryptionInterceptor encryptionInterceptor = DynamoDbEncryptionInterceptor .builder() @@ -520,7 +558,7 @@ public static void PutExampleWithKeyring( ) .build(); - // 7. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above + // Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above final DynamoDbClient ddb = DynamoDbClient .builder() .overrideConfiguration( @@ -531,9 +569,9 @@ public static void PutExampleWithKeyring( ) .build(); - // 8. Put an item into our table using the above client. - // Before the item gets sent to DynamoDb, it will be encrypted - // client-side, according to our configuration. + // Put an item into our table using the above client. + // Before the item gets sent to DynamoDb, it will be encrypted + // client-side, according to our configuration. final HashMap item = new HashMap<>(); item.put( "partition_key", @@ -561,12 +599,12 @@ public static void GetExampleWithKeyring( IKeyring rawEcdhKeyring, String ddbTableName ) { - // 3. Configure which attributes are encrypted and/or signed when writing new items. - // For each attribute that may exist on the items we plan to write to our DynamoDbTable, - // we must explicitly configure how they should be treated during item encryption: - // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature - // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature - // - DO_NOTHING: The attribute is not encrypted and not included in the signature + // Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature final Map attributeActionsOnEncrypt = new HashMap<>(); attributeActionsOnEncrypt.put("partition_key", CryptoAction.SIGN_ONLY); // Our partition attribute must be SIGN_ONLY attributeActionsOnEncrypt.put("sort_key", CryptoAction.SIGN_ONLY); // Our sort attribute must be SIGN_ONLY @@ -574,37 +612,37 @@ public static void GetExampleWithKeyring( "sensitive_data", CryptoAction.ENCRYPT_AND_SIGN ); - // 4. Configure which attributes we expect to be included in the signature - // when reading items. There are two options for configuring this: + // Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: // - // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: - // When defining your DynamoDb schema and deciding on attribute names, - // choose a distinguishing prefix (such as ":") for all attributes that - // you do not want to include in the signature. - // This has two main benefits: - // - It is easier to reason about the security and authenticity of data within your item - // when all unauthenticated data is easily distinguishable by their attribute name. - // - If you need to add new unauthenticated attributes in the future, - // you can easily make the corresponding update to your `attributeActionsOnEncrypt` - // and immediately start writing to that new attribute, without - // any other configuration update needed. - // Once you configure this field, it is not safe to update it. + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActionsOnEncrypt` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. // - // - Configure `allowedUnsignedAttributes`: You may also explicitly list - // a set of attributes that should be considered unauthenticated when encountered - // on read. Be careful if you use this configuration. Do not remove an attribute - // name from this configuration, even if you are no longer writing with that attribute, - // as old items may still include this attribute, and our configuration needs to know - // to continue to exclude this attribute from the signature scope. - // If you add new attribute names to this field, you must first deploy the update to this - // field to all readers in your host fleet before deploying the update to start writing - // with that new attribute. + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. // - // For this example, we currently authenticate all attributes. To make it easier to - // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. + // For this example, we currently authenticate all attributes. To make it easier to + // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. final String unsignAttrPrefix = ":"; - // 5. Create the DynamoDb Encryption configuration for the table we will be writing to. + // Create the DynamoDb Encryption configuration for the table we will be writing to. final Map tableConfigs = new HashMap<>(); final DynamoDbTableEncryptionConfig config = DynamoDbTableEncryptionConfig @@ -618,7 +656,7 @@ public static void GetExampleWithKeyring( .build(); tableConfigs.put(ddbTableName, config); - // 6. Create the DynamoDb Encryption Interceptor + // Create the DynamoDb Encryption Interceptor DynamoDbEncryptionInterceptor encryptionInterceptor = DynamoDbEncryptionInterceptor .builder() @@ -630,7 +668,7 @@ public static void GetExampleWithKeyring( ) .build(); - // 7. Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above + // Create a new AWS SDK DynamoDb client using the DynamoDb Encryption Interceptor above final DynamoDbClient ddb = DynamoDbClient .builder() .overrideConfiguration( @@ -641,9 +679,9 @@ public static void GetExampleWithKeyring( ) .build(); - // 8. Get the item back from our table using the same client. - // The client will decrypt the item client-side, and return - // back the original item. + // Get the item back from our table using the same client. + // The client will decrypt the item client-side, and return + // back the original item. final HashMap keyToGet = new HashMap<>(); keyToGet.put( "partition_key", @@ -668,6 +706,12 @@ public static void GetExampleWithKeyring( .equals("encrypt and sign me!"); } + /* + To run this example standalone, you will need to supply your own ECC keys at: + - EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER + - EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT + You will also need to provide the Dynamodb table name as part of the args. + */ public static void main(final String[] args) { if (args.length <= 0) { throw new IllegalArgumentException( @@ -691,48 +735,12 @@ public static boolean shouldGenerateNewEccKeyPairs() { ); // If keys already exist: do not overwrite existing keys - if ( - privateKeyFileSender.exists() && - publicKeyFileRecipient.exists() && - privateKeyFileRecipient.exists() - ) { - return false; - } - - // If not all three keys are present; throw an exception - if ( - !privateKeyFileSender.exists() && - publicKeyFileRecipient.exists() && - privateKeyFileRecipient.exists() - ) { - throw new IllegalStateException( - "Missing private key sender file at " + - EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER - ); - } - if ( - privateKeyFileSender.exists() && - !publicKeyFileRecipient.exists() && - privateKeyFileRecipient.exists() - ) { - throw new IllegalStateException( - "Missing public key recipient file at " + - EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT - ); - } - if ( - privateKeyFileSender.exists() && - publicKeyFileRecipient.exists() && + return ( + !privateKeyFileSender.exists() || + !publicKeyFileRecipient.exists() || !privateKeyFileRecipient.exists() - ) { - throw new IllegalStateException( - "Missing private key recipient file at " + - EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT - ); - } - + ); // If no keys are present, generate new keys - return true; } public static void generateEccKeyPairs() { diff --git a/Examples/runtimes/java/DynamoDbEncryption/src/test/java/software/amazon/cryptography/examples/keyring/TestKmsEcdhKeyringExample.java b/Examples/runtimes/java/DynamoDbEncryption/src/test/java/software/amazon/cryptography/examples/keyring/TestKmsEcdhKeyringExample.java index 42f9f51a8..7495261af 100644 --- a/Examples/runtimes/java/DynamoDbEncryption/src/test/java/software/amazon/cryptography/examples/keyring/TestKmsEcdhKeyringExample.java +++ b/Examples/runtimes/java/DynamoDbEncryption/src/test/java/software/amazon/cryptography/examples/keyring/TestKmsEcdhKeyringExample.java @@ -12,8 +12,10 @@ public class TestKmsEcdhKeyringExample { @Test public void TestKmsEcdhKeyringExampleStatic() { - // You may provide your own ECC public keys at EXAMPLE_ECC_PUBLIC_KEY_SENDER_FILENAME - // and EXAMPLE_ECC_PUBLIC_KEY_RECIPIENT_FILENAME. + // You may provide your own ECC public keys at + // - EXAMPLE_ECC_PUBLIC_KEY_SENDER_FILENAME + // - EXAMPLE_ECC_PUBLIC_KEY_RECIPIENT_FILENAME. + // If you provide these, the keys MUST be on curve P256 // This must be the public key for the ECC key represented at eccKeyArn // If this file is not present, this will write a UTF-8 encoded PEM file for you. if (shouldGetNewPublicKeys()) { diff --git a/Examples/runtimes/java/DynamoDbEncryption/src/test/java/software/amazon/cryptography/examples/keyring/TestRawEcdhKeyringExample.java b/Examples/runtimes/java/DynamoDbEncryption/src/test/java/software/amazon/cryptography/examples/keyring/TestRawEcdhKeyringExample.java index a7f9cb252..a6ee1aff6 100644 --- a/Examples/runtimes/java/DynamoDbEncryption/src/test/java/software/amazon/cryptography/examples/keyring/TestRawEcdhKeyringExample.java +++ b/Examples/runtimes/java/DynamoDbEncryption/src/test/java/software/amazon/cryptography/examples/keyring/TestRawEcdhKeyringExample.java @@ -13,6 +13,7 @@ public void TestStaticRawEcdhKeyringExample() { // You may provide your own ECC Key pairs in the files located at // - EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER // - EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT + // If you provide this, the keys MUST be on curve P256 // If these files are not present, this will generate a pair for you. // For this example we will use the curve P256. if (RawEcdhKeyringExample.shouldGenerateNewEccKeyPairs()) { @@ -31,6 +32,7 @@ public void TestStaticRawEcdhKeyringExample() { public void TestEphemeralRawEcdhKeyringExample() { // You may provide your own ECC Public Key in the files located at // - EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT + // If you provide this, the keys MUST be on curve P256 // If these files are not present, this will generate a pair for you. // For this example we will use the curve P256. if (RawEcdhKeyringExample.shouldGenerateNewEccKeyPairs()) { @@ -50,6 +52,7 @@ public void TestDiscoveryRawEcdhKeyringExample() { // You may provide your own ECC Public Key in the files located at // - EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT // - EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT + // If you provide this, the keys MUST be on curve P256 // If these files are not present, this will generate a pair for you. // For this example we will use the curve P256. if (RawEcdhKeyringExample.shouldGenerateNewEccKeyPairs()) { diff --git a/Examples/runtimes/net/src/keyring/KmsEcdhKeyringExample.cs b/Examples/runtimes/net/src/keyring/KmsEcdhKeyringExample.cs index 42ccf9060..ae3fc8bbe 100644 --- a/Examples/runtimes/net/src/keyring/KmsEcdhKeyringExample.cs +++ b/Examples/runtimes/net/src/keyring/KmsEcdhKeyringExample.cs @@ -17,6 +17,21 @@ namespace Examples.keyring; +/* + These examples set up DynamoDb Encryption for the AWS SDK client + using the AWS KMS ECDH Keyring. This keyring, depending on its KeyAgreement scheme, + takes in the sender's KMS ECC Key ARN, and the recipient's ECC Public Key to derive a shared secret. + The keyring uses the shared secret to derive a data key to protect the + data keys that encrypt and decrypt DynamoDb table items. + + + Running these examples require access to the DDB Table whose name + is provided in CLI arguments. + This table must be configured with the following + primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (S) + */ public class KmsEcdhKeyringExample { private static String EXAMPLE_ECC_PUBLIC_KEY_SENDER_FILENAME = @@ -24,16 +39,46 @@ public class KmsEcdhKeyringExample private static String EXAMPLE_ECC_PUBLIC_KEY_RECIPIENT_FILENAME = "KmsEccKeyringKeyringExamplePublicKeyRecipient.pem"; + // This example takes in the sender's KMS ECC key ARN, the sender's public key, + // the recipient's public key, and the algorithm definition where the ECC keys lie. + // Both public keys MUST be UTF8 PEM-encoded X.509 public key, also known as SubjectPublicKeyInfo (SPKI), + // + // This example encrypts a test item using the provided ECC keys and puts the + // encrypted item to the provided DynamoDb table. Then, it gets the + // item from the table and decrypts it. + // + // Running this example requires access to the DDB Table whose name + // is provided through the TestUtils.TEST_DDB_TABLE_NAME variable. + // This table must be configured with the following + // primary key configuration: + // - Partition key is named "partition_key" with type (S) + // - Sort key is named "sort_key" with type (S) + // This example also requires access to a KMS ECC key. + // Our tests provide a KMS ECC Key ARN that anyone can use, but you + // can also provide your own KMS ECC key. + // To use your own KMS ECC key, you must have either: + // - Its public key downloaded in a UTF-8 encoded PEM file + // - kms:GetPublicKey permissions on that key. + // If you do not have the public key downloaded, running this example + // through its main method will download the public key for you + // by calling kms:GetPublicKey. + // You must also have kms:DeriveSharedSecret permissions on the KMS ECC key. + // This example also requires a recipient ECC Public Key that lies on the same + // curve as the sender public key. This examples uses another distinct + // KMS ECC Public Key, it does not have to be a KMS key; it can be a + // valid SubjectPublicKeyInfo (SPKI) Public Key. public static async Task KmsEcdhKeyringGetItemPutItem() { var ddbTableName = TestUtils.TEST_DDB_TABLE_NAME; var ecdhKeyArnSender = TestUtils.TEST_KMS_ECDH_KEY_ID_P256_SENDER; - // 1. Load public keys from UTF-8 encoded PEM files. - // You may provide your own PEM files to use here. - // If you do not, the main method in this class will generate PEM - // files for example use. Do not use these files for any other purpose. + // Load UTF-8 encoded public key PEM files as DER encoded bytes. + // You may provide your own PEM files to use here. If you provide this, it MUST + // be a key on curve P256. + // If not, the main method in this class will call + // the KMS ECC key, retrieve its public key, and store it + // in a PEM file for example use. MemoryStream publicKeySenderUtf8EncodedByteBuffer; try { @@ -58,19 +103,26 @@ public static async Task KmsEcdhKeyringGetItemPutItem() throw new IOException("Exception while reading public key from file", e); } - // 2. Create the keyring. - // The DynamoDb encryption client uses this to encrypt and decrypt items. + // Create a KMS ECDH keyring. + // This keyring uses the KmsPrivateKeyToStaticPublicKey configuration. This configuration calls for both of + // the keys to be on the same curve (P256, P384, P521). + // On encrypt, the keyring calls AWS KMS to derive the shared from the sender's KMS ECC Key ARN and the recipient's public key. + // For this example, on decrypt, the keyring calls AWS KMS to derive the shared from the sender's KMS ECC Key ARN and the recipient's public key; + // however, on decrypt the recipient can construct a keyring such that the shared secret is calculated with + // the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. + // For more information on this configuration see: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-create + // The DynamoDb encryption client uses this to encrypt and decrypt items. + // This keyring takes in: + // - kmsClient + // - kmsKeyId: Must be an ARN representing a KMS ECC key meant for KeyAgreement + // - curveSpec: The curve name where the public keys lie + // - senderPublicKey: A ByteBuffer of a UTF-8 encoded public + // key for the key passed into kmsKeyId in DER format + // - recipientPublicKey: A ByteBuffer of a UTF-8 encoded public + // key for the key passed into kmsKeyId in DER format var keyringInput = new CreateAwsKmsEcdhKeyringInput { - // 2. Create a KMS ECDH keyring. - // This keyring takes in: - // - kmsClient - // - kmsKeyId: Must be an ARN representing a KMS ECC key meant for KeyAgreement - // - curveSpec: The curve name where the public keys lie - // - senderPublicKey: A ByteBuffer of a UTF-8 encoded public - // key for the key passed into kmsKeyId in DER format - // - recipientPublicKey: A ByteBuffer of a UTF-8 encoded public key - // for the recipient public key. CurveSpec = ECDHCurveSpec.ECC_NIST_P256, KmsClient = new AmazonKeyManagementServiceClient(), KeyAgreementScheme = new KmsEcdhStaticConfigurations @@ -89,16 +141,46 @@ public static async Task KmsEcdhKeyringGetItemPutItem() await PutItemGetItemWithKeyring(kmsEcdhKeyring, ddbTableName); } + // This example takes in the recipient's KMS ECC key ARN, + // and the algorithm definition where the ECC keys lie. + // + // This example attempts to decrypt a test item using the provided eccRecipientKeyArn, + // it does so by checking if the message header contains the recipient's public key. + // + // Running this example requires access to the DDB Table whose name + // is provided through the TestUtils.TEST_DDB_TABLE_NAME variable. + // + // This table must be configured with the following + // primary key configuration: + // - Partition key is named "partition_key" with type (S) + // - Sort key is named "sort_key" with type (S) + // + // This example also requires access to a KMS ECC key. + // Our tests provide a KMS ECC Key ARN that anyone can use, but you + // can also provide your own KMS ECC key. + // + // To use your own KMS ECC key, you must have: + // - kms:GetPublicKey permissions on that key. + // + // This example will call kms:GetPublicKey on keyring creation. + // You must also have kms:DeriveSharedSecret permissions on the KMS ECC key. private static async Task KmsEcdhKeyringDiscoveryGetItem() { var ddbTableName = TestUtils.TEST_DDB_TABLE_NAME; var ecdhKeyArnRecipient = TestUtils.TEST_KMS_ECDH_KEY_ID_P256_RECIPIENT; - // 1. Create a KMS ECDH keyring. - // This keyring takes in: - // - kmsClient - // - recipientKmsIdentifier: Must be an ARN representing a KMS ECC key meant for KeyAgreement - // - curveSpec: The curve name where the public keys lie + // Create a KMS ECDH keyring. + // This keyring uses the KmsPublicKeyDiscovery configuration. + // On encrypt, the keyring will fail as it is not allowed to encrypt data under this configuration. + // On decrypt, the keyring will check if its corresponding public key is stored in the message header. It + // will AWS KMS to derive the shared from the recipient's KMS ECC Key ARN and the sender's public key; + // For more information on this configuration see: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-ecdh-keyring.html#kms-ecdh-discovery + // The DynamoDb encryption client uses this to encrypt and decrypt items. + // This keyring takes in: + // - kmsClient + // - recipientKmsIdentifier: Must be an ARN representing a KMS ECC key meant for KeyAgreement + // - curveSpec: The curve name where the public keys lie var keyringInput = new CreateAwsKmsEcdhKeyringInput { CurveSpec = ECDHCurveSpec.ECC_NIST_P256, @@ -119,12 +201,12 @@ private static async Task KmsEcdhKeyringDiscoveryGetItem() private static async Task GetItemWithKeyring(IKeyring kmsEcdhKeyring, string ddbTableName) { - // 2. Configure which attributes are encrypted and/or signed when writing new items. - // For each attribute that may exist on the items we plan to write to our DynamoDbTable, - // we must explicitly configure how they should be treated during item encryption: - // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature - // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature - // - DO_NOTHING: The attribute is not encrypted and not included in the signature + // Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature var attributeActionsOnEncrypt = new Dictionary { ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY @@ -132,37 +214,37 @@ private static async Task GetItemWithKeyring(IKeyring kmsEcdhKeyring, string ddb ["sensitive_data"] = CryptoAction.ENCRYPT_AND_SIGN }; - // 3. Configure which attributes we expect to be included in the signature - // when reading items. There are two options for configuring this: + // Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: // - // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: - // When defining your DynamoDb schema and deciding on attribute names, - // choose a distinguishing prefix (such as ":") for all attributes that - // you do not want to include in the signature. - // This has two main benefits: - // - It is easier to reason about the security and authenticity of data within your item - // when all unauthenticated data is easily distinguishable by their attribute name. - // - If you need to add new unauthenticated attributes in the future, - // you can easily make the corresponding update to your `attributeActionsOnEncrypt` - // and immediately start writing to that new attribute, without - // any other configuration update needed. - // Once you configure this field, it is not safe to update it. + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActionsOnEncrypt` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. // - // - Configure `allowedUnsignedAttributes`: You may also explicitly list - // a set of attributes that should be considered unauthenticated when encountered - // on read. Be careful if you use this configuration. Do not remove an attribute - // name from this configuration, even if you are no longer writing with that attribute, - // as old items may still include this attribute, and our configuration needs to know - // to continue to exclude this attribute from the signature scope. - // If you add new attribute names to this field, you must first deploy the update to this - // field to all readers in your host fleet before deploying the update to start writing - // with that new attribute. + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. // // For this example, we currently authenticate all attributes. To make it easier to // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. const String unsignAttrPrefix = ":"; - // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. + // Create the DynamoDb Encryption configuration for the table we will be writing to. var tableConfigs = new Dictionary { [ddbTableName] = new DynamoDbTableEncryptionConfig @@ -176,13 +258,13 @@ private static async Task GetItemWithKeyring(IKeyring kmsEcdhKeyring, string ddb } }; - // 5. Create a new AWS SDK DynamoDb client using the config above + // Create a new AWS SDK DynamoDb client using the config above var ddb = new Client.DynamoDbClient( new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); - // 6. Get the item back from our table using the same client. - // The client will decrypt the item client-side, and return - // back the original item. + // Get the item back from our table using the same client. + // The client will decrypt the item client-side, and return + // back the original item. var keyToGet = new Dictionary { ["partition_key"] = new AttributeValue("kmsEcdhKeyringItem"), @@ -205,12 +287,12 @@ private static async Task GetItemWithKeyring(IKeyring kmsEcdhKeyring, string ddb private static async Task PutItemGetItemWithKeyring(IKeyring kmsEcdhKeyring, string ddbTableName) { - // 3. Configure which attributes are encrypted and/or signed when writing new items. - // For each attribute that may exist on the items we plan to write to our DynamoDbTable, - // we must explicitly configure how they should be treated during item encryption: - // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature - // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature - // - DO_NOTHING: The attribute is not encrypted and not included in the signature + // Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature var attributeActionsOnEncrypt = new Dictionary { ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY @@ -218,37 +300,37 @@ private static async Task PutItemGetItemWithKeyring(IKeyring kmsEcdhKeyring, str ["sensitive_data"] = CryptoAction.ENCRYPT_AND_SIGN }; - // 4. Configure which attributes we expect to be included in the signature - // when reading items. There are two options for configuring this: + // Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: // - // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: - // When defining your DynamoDb schema and deciding on attribute names, - // choose a distinguishing prefix (such as ":") for all attributes that - // you do not want to include in the signature. - // This has two main benefits: - // - It is easier to reason about the security and authenticity of data within your item - // when all unauthenticated data is easily distinguishable by their attribute name. - // - If you need to add new unauthenticated attributes in the future, - // you can easily make the corresponding update to your `attributeActionsOnEncrypt` - // and immediately start writing to that new attribute, without - // any other configuration update needed. - // Once you configure this field, it is not safe to update it. + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActionsOnEncrypt` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. // - // - Configure `allowedUnsignedAttributes`: You may also explicitly list - // a set of attributes that should be considered unauthenticated when encountered - // on read. Be careful if you use this configuration. Do not remove an attribute - // name from this configuration, even if you are no longer writing with that attribute, - // as old items may still include this attribute, and our configuration needs to know - // to continue to exclude this attribute from the signature scope. - // If you add new attribute names to this field, you must first deploy the update to this - // field to all readers in your host fleet before deploying the update to start writing - // with that new attribute. + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. // - // For this example, we currently authenticate all attributes. To make it easier to - // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. + // For this example, we currently authenticate all attributes. To make it easier to + // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. const String unsignAttrPrefix = ":"; - // 5. Create the DynamoDb Encryption configuration for the table we will be writing to. + // Create the DynamoDb Encryption configuration for the table we will be writing to. var tableConfigs = new Dictionary { [ddbTableName] = new DynamoDbTableEncryptionConfig @@ -262,13 +344,13 @@ private static async Task PutItemGetItemWithKeyring(IKeyring kmsEcdhKeyring, str } }; - // 6. Create a new AWS SDK DynamoDb client using the config above + // Create a new AWS SDK DynamoDb client using the config above var ddb = new Client.DynamoDbClient( new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); - // 7. Put an item into our table using the above client. - // Before the item gets sent to DynamoDb, it will be encrypted - // client-side, according to our configuration. + // Put an item into our table using the above client. + // Before the item gets sent to DynamoDb, it will be encrypted + // client-side, according to our configuration. var item = new Dictionary { ["partition_key"] = new AttributeValue("kmsEcdhKeyringItem"), @@ -287,9 +369,9 @@ private static async Task PutItemGetItemWithKeyring(IKeyring kmsEcdhKeyring, str // Demonstrate that PutItem succeeded Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); - // 8. Get the item back from our table using the same client. - // The client will decrypt the item client-side, and return - // back the original item. + // Get the item back from our table using the same client. + // The client will decrypt the item client-side, and return + // back the original item. var keyToGet = new Dictionary { ["partition_key"] = new AttributeValue("kmsEcdhKeyringItem"), diff --git a/Examples/runtimes/net/src/keyring/RawEcdhKeyringExample.cs b/Examples/runtimes/net/src/keyring/RawEcdhKeyringExample.cs index 93691bfad..be33ff5d4 100644 --- a/Examples/runtimes/net/src/keyring/RawEcdhKeyringExample.cs +++ b/Examples/runtimes/net/src/keyring/RawEcdhKeyringExample.cs @@ -28,45 +28,53 @@ namespace Examples.keyring; -/* - This example sets up DynamoDb Encryption for the AWS SDK client - using the raw ECDH Keyring. This keyring takes in the sender's ECC - private key and the recipient's ECC Public Key to derive a shared secret. - The keyring uses the shared secret to derive a data key to protect the - data keys that encrypt and decrypt DynamoDb table items. - - This example takes in the sender's private key, the recipient's - public key, and the algorithm definition where the ECC keys lie. - This parameter takes in the sender's private key as a - UTF8 PEM-encoded (PKCS #8 PrivateKeyInfo structures), the recipient's - DER-encoded X.509 public key, also known as SubjectPublicKeyInfo (SPKI), - and the Curve Specification where the keys lie. - - This example encrypts a test item using the provided ECC keys and puts the - encrypted item to the provided DynamoDb table. Then, it gets the - item from the table and decrypts it. - - Running this example requires access to the DDB Table whose name - is provided in CLI arguments. - This table must be configured with the following - primary key configuration: - - Partition key is named "partition_key" with type (S) - - Sort key is named "sort_key" with type (S) - */ +// These examples set up DynamoDb Encryption for the AWS SDK client +// using the raw ECDH Keyring. This keyring, depending on its KeyAgreement scheme, +// takes in the sender's ECC private key, and the recipient's ECC Public Key to derive a shared secret. +// The keyring uses the shared secret to derive a data key to protect the +// data keys that encrypt and decrypt DynamoDb table items. +// +// +// Running these examples require access to the DDB Table whose name +// is provided in CLI arguments. +// This table must be configured with the following +// primary key configuration: +// - Partition key is named "partition_key" with type (S) +// - Sort key is named "sort_key" with type (S) public class RawEcdhKeyringExample { private static readonly String EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER = "RawEcdhKeyringExamplePrivateKeySender.pem"; private static readonly String EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT = "RawEcdhKeyringExamplePrivateKeyRecipient.pem"; private static readonly String EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT = "RawEcdhKeyringExamplePublicKeyRecipient.pem"; + // This example takes in the sender's private key as a + // UTF8 PEM-encoded (PKCS #8 PrivateKeyInfo structures) + // located at the file location defined in EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER, + // the recipient's public key as a UTF8 PEM-encoded X.509 public key, also known as SubjectPublicKeyInfo (SPKI), + // located at the file location defined in EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT, + // and the Curve Specification where the keys lie. + // + // This example encrypts a test item using the provided ECC keys and puts the + // encrypted item to the provided DynamoDb table. Then, it gets the + // item from the table and decrypts it. + // + // This examples creates a RawECDH keyring with the RawPrivateKeyToStaticPublicKey key agreement scheme. + // For more information on this configuration see: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-RawPrivateKeyToStaticPublicKey + // + // On encrypt, the shared secret is derived from the sender's private key and the recipient's public key. + // On decrypt, the shared secret is derived from the sender's private key and the recipient's public key; + // however, on decrypt the recipient can construct a keyring such that the shared secret is calculated with + // the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. private static async Task RawEcdhKeyringExampleGetItemPutItem() { var ddbTableName = TestUtils.TEST_DDB_TABLE_NAME; - // 1. Load key pair from UTF-8 encoded PEM files. - // You may provide your own PEM files to use here. - // If you do not, the main method in this class will generate PEM - // files for example use. Do not use these files for any other purpose. + // Load key pair from UTF-8 encoded PEM files. + // You may provide your own PEM files to use here. If you provide this, it MUST + // be a key on curve P256. + // If you do not, the main method in this class will generate PEM + // files for example use. Do not use these files for any other purpose. MemoryStream privateKeySenderUtf8EncodedByteBuffer; try { @@ -90,13 +98,16 @@ private static async Task RawEcdhKeyringExampleGetItemPutItem() throw new IOException("Exception while reading public key from file", e); } - // 2. Create the keyring. - // The DynamoDb encryption client uses this to encrypt and decrypt items. + // Create the keyring. + // This keyring uses static sender and recipient keys. This configuration calls for both of + // the keys to be on the same curve (P256, P384, P521). + // On encrypt, the shared secret is derived from the sender's private key and the recipient's public key. + // For this example, on decrypt, the shared secret is derived from the sender's private key and the recipient's public key; + // however, on decrypt the recipient can construct a keyring such that the shared secret is calculated with + // the recipient's private key and the sender's public key. In both scenarios the shared secret will be the same. + // The DynamoDb encryption client uses this to encrypt and decrypt items. var keyringInput = new CreateRawEcdhKeyringInput { - // This example uses keys that lie on the ECC Curve P256. - // The keyring supports curves P256, P384, and P521. - // On creation, the keyring verifies that all configured keys lie on the provided curve spec. CurveSpec = ECDHCurveSpec.ECC_NIST_P256, KeyAgreementScheme = new RawEcdhStaticConfigurations { @@ -119,14 +130,23 @@ private static async Task RawEcdhKeyringExampleGetItemPutItem() await PutGetExampleWithKeyring(rawEcdhKeyring, ddbTableName); } + // This example takes in the recipient's public key located at EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT + // as a UTF8 PEM-encoded (PKCS #8 PrivateKeyInfo structures), and the Curve Specification where the key lies. + // This examples creates a RawECDH keyring with the EphemeralPrivateKeyToStaticPublicKey key agreement scheme. + // This configuration will always create a new key pair as the sender key pair for the key agreement operation. + // The ephemeral configuration can only encrypt data and CANNOT decrypt messages. + // For more information on this configuration see: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-EphemeralPrivateKeyToStaticPublicKey + // private static async Task EphemeralRawEcdhKeyringPutItem() { var ddbTableName = TestUtils.TEST_DDB_TABLE_NAME; - // 1. Load key pair from UTF-8 encoded PEM files. - // You may provide your own PEM files to use here. - // If you do not, the RawEcdhKeyringExamples method in this class will generate PEM - // files for example use. Do not use these files for any other purpose. + // Load public key from UTF-8 encoded PEM files into a DER encoded public key. + // You may provide your own PEM files to use here. If you provide this, it MUST + // be a key on curve P256. + // If you do not, the main method in this class will generate PEM + // files for example use. Do not use these files for any other purpose. MemoryStream publicKeyRecipientUtf8EncodedByteBuffer; try { @@ -139,8 +159,11 @@ private static async Task EphemeralRawEcdhKeyringPutItem() throw new IOException("Exception while reading public key from file", e); } - // 2. Create the keyring. - // The DynamoDb encryption client uses this to encrypt and decrypt items. + // Create the keyring. + // This keyring uses an ephemeral configuration. This configuration will always create a new + // key pair as the sender key pair for the key agreement operation. The ephemeral configuration can only + // encrypt data and CANNOT decrypt messages. + // The DynamoDb encryption client uses this to encrypt and decrypt items. var keyringInput = new CreateRawEcdhKeyringInput { // This example uses keys that lie on the ECC Curve P256. @@ -149,10 +172,6 @@ private static async Task EphemeralRawEcdhKeyringPutItem() CurveSpec = ECDHCurveSpec.ECC_NIST_P256, KeyAgreementScheme = new RawEcdhStaticConfigurations { - // This keyring is configured with the EphemeralPrivateKeyToStaticPublicKey configuration. - // This configuration will always create a new - // key pair as the sender key pair for the key agreement operation. - // The ephemeral configuration can only encrypt data and CANNOT decrypt messages. EphemeralPrivateKeyToStaticPublicKey = new EphemeralPrivateKeyToStaticPublicKeyInput { RecipientPublicKey = publicKeyRecipientUtf8EncodedByteBuffer @@ -168,14 +187,22 @@ private static async Task EphemeralRawEcdhKeyringPutItem() await PutExampleWithKeyring(rawEcdhKeyring, ddbTableName); } + // This example takes in the recipient's private key located at EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT + // as a UTF8 PEM-encoded X.509 public key, also known as SubjectPublicKeyInfo (SPKI), + // and the Curve Specification where the key lies. + // This examples creates a RawECDH keyring with the PublicKeyDiscovery key agreement scheme. + // This scheme is only available on decrypt. + // For more information on this configuration see: + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-ecdh-keyring.html#raw-ecdh-PublicKeyDiscovery private static async Task DiscoveryRawEcdhKeyringGetItem() { var ddbTableName = TestUtils.TEST_DDB_TABLE_NAME; - // 1. Load key pair from UTF-8 encoded PEM files. - // You may provide your own PEM files to use here. - // If you do not, the main method in this class will generate PEM - // files for example use. Do not use these files for any other purpose. + // Load key pair from UTF-8 encoded PEM files. + // You may provide your own PEM files to use here. If you provide this, it MUST + // be a key on curve P256. + // If you do not, the main method in this class will generate PEM + // files for example use. Do not use these files for any other purpose. MemoryStream privateKeyRecipientUtf8EncodedByteBuffer; try { @@ -187,8 +214,11 @@ private static async Task DiscoveryRawEcdhKeyringGetItem() throw new IOException("Exception while reading private key from file", e); } - // 2. Create the keyring. - // The DynamoDb encryption client uses this to encrypt and decrypt items. + // Create the keyring. + // This keyring uses a discovery configuration. This configuration will check on decrypt + // if it is meant to decrypt the message by checking if the configured public key is stored on the message. + // The discovery configuration can only decrypt messages and CANNOT encrypt messages. + // The DynamoDb encryption client uses this to encrypt and decrypt items. var keyringInput = new CreateRawEcdhKeyringInput { // This example uses keys that lie on the ECC Curve P256. @@ -197,10 +227,6 @@ private static async Task DiscoveryRawEcdhKeyringGetItem() CurveSpec = ECDHCurveSpec.ECC_NIST_P256, KeyAgreementScheme = new RawEcdhStaticConfigurations { - // This keyring uses a discovery configuration. This configuration will check on decrypt - // if it is meant to decrypt the message by checking if the configured public key is stored on the message. - // The discovery configuration can only decrypt messages and CANNOT encrypt messages. - // The DynamoDb encryption client uses this to encrypt and decrypt items. PublicKeyDiscovery = new PublicKeyDiscoveryInput { // Must be a UTF8 PEM-encoded private key @@ -216,12 +242,12 @@ private static async Task DiscoveryRawEcdhKeyringGetItem() private static async Task PutGetExampleWithKeyring(IKeyring rawEcdhKeyring, string ddbTableName) { - // 3. Configure which attributes are encrypted and/or signed when writing new items. - // For each attribute that may exist on the items we plan to write to our DynamoDbTable, - // we must explicitly configure how they should be treated during item encryption: - // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature - // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature - // - DO_NOTHING: The attribute is not encrypted and not included in the signature + // Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature var attributeActionsOnEncrypt = new Dictionary { ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY @@ -229,37 +255,37 @@ private static async Task PutGetExampleWithKeyring(IKeyring rawEcdhKeyring, stri ["sensitive_data"] = CryptoAction.ENCRYPT_AND_SIGN }; - // 4. Configure which attributes we expect to be included in the signature - // when reading items. There are two options for configuring this: + // Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: // - // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: - // When defining your DynamoDb schema and deciding on attribute names, - // choose a distinguishing prefix (such as ":") for all attributes that - // you do not want to include in the signature. - // This has two main benefits: - // - It is easier to reason about the security and authenticity of data within your item - // when all unauthenticated data is easily distinguishable by their attribute name. - // - If you need to add new unauthenticated attributes in the future, - // you can easily make the corresponding update to your `attributeActionsOnEncrypt` - // and immediately start writing to that new attribute, without - // any other configuration update needed. - // Once you configure this field, it is not safe to update it. + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActionsOnEncrypt` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. // - // - Configure `allowedUnsignedAttributes`: You may also explicitly list - // a set of attributes that should be considered unauthenticated when encountered - // on read. Be careful if you use this configuration. Do not remove an attribute - // name from this configuration, even if you are no longer writing with that attribute, - // as old items may still include this attribute, and our configuration needs to know - // to continue to exclude this attribute from the signature scope. - // If you add new attribute names to this field, you must first deploy the update to this - // field to all readers in your host fleet before deploying the update to start writing - // with that new attribute. + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. // // For this example, we currently authenticate all attributes. To make it easier to // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. const String unsignAttrPrefix = ":"; - // 5. Create the DynamoDb Encryption configuration for the table we will be writing to. + // Create the DynamoDb Encryption configuration for the table we will be writing to. var tableConfigs = new Dictionary { [ddbTableName] = new DynamoDbTableEncryptionConfig @@ -273,13 +299,13 @@ private static async Task PutGetExampleWithKeyring(IKeyring rawEcdhKeyring, stri } }; - // 6. Create a new AWS SDK DynamoDb client using the config above + // Create a new AWS SDK DynamoDb client using the config above var ddb = new Client.DynamoDbClient( new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); - // 7. Put an item into our table using the above client. - // Before the item gets sent to DynamoDb, it will be encrypted - // client-side, according to our configuration. + // Put an item into our table using the above client. + // Before the item gets sent to DynamoDb, it will be encrypted + // client-side, according to our configuration. var item = new Dictionary { ["partition_key"] = new AttributeValue("rawEcdhKeyringItem"), @@ -298,9 +324,9 @@ private static async Task PutGetExampleWithKeyring(IKeyring rawEcdhKeyring, stri // Demonstrate that PutItem succeeded Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); - // 8. Get the item back from our table using the same client. - // The client will decrypt the item client-side, and return - // back the original item. + // Get the item back from our table using the same client. + // The client will decrypt the item client-side, and return + // back the original item. var keyToGet = new Dictionary { ["partition_key"] = new AttributeValue("rawEcdhKeyringItem"), @@ -322,12 +348,12 @@ private static async Task PutGetExampleWithKeyring(IKeyring rawEcdhKeyring, stri } private static async Task PutExampleWithKeyring(IKeyring rawEcdhKeyring, string ddbTableName) { - // 3. Configure which attributes are encrypted and/or signed when writing new items. - // For each attribute that may exist on the items we plan to write to our DynamoDbTable, - // we must explicitly configure how they should be treated during item encryption: - // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature - // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature - // - DO_NOTHING: The attribute is not encrypted and not included in the signature + // Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature var attributeActionsOnEncrypt = new Dictionary { ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY @@ -335,37 +361,37 @@ private static async Task PutExampleWithKeyring(IKeyring rawEcdhKeyring, string ["sensitive_data"] = CryptoAction.ENCRYPT_AND_SIGN }; - // 4. Configure which attributes we expect to be included in the signature - // when reading items. There are two options for configuring this: + // Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: // - // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: - // When defining your DynamoDb schema and deciding on attribute names, - // choose a distinguishing prefix (such as ":") for all attributes that - // you do not want to include in the signature. - // This has two main benefits: - // - It is easier to reason about the security and authenticity of data within your item - // when all unauthenticated data is easily distinguishable by their attribute name. - // - If you need to add new unauthenticated attributes in the future, - // you can easily make the corresponding update to your `attributeActionsOnEncrypt` - // and immediately start writing to that new attribute, without - // any other configuration update needed. - // Once you configure this field, it is not safe to update it. + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActionsOnEncrypt` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. // - // - Configure `allowedUnsignedAttributes`: You may also explicitly list - // a set of attributes that should be considered unauthenticated when encountered - // on read. Be careful if you use this configuration. Do not remove an attribute - // name from this configuration, even if you are no longer writing with that attribute, - // as old items may still include this attribute, and our configuration needs to know - // to continue to exclude this attribute from the signature scope. - // If you add new attribute names to this field, you must first deploy the update to this - // field to all readers in your host fleet before deploying the update to start writing - // with that new attribute. + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. // // For this example, we currently authenticate all attributes. To make it easier to // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. const String unsignAttrPrefix = ":"; - // 5. Create the DynamoDb Encryption configuration for the table we will be writing to. + // Create the DynamoDb Encryption configuration for the table we will be writing to. var tableConfigs = new Dictionary { [ddbTableName] = new DynamoDbTableEncryptionConfig @@ -379,11 +405,11 @@ private static async Task PutExampleWithKeyring(IKeyring rawEcdhKeyring, string } }; - // 6. Create a new AWS SDK DynamoDb client using the config above + // Create a new AWS SDK DynamoDb client using the config above var ddb = new Client.DynamoDbClient( new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); - // 7. Put an item into our table using the above client. + // Put an item into our table using the above client. // Before the item gets sent to DynamoDb, it will be encrypted // client-side, according to our configuration. var item = new Dictionary @@ -404,7 +430,7 @@ private static async Task PutExampleWithKeyring(IKeyring rawEcdhKeyring, string // Demonstrate that PutItem succeeded Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); - // 8. Try to get the item and assert that the ephemeral keyring configuration + // Try to get the item and assert that the ephemeral keyring configuration // cannot decrypt data. var keyToGet = new Dictionary { @@ -431,12 +457,12 @@ private static async Task PutExampleWithKeyring(IKeyring rawEcdhKeyring, string private static async Task GetExampleWithKeyring(IKeyring rawEcdhKeyring, string ddbTableName) { - // 3. Configure which attributes are encrypted and/or signed when writing new items. - // For each attribute that may exist on the items we plan to write to our DynamoDbTable, - // we must explicitly configure how they should be treated during item encryption: - // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature - // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature - // - DO_NOTHING: The attribute is not encrypted and not included in the signature + // Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature var attributeActionsOnEncrypt = new Dictionary { ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY @@ -444,37 +470,37 @@ private static async Task GetExampleWithKeyring(IKeyring rawEcdhKeyring, string ["sensitive_data"] = CryptoAction.ENCRYPT_AND_SIGN }; - // 4. Configure which attributes we expect to be included in the signature - // when reading items. There are two options for configuring this: + // Configure which attributes we expect to be included in the signature + // when reading items. There are two options for configuring this: // - // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: - // When defining your DynamoDb schema and deciding on attribute names, - // choose a distinguishing prefix (such as ":") for all attributes that - // you do not want to include in the signature. - // This has two main benefits: - // - It is easier to reason about the security and authenticity of data within your item - // when all unauthenticated data is easily distinguishable by their attribute name. - // - If you need to add new unauthenticated attributes in the future, - // you can easily make the corresponding update to your `attributeActionsOnEncrypt` - // and immediately start writing to that new attribute, without - // any other configuration update needed. - // Once you configure this field, it is not safe to update it. + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActionsOnEncrypt` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. // - // - Configure `allowedUnsignedAttributes`: You may also explicitly list - // a set of attributes that should be considered unauthenticated when encountered - // on read. Be careful if you use this configuration. Do not remove an attribute - // name from this configuration, even if you are no longer writing with that attribute, - // as old items may still include this attribute, and our configuration needs to know - // to continue to exclude this attribute from the signature scope. - // If you add new attribute names to this field, you must first deploy the update to this - // field to all readers in your host fleet before deploying the update to start writing - // with that new attribute. + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. // // For this example, we currently authenticate all attributes. To make it easier to // add unauthenticated attributes in the future, we define a prefix ":" for such attributes. const String unsignAttrPrefix = ":"; - // 5. Create the DynamoDb Encryption configuration for the table we will be writing to. + // Create the DynamoDb Encryption configuration for the table we will be writing to. var tableConfigs = new Dictionary { [ddbTableName] = new DynamoDbTableEncryptionConfig @@ -488,12 +514,12 @@ private static async Task GetExampleWithKeyring(IKeyring rawEcdhKeyring, string } }; - // 6. Create a new AWS SDK DynamoDb client using the config above + // Create a new AWS SDK DynamoDb client using the config above var ddb = new Client.DynamoDbClient( new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); - // 7. Attempt to put an item into our table using the above client. - // Assert that Discovery configuration cannot encrypt data + // Attempt to put an item into our table using the above client. + // Assert that Discovery configuration cannot encrypt data var item = new Dictionary { ["partition_key"] = new AttributeValue("rawEcdhKeyringItem"), @@ -516,9 +542,9 @@ private static async Task GetExampleWithKeyring(IKeyring rawEcdhKeyring, string Debug.Assert(e.Message.Contains("PublicKeyDiscovery Key Agreement Scheme is forbidden on encrypt.")); } - // 8. Get the item back from our table using the same client. - // The client will decrypt the item client-side, and return - // back the original item. + // Get the item back from our table using the same client. + // The client will decrypt the item client-side, and return + // back the original item. var keyToGet = new Dictionary { ["partition_key"] = new AttributeValue("rawEcdhKeyringItem"), @@ -560,38 +586,9 @@ public static async Task RawEcdhKeyringExamples() private static bool ShouldGenerateNewEccKeys() { // If keys already exists; do not overwrite existing keys. - if (File.Exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER) - && File.Exists(EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT) - && File.Exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT)) - { - return false; - } - - // If only two keys are present; throw exception - if (!File.Exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER) - && File.Exists(EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT) - && File.Exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT)) - { - throw new ApplicationException("Missing private key file at: " + EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER); - } - - // If only two keys are present; throw exception - if (File.Exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER) - && File.Exists(EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT) - && !File.Exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT)) - { - throw new ApplicationException("Missing private key file at: " + EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT); - } - - // If only two keys are present; throw exception - if (File.Exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER) - && !File.Exists(EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT) - && File.Exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT)) - { - throw new ApplicationException("Missing public key file at: " + EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT); - } - - return true; + return !File.Exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_SENDER) + || !File.Exists(EXAMPLE_ECC_PUBLIC_KEY_FILENAME_RECIPIENT) + || !File.Exists(EXAMPLE_ECC_PRIVATE_KEY_FILENAME_RECIPIENT); } private static void GenerateEccKeyPairs()