Skip to content

Commit 5bf8d2b

Browse files
committed
adding more keyrings
1 parent 437b81d commit 5bf8d2b

File tree

5 files changed

+461
-17
lines changed

5 files changed

+461
-17
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
This example sets up the AWS KMS Discovery Keyring
5+
6+
AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys.
7+
8+
The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring
9+
for AWS KMS multi-Region keys. For information about using multi-Region keys with the
10+
AWS Encryption SDK, see
11+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks
12+
13+
Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data.
14+
If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt
15+
operation fails. The exception is the AWS Encryption SDK for C, where the encrypt operation
16+
ignores a standard discovery keyring, but fails if you specify a multi-Region discovery
17+
keyring, alone or in a multi-keyring.
18+
19+
When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt
20+
any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or
21+
has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt
22+
permission on the AWS KMS key.
23+
24+
This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA
25+
with an encryption context. This encrypted ciphertext is then decrypted using the Discovery keyring.
26+
This example also includes some sanity checks for demonstration:
27+
1. Ciphertext and plaintext data are not the same
28+
2. Encryption context is correct in the decrypted message header
29+
3. Decrypted plaintext value matches EXAMPLE_DATA
30+
4. Decryption is only possible if the Discovery Keyring contains the correct AWS Account ID's to
31+
which the KMS key used for encryption belongs
32+
These sanity checks are for demonstration in the example only. You do not need these in your code.
33+
34+
For more information on how to use KMS keyrings, see
35+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery
36+
"""
37+
import sys
38+
39+
import boto3
40+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
41+
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
42+
from aws_cryptographic_materialproviders.mpl.models import CreateAwsKmsKeyringInput, CreateAwsKmsDiscoveryKeyringInput, DiscoveryFilter
43+
from aws_cryptographic_materialproviders.mpl.references import IKeyring
44+
from typing import Dict
45+
46+
import aws_encryption_sdk
47+
from aws_encryption_sdk import CommitmentPolicy
48+
from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError
49+
50+
# TODO-MPL: Remove this as part of removing PYTHONPATH hacks.
51+
MODULE_ROOT_DIR = '/'.join(__file__.split("/")[:-1])
52+
53+
sys.path.append(MODULE_ROOT_DIR)
54+
55+
EXAMPLE_DATA: bytes = b"Hello World"
56+
57+
58+
def encrypt_and_decrypt_with_keyring(
59+
kms_key_id: str
60+
):
61+
"""Demonstrate an encrypt/decrypt cycle using an AWS KMS Discovery Keyring.
62+
63+
Usage: encrypt_and_decrypt_with_keyring(kms_key_id)
64+
:param kms_key_id: KMS Key identifier for the KMS key you want to use for creating
65+
the kms_keyring used for encryption
66+
:type kms_key_id: string
67+
68+
For more information on KMS Key identifiers, see
69+
https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
70+
"""
71+
# 1. Instantiate the encryption SDK client.
72+
# This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy,
73+
# which enforces that this client only encrypts using committing algorithm suites and enforces
74+
# that this client will only decrypt encrypted messages that were created with a committing
75+
# algorithm suite.
76+
# This is the default commitment policy if you were to build the client as
77+
# `client = aws_encryption_sdk.EncryptionSDKClient()`.
78+
client = aws_encryption_sdk.EncryptionSDKClient(
79+
commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
80+
)
81+
82+
# 2. Create a boto3 client for KMS.
83+
kms_client = boto3.client('kms', region_name="us-west-2")
84+
85+
# 3. Create encryption context.
86+
# Remember that your encryption context is NOT SECRET.
87+
# For more information, see
88+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
89+
encryption_context: Dict[str, str] = {
90+
"encryption": "context",
91+
"is not": "secret",
92+
"but adds": "useful metadata",
93+
"that can help you": "be confident that",
94+
"the data you are handling": "is what you think it is",
95+
}
96+
97+
# 4. Create the keyring that determines how your data keys are protected.
98+
# Although this example highlights Discovery keyrings, Discovery keyrings cannot
99+
# be used to encrypt, so for encryption we create a KMS keyring without discovery mode.
100+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
101+
config=MaterialProvidersConfig()
102+
)
103+
104+
kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput(
105+
kms_key_id=kms_key_id,
106+
kms_client=kms_client
107+
)
108+
109+
encrypt_kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring(
110+
input=kms_keyring_input
111+
)
112+
113+
# 5. Encrypt the data for the encryptionContext
114+
ciphertext, _ = client.encrypt(
115+
source=EXAMPLE_DATA,
116+
keyring=encrypt_kms_keyring,
117+
encryption_context=encryption_context
118+
)
119+
120+
# 6. Demonstrate that the ciphertext and plaintext are different.
121+
# (This is an example for demonstration; you do not need to do this in your own code.)
122+
assert ciphertext != EXAMPLE_DATA, \
123+
"Ciphertext and plaintext data are the same. Invalid encryption"
124+
125+
# 7. Now create a Discovery keyring to use for decryption. We'll add a discovery filter
126+
# so that we limit the set of ciphertexts we are willing to decrypt to only ones
127+
# created by KMS keys in our account and partition.
128+
129+
discovery_keyring_input: CreateAwsKmsDiscoveryKeyringInput = CreateAwsKmsDiscoveryKeyringInput(
130+
kms_client=kms_client,
131+
discovery_filter=DiscoveryFilter(
132+
account_ids=["658956600833"],
133+
partition="aws"
134+
)
135+
)
136+
137+
discovery_keyring: IKeyring = mat_prov.create_aws_kms_discovery_keyring(
138+
input=discovery_keyring_input
139+
)
140+
141+
# 8. Decrypt your encrypted data using the discovery keyring.
142+
# On Decrypt, the header of the encrypted message (ciphertext) will be parsed.
143+
# The header contains the Encrypted Data Keys (EDKs), which, if the EDK
144+
# was encrypted by a KMS Keyring, includes the KMS Key ARN.
145+
# The Discovery Keyring filters these EDKs for
146+
# EDKs encrypted by Single Region OR Multi Region KMS Keys.
147+
# If a Discovery Filter is present, these KMS Keys must belong
148+
# to an AWS Account ID in the discovery filter's AccountIds and
149+
# must be from the discovery filter's partition.
150+
# Finally, KMS is called to decrypt each filtered EDK until an EDK is
151+
# successfully decrypted. The resulting data key is used to decrypt the
152+
# ciphertext's message.
153+
# If all calls to KMS fail, the decryption fails.
154+
155+
plaintext_bytes, dec_header = client.decrypt(
156+
source=ciphertext,
157+
keyring=discovery_keyring
158+
)
159+
160+
# 9. Demonstrate that the encryption context is correct in the decrypted message header
161+
# (This is an example for demonstration; you do not need to do this in your own code.)
162+
for k, v in encryption_context.items():
163+
assert v == dec_header.encryption_context[k], \
164+
"Encryption context does not match expected values"
165+
166+
# 10. Demonstrate that the decrypted plaintext is identical to the original plaintext.
167+
# (This is an example for demonstration; you do not need to do this in your own code.)
168+
assert plaintext_bytes == EXAMPLE_DATA
169+
170+
# 11. Demonstrate that if the Discovery keyring (Bob's) doesn't have the correct account id's,
171+
# the decrypt will fail with an error message
172+
discovery_keyring_input_bob: CreateAwsKmsDiscoveryKeyringInput = \
173+
CreateAwsKmsDiscoveryKeyringInput(
174+
kms_client=kms_client,
175+
discovery_filter=DiscoveryFilter(
176+
account_ids=["658956600834"],
177+
partition="aws"
178+
)
179+
)
180+
181+
discovery_keyring_bob: IKeyring = mat_prov.create_aws_kms_discovery_keyring(
182+
input=discovery_keyring_input_bob
183+
)
184+
185+
# Decrypt the ciphertext using Bob's discovery keyring which doesn't contain the required
186+
# Account ID's for the KMS keyring used for encryption
187+
try:
188+
plaintext_bytes, _ = client.decrypt(
189+
source=ciphertext,
190+
keyring=discovery_keyring_bob
191+
)
192+
193+
raise AssertionError("Decrypt using discovery keyring with wrong AWS Account ID should"
194+
+ "raise AWSEncryptionSDKClientError")
195+
except AWSEncryptionSDKClientError:
196+
pass

examples/src/keyrings/aws_kms_rsa_keyring_example.py

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@
1111
2. Encryption context is correct in the decrypted message header
1212
3. Decrypted plaintext value matches EXAMPLE_DATA
1313
These sanity checks are for demonstration in the example only. You do not need these in your code.
14-
15-
AWS KMS RSA keyrings can be used independently or in a multi-keyring with other keyrings
16-
of the same or a different type.
17-
18-
For more information on how to use KMS keyrings, see
19-
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html
2014
"""
15+
#
16+
# AWS KMS RSA keyrings can be used independently or in a multi-keyring with other keyrings
17+
# of the same or a different type.
18+
19+
# For more information on how to use KMS keyrings, see
20+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html
2121
import sys
2222

