As per Amazon, AWS Key Management Service (AWS KMS) is a managed service that makes it easy for you to create and control the encryption keys used to encrypt your data. The master keys that you create in AWS KMS are protected by FIPS 140-2 validated cryptographic modules.
The primary resources in AWS KMS are customer master keys (CMKs). You can use a CMK to encrypt and decrypt up to 4 KB (4096 bytes) of data. Typically, you use CMKs to generate, encrypt, and decrypt the data keys that you use outside of AWS KMS to encrypt your data. This strategy is known as envelope encryption.
The primary resources in AWS KMS are customer master keys (CMKs). You can use a CMK to encrypt and decrypt up to 4 KB (4096 bytes) of data. Typically, you use CMKs to generate, encrypt, and decrypt the data keys that you use outside of AWS KMS to encrypt your data. This strategy is known as envelope encryption.
Encryption process
- Generate a data key using the GenerateDataKey operation of AWS KMS. The operation returns a plaintext copy of the data key and a copy of the data key encrypted using the master key (CMK)
- Encrypt the data with the plain data key and destroy the key
- We can store this encrypted key along with our encrypted message.The message can’t be decrypted unless you can decrypt the data key and the data key can’t be decrypted unless you have access to the master key.
Decryption process
- Decrypt the encrypted key using our master key to get the plain data key
- Decrypt the data with the plain data key
Following is the helper class for Encrypt or Decrypt the data with KMS key in .NET/.NET Core with C#. AWSSDK.KeyManagementService is the nuget package required to work with AWS KMS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | using Amazon.KeyManagementService; using Amazon.KeyManagementService.Model; using Amazon.Runtime; using log4net; using System; using System.IO; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; public class KMSHelper { private AmazonKeyManagementServiceClient client; private static readonly ILog _logger = LogManager.GetLogger( typeof (KMSHelper)); private readonly string accessKey, secretKey, serviceUrl; public KMSHelper( string accessKey, string secretKey, string serviceUrl) { this .accessKey = accessKey; this .secretKey = secretKey; this .serviceUrl = serviceUrl; client = GetClient(); } private AmazonKeyManagementServiceClient GetClient() { if (client == null ) { try { // DynamoDB config object AmazonKeyManagementServiceConfig clientConfig = new AmazonKeyManagementServiceConfig { // Set the endpoint URL ServiceURL = serviceUrl }; client = new AmazonKeyManagementServiceClient(accessKey, secretKey, clientConfig); } catch (AmazonKeyManagementServiceException ex) { _logger.Error($ "Error (AmazonKeyManagementServiceException) creating KMS client" , ex); } catch (AmazonServiceException ex) { _logger.Error($ "Error (AmazonServiceException) creating KMS client" , ex); } catch (Exception ex) { _logger.Error($ "Error creating KMS client" , ex); } } return client; } /// <summary> /// Generates new data key under the master key /// </summary> /// <param name="masterKeyId">Master key</param> /// <returns></returns> private async Task<MemoryStream> GenerateDataKey( string masterKeyId) { using (var kmsClient = GetClient()) { var result = await kmsClient.GenerateDataKeyAsync( new GenerateDataKeyRequest { KeyId = masterKeyId, KeySpec = DataKeySpec.AES_256 }); return result.CiphertextBlob; } } /// <summary> /// Decrypts the data key using the master key /// </summary> /// <param name="ciphertext">Stream to decrypt</param> /// <returns></returns> private async Task<MemoryStream> DecryptDataKey(MemoryStream ciphertext) { using (var kmsClient = GetClient()) { var decryptionResponse = await kmsClient.DecryptAsync( new DecryptRequest { CiphertextBlob = ciphertext }); return decryptionResponse.Plaintext; } } /// <summary> /// Encrypts the data key using the master key /// </summary> /// <param name="masterKeyId">Master key arn</param> /// <param name="plaintext">Plain data key</param> /// <returns></returns> private async Task< string > EncryptDataKey( string masterKeyId, MemoryStream plaintext) { using (var kmsClient = new AmazonKeyManagementServiceClient(accessKey, secretKey, serviceUrl)) { var encryptionResponse = await kmsClient.EncryptAsync( new EncryptRequest { KeyId = masterKeyId, Plaintext = plaintext }); return Convert.ToBase64String(encryptionResponse.CiphertextBlob.ToArray()); } } /// <summary> /// Encrypts the text using the master key /// </summary> /// <param name="textToEncrypt">Text to encrypt</param> /// <param name="masterKeyId">Master key arn</param> /// <returns></returns> public async Task< string > Encrypt( byte [] textToEncrypt, string masterKeyId) { var kmsClient = new AmazonKeyManagementServiceClient(accessKey, secretKey, serviceUrl); using (var algorithm = Aes.Create()) { // Create the streams used for encryption. using (MemoryStream msEncrypt = new MemoryStream()) { // Generates the data key under the master key var dataKey = await kmsClient.GenerateDataKeyAsync( new GenerateDataKeyRequest { KeyId = masterKeyId, KeySpec = DataKeySpec.AES_256 }); msEncrypt.WriteByte(( byte )dataKey.CiphertextBlob.Length); dataKey.CiphertextBlob.CopyTo(msEncrypt); algorithm.Key = dataKey.Plaintext.ToArray(); // Writing algorithm.IV in output stream for decryption purpose. msEncrypt.Write(algorithm.IV, 0, algorithm.IV.Length); // Create a decrytor to perform the stream transform. ICryptoTransform encryptor = algorithm.CreateEncryptor(algorithm.Key, algorithm.IV); using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (MemoryStream input = new MemoryStream(textToEncrypt)) { input.CopyTo(csEncrypt); csEncrypt.FlushFinalBlock(); } return Convert.ToBase64String(msEncrypt.ToArray()); } } } } /// <summary> /// Decrypts the text encrypted with the master key /// </summary> /// <param name="textToDecrypt">Encrypted text to decrypt</param> /// <returns></returns> public async Task< string > Decrypt( byte [] textToDecrypt) { // Create the streams used for decryption. using (MemoryStream msDecrypt = new MemoryStream(textToDecrypt)) { var length = msDecrypt.ReadByte(); var buffer = new byte [length]; msDecrypt.Read(buffer, 0, length); // Decrypt the datakey MemoryStream dataKeyCipher = await DecryptDataKey( new MemoryStream(buffer)); using (var algorithm = Aes.Create()) { algorithm.Key = dataKeyCipher.ToArray(); var iv = algorithm.IV; msDecrypt.Read(iv, 0, iv.Length); algorithm.IV = iv; // Create a decrytor to perform the stream transform. ICryptoTransform decryptor = algorithm.CreateDecryptor(algorithm.Key, algorithm.IV); using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) { using (MemoryStream srDecrypt = new MemoryStream()) { csDecrypt.CopyTo(srDecrypt); //Write all data to the stream. return Encoding.ASCII.GetString(srDecrypt.ToArray()); } } } } } } |
Happy Coding!😊
Very helpful!
ReplyDeleteI'm getting an invalid ciphertext. It was encrypted by Connect. Thoughts?
ReplyDeleteThe ciphertext given to the decryption process should be generated using the encryption method in the above helper method only. Because in the encryption process we are storing the masterkey cipher text blob in the generated cipher text.
DeleteHi ,
ReplyDeleteI am getting exception in the following line in Encrypt method .
var dataKey = await kmsClient.GenerateDataKeyAsync(new GenerateDataKeyRequest
{
KeyId = masterKeyId,
KeySpec = DataKeySpec.AES_256
});
There is no error message displayed. KeyID i am passing ARN.
Regards
Arun
I'm getting CryptographicException: The input data is not a complete block. Any thoughts to this issue
ReplyDeleteI also encountering the same issue as the user above, I am amble to encrypt the data but when i pass the same string to the decrypt data method, I get invalidciphertext error
ReplyDeleteWhile passing it to decrypt method you should do this
DeleteDecrypt(Convert.FromBase64String(encryptedText))