|
| 1 | +using AWS.Cryptography.EncryptionSDK; |
| 2 | +using AWS.Cryptography.MaterialProviders; |
| 3 | +using Xunit; |
| 4 | +using static ExampleUtils.ExampleUtils; |
| 5 | + |
| 6 | +/// Demonstrate an encrypt/decrypt cycle using a Required Encryption Context CMM. |
| 7 | +/// A required encryption context CMM asks for required keys in the encryption context field |
| 8 | +/// on encrypt such that they will not be stored on the message, but WILL be included in the header signature. |
| 9 | +/// On decrypt the client MUST supply the key/value pair(s) that were not stored to successfully decrypt the message. |
| 10 | +public class RequiredEncryptionContextExample |
| 11 | +{ |
| 12 | + private static void Run(MemoryStream plaintext) |
| 13 | + { |
| 14 | + // Create your encryption context. |
| 15 | + // Remember that your encryption context is NOT SECRET. |
| 16 | + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context |
| 17 | + var encryptionContext = new Dictionary<string, string>() |
| 18 | + { |
| 19 | + {"encryption", "context"}, |
| 20 | + {"is not", "secret"}, |
| 21 | + {"but adds", "useful metadata"}, |
| 22 | + {"that can help you", "be confident that"}, |
| 23 | + {"the data you are handling", "is what you think it is"} |
| 24 | + }; |
| 25 | + // Create your required encryption context keys. |
| 26 | + // These keys MUST be in your encryption context. |
| 27 | + // These keys and their corresponding values WILL NOT be stored on the message but will be used |
| 28 | + // for authentication. |
| 29 | + var requiredEncryptionContextKeys = new List<string>() |
| 30 | + { |
| 31 | + "encryption", |
| 32 | + "but adds", |
| 33 | + "the data you are handling" |
| 34 | + }; |
| 35 | + |
| 36 | + // Instantiate the Material Providers and the AWS Encryption SDK |
| 37 | + var materialProviders = new MaterialProviders(new MaterialProvidersConfig()); |
| 38 | + var encryptionSdk = new ESDK(new AwsEncryptionSdkConfig()); |
| 39 | + |
| 40 | + // Create a keyring via a helper method. |
| 41 | + var keyring = GetRawAESKeyring(materialProviders); |
| 42 | + |
| 43 | + // Create a required encryption context cmm via a helper method. |
| 44 | + var cmm = GetRequiredEncryptionContextCMM(materialProviders, requiredEncryptionContextKeys, keyring); |
| 45 | + |
| 46 | + // Encrypt your plaintext data. NOTE: the keys "encryption", "but adds", and "the data you are handling" |
| 47 | + // WILL NOT be stored in the message header, but "is not" and "that can help you" WILL be stored. |
| 48 | + var encryptInput = new EncryptInput |
| 49 | + { |
| 50 | + Plaintext = plaintext, |
| 51 | + MaterialsManager = cmm, |
| 52 | + EncryptionContext = encryptionContext |
| 53 | + }; |
| 54 | + var encryptOutput = encryptionSdk.Encrypt(encryptInput); |
| 55 | + var ciphertext = encryptOutput.Ciphertext; |
| 56 | + |
| 57 | + // Demonstrate that the ciphertext and plaintext are different. |
| 58 | + Assert.NotEqual(ciphertext.ToArray(), plaintext.ToArray()); |
| 59 | + |
| 60 | + // Attempt to decrypt your encrypted data using the same cryptographic material manager |
| 61 | + // you used on encrypt, but we won't pass the encryption context we DID NOT store on the message. |
| 62 | + // This will fail |
| 63 | + var decryptFailed = false; |
| 64 | + var decryptInput = new DecryptInput |
| 65 | + { |
| 66 | + Ciphertext = ciphertext, |
| 67 | + MaterialsManager = cmm, |
| 68 | + }; |
| 69 | + try |
| 70 | + { |
| 71 | + encryptionSdk.Decrypt(decryptInput); |
| 72 | + } |
| 73 | + catch (AwsCryptographicMaterialProvidersException) |
| 74 | + { |
| 75 | + decryptFailed = true; |
| 76 | + } |
| 77 | + |
| 78 | + Assert.True(decryptFailed); |
| 79 | + |
| 80 | + // Decrypt your encrypted data using the same cryptographic material manager |
| 81 | + // you used to encrypt, but supply encryption context that contains ONLY the encryption context that |
| 82 | + // was NOT stored. |
| 83 | + var reproducedEcryptionContext = new Dictionary<string, string>() |
| 84 | + { |
| 85 | + {"encryption", "context"}, |
| 86 | + {"but adds", "useful metadata"}, |
| 87 | + {"the data you are handling", "is what you think it is"} |
| 88 | + }; |
| 89 | + |
| 90 | + decryptInput = new DecryptInput |
| 91 | + { |
| 92 | + Ciphertext = ciphertext, |
| 93 | + MaterialsManager = cmm, |
| 94 | + EncryptionContext = reproducedEcryptionContext |
| 95 | + }; |
| 96 | + var decryptOutput = encryptionSdk.Decrypt(decryptInput); |
| 97 | + |
| 98 | + VerifyDecryptedIsPlaintext(decryptOutput, plaintext); |
| 99 | + } |
| 100 | + |
| 101 | + private static void VerifyDecryptedIsPlaintext(DecryptOutput decryptOutput, MemoryStream plaintext) |
| 102 | + { |
| 103 | + // Demonstrate that the decrypted plaintext is identical to the original plaintext. |
| 104 | + var decrypted = decryptOutput.Plaintext; |
| 105 | + Assert.Equal(decrypted.ToArray(), plaintext.ToArray()); |
| 106 | + } |
| 107 | + |
| 108 | + // We test examples to ensure they remain up-to-date. |
| 109 | + [Fact] |
| 110 | + public void TestRequiredEncryptionContextExample() |
| 111 | + { |
| 112 | + Run(GetPlaintextStream()); |
| 113 | + } |
| 114 | +} |
0 commit comments