2323
import boto3
2424
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
2525
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
26-
from aws_cryptographic_materialproviders.mpl.models import CreateAwsKmsKeyringInput
26+
from aws_cryptographic_materialproviders.mpl.models import CreateAwsKmsRsaKeyringInput
2727
from aws_cryptographic_materialproviders.mpl.references import IKeyring
2828
from typing import Dict
2929

@@ -41,11 +41,11 @@
4141
def encrypt_and_decrypt_with_keyring(
4242
kms_rsa_key_id: str
4343
):
44-
"""Demonstrate an encrypt/decrypt cycle using an AWS KMS keyring.
44+
"""Demonstrate an encrypt/decrypt cycle using an AWS KMS RSA keyring.
4545
4646
Usage: encrypt_and_decrypt_with_keyring(kms_rsa_key_id)
47-
:param kms_rsa_key_id: KMS RSA Key identifier for the KMS key you want to use for encryption and
48-
decryption of your data keys.
47+
:param kms_rsa_key_id: KMS RSA Key identifier for the KMS RSA key you want to use for
48+
encryption and decryption of your data keys.
4949
:type kms_rsa_key_id: string
5050
5151
For more information on KMS Key identifiers, see
@@ -77,24 +77,37 @@ def encrypt_and_decrypt_with_keyring(
7777
"the data you are handling": "is what you think it is",
7878
}
7979

80-
# 4. Create a KMS keyring
80+
# 4. Create a KMS RSA keyring
81+
kms_rsa_public_key = '''-----BEGIN PUBLIC KEY-----
82+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ
83+
oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN
84+
/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK
85+
fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2
86+
r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9
87+
yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou
88+
twIDAQAB
89+
-----END PUBLIC KEY-----
90+
'''
91+
8192
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
8293
config=MaterialProvidersConfig()
8394
)
8495

85-
keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput(
86-
kms_rsa_key_id=kms_rsa_key_id,
96+
keyring_input: CreateAwsKmsRsaKeyringInput = CreateAwsKmsRsaKeyringInput(
97+
public_key=kms_rsa_public_key,
98+
kms_key_id=kms_rsa_key_id,
99+
encryption_algorithm="RSAES_OAEP_SHA_256",
87100
kms_client=kms_client
88101
)
89102

90-
kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring(
103+
kms_rsa_keyring: IKeyring = mat_prov.create_aws_kms_rsa_keyring(
91104
input=keyring_input
92105
)
93106

94107
# 5. Encrypt the data for the encryptionContext
95108
ciphertext, _ = client.encrypt(
96109
source=EXAMPLE_DATA,
97-
keyring=kms_keyring,
110+
keyring=kms_rsa_keyring,
98111
encryption_context=encryption_context
99112
)
100113

@@ -106,7 +119,7 @@ def encrypt_and_decrypt_with_keyring(
106119
# 7. Decrypt your encrypted data using the same keyring you used on encrypt.
107120
plaintext_bytes, dec_header = client.decrypt(
108121
source=ciphertext,
109-
keyring=kms_keyring
122+
keyring=kms_rsa_keyring
110123
)
111124

112125
# 8. Demonstrate that the encryption context is correct in the decrypted message header
@@ -117,4 +130,4 @@ def encrypt_and_decrypt_with_keyring(
117130

118131
# 9. Demonstrate that the decrypted plaintext is identical to the original plaintext.
119132
# (This is an example for demonstration; you do not need to do this in your own code.)
120-
assert plaintext_bytes == EXAMPLE_DATA
133+
assert plaintext_bytes == EXAMPLE_DATA

0 commit comments

Comments
 (0)