Skip to content

Commit 1a01d32

Browse files
committed
docs: add MKP combination example
1 parent d6b86c7 commit 1a01d32

File tree

3 files changed

+164
-0
lines changed

3 files changed

+164
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
Master key provider examples.
5+
6+
These examples show how to use master key providers.
7+
"""
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
Multi-master key provider examples.
5+
6+
These examples show how to combine master key providers.
7+
"""
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
This example is provided as a reference for users migrating away from master key providers.
5+
We recommend that all new use should use keyrings.
6+
For examples using keyrings, see the ``examples/src/keyrings`` directory.
7+
8+
One use-case that we have seen customers need is
9+
the ability to enjoy the benefits of AWS KMS during normal operation
10+
but retain the ability to decrypt encrypted messages without access to AWS KMS.
11+
This example shows how you can achieve this
12+
by combining a KMS master key with a raw RSA master key.
13+
14+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#master-key-provider
15+
16+
For more examples of how to use the KMS master key provider, see the ``master_key_provider/aws_kms`` examples.
17+
18+
For more examples of how to use the raw RSA master key, see the ``master_key_provider/raw_rsa`` examples.
19+
20+
In this example we generate a RSA keypair
21+
but in practice you would want to keep your private key in an HSM
22+
or other key management system.
23+
24+
In this example, we use the one-step encrypt and decrypt APIs.
25+
"""
26+
from cryptography.hazmat.backends import default_backend
27+
from cryptography.hazmat.primitives import serialization
28+
from cryptography.hazmat.primitives.asymmetric import rsa
29+
30+
import aws_encryption_sdk
31+
from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm
32+
from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider
33+
from aws_encryption_sdk.key_providers.raw import RawMasterKey, WrappingKey
34+
35+
36+
def run(aws_kms_cmk, source_plaintext):
37+
# type: (str, bytes) -> None
38+
"""Demonstrate configuring a master key provider to use an AWS KMS CMK and a RSA wrapping key.
39+
40+
:param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys
41+
:param bytes source_plaintext: Plaintext to encrypt
42+
"""
43+
# Prepare your encryption context.
44+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
45+
encryption_context = {
46+
"encryption": "context",
47+
"is not": "secret",
48+
"but adds": "useful metadata",
49+
"that can help you": "be confident that",
50+
"the data you are handling": "is what you think it is",
51+
}
52+
53+
# Generate an RSA private key to use with your master key.
54+
# In practice, you should get this key from a secure key management system such as an HSM.
55+
#
56+
# The National Institute of Standards and Technology (NIST) recommends a minimum of 2048-bit keys for RSA.
57+
# https://www.nist.gov/publications/transitioning-use-cryptographic-algorithms-and-key-lengths
58+
#
59+
# Why did we use this public exponent?
60+
# https://crypto.stanford.edu/~dabo/pubs/papers/RSA-survey.pdf
61+
private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend())
62+
63+
# Serialize the RSA private key to PEM encoding.
64+
# This or DER encoding is likely to be what you get from your key management system in practice.
65+
private_key_pem = private_key.private_bytes(
66+
encoding=serialization.Encoding.PEM,
67+
format=serialization.PrivateFormat.PKCS8,
68+
encryption_algorithm=serialization.NoEncryption(),
69+
)
70+
71+
# Collect the public key from the private key.
72+
public_key = private_key.public_key()
73+
74+
# Serialize the RSA public key to PEM encoding.
75+
# This or DER encoding is likely to be what you get from your key management system in practice.
76+
public_key_pem = public_key.public_bytes(
77+
encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo,
78+
)
79+
80+
# Create the encrypt master key that only has access to the public key.
81+
escrow_encrypt_master_key = RawMasterKey(
82+
# The provider ID and key ID are defined by you
83+
# and are used by the raw RSA master key
84+
# to determine whether it should attempt to decrypt
85+
# an encrypted data key.
86+
provider_id="some managed raw keys", # provider ID corresponds to key namespace for keyrings
87+
key_id=b"my RSA wrapping key", # key ID corresponds to key name for keyrings
88+
wrapping_key=WrappingKey(
89+
wrapping_key=public_key_pem,
90+
wrapping_key_type=EncryptionKeyType.PUBLIC,
91+
# The wrapping algorithm tells the raw RSA master key
92+
# how to use your wrapping key to encrypt data keys.
93+
#
94+
# We recommend using RSA_OAEP_SHA256_MGF1.
95+
# You should not use RSA_PKCS1 unless you require it for backwards compatibility.
96+
wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1,
97+
),
98+
)
99+
100+
# Create the decrypt master key that has access to the private key.
101+
escrow_decrypt_master_key = RawMasterKey(
102+
# The key namespace and key name MUST match the encrypt master key.
103+
provider_id="some managed raw keys", # provider ID corresponds to key namespace for keyrings
104+
key_id=b"my RSA wrapping key", # key ID corresponds to key name for keyrings
105+
wrapping_key=WrappingKey(
106+
wrapping_key=private_key_pem,
107+
wrapping_key_type=EncryptionKeyType.PRIVATE,
108+
# The wrapping algorithm MUST match the encrypt master key.
109+
wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1,
110+
),
111+
)
112+
113+
# Create the KMS master key that you will use for decryption during normal operations.
114+
kms_master_key = KMSMasterKeyProvider(key_ids=[aws_kms_cmk])
115+
116+
# Add the escrow encrypt master key to the KMS master key.
117+
kms_master_key.add_master_key_provider(escrow_encrypt_master_key)
118+
119+
# Encrypt your plaintext data using the combined master keys.
120+
ciphertext, encrypt_header = aws_encryption_sdk.encrypt(
121+
source=source_plaintext, encryption_context=encryption_context, key_provider=kms_master_key
122+
)
123+
124+
# Verify that the header contains the expected number of encrypted data keys (EDKs).
125+
# It should contain one EDK for KMS and one for the escrow key.
126+
assert len(encrypt_header.encrypted_data_keys) == 2
127+
128+
# Demonstrate that the ciphertext and plaintext are different.
129+
assert ciphertext != source_plaintext
130+
131+
# Decrypt your encrypted data separately using the KMS master key and the escrow decrypt master key.
132+
#
133+
# You do not need to specify the encryption context on decrypt
134+
# because the header of the encrypted message includes the encryption context.
135+
decrypted_kms, decrypt_header_kms = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=kms_master_key)
136+
decrypted_escrow, decrypt_header_escrow = aws_encryption_sdk.decrypt(
137+
source=ciphertext, key_provider=escrow_decrypt_master_key
138+
)
139+
140+
# Demonstrate that the decrypted plaintext is identical to the original plaintext.
141+
assert decrypted_kms == source_plaintext
142+
assert decrypted_escrow == source_plaintext
143+
144+
# Verify that the encryption context used in the decrypt operation includes
145+
# the encryption context that you specified when encrypting.
146+
# The AWS Encryption SDK can add pairs, so don't require an exact match.
147+
#
148+
# In production, always use a meaningful encryption context.
149+
assert set(encryption_context.items()) <= set(decrypt_header_kms.encryption_context.items())
150+
assert set(encryption_context.items()) <= set(decrypt_header_escrow.encryption_context.items())

0 commit comments

Comments
 (0)