I started doing PCI-DSS Audit work in 2008 (yes I know). Since then, PCI-DSS has always had “Requirement 3”, a PCI-DSS set of security requirements that were typically seen as infamous because of the number of organizations, who would just not be able to fulfill them. What was Requirement 3? It pertained “Protecting Cardholder Data”, more specifically “at rest”. And when someone says “Protecting Data at rest”, you naturally gravitate towards that one dreaded word, “Crypto”. No, I don’t mean the other dreaded avatar of “Crypto” (as in CryptoCurrency), but “Crypto” as in Cryptography.
The problem was never encrypting and decrypting data, but the greatest challenge: “How do we manage and protect keys?” Key Management is no easy task, and was even less so back then. I have seen implementations involving “home-grown” key management servers, PGP/GPG Implementations of Key Management with Symmetric Keys, Windows Executables shared between the CEO and COO that would be used to perform “Dual Custodianship of encryption keys” to something more simplistic like “Hey, we can’t encrypt and manage keys, lets look at compensating controls”. But some of you might wonder “What about Banks? Didn’t they have those fancy HSMs that could manage encryption and key management?”
Yes, Banks did have HSMs, but they used to be notoriously difficult to work with and way, way beyond the financial reach of other organizations
Which is why I smiled when I saw Amazon Key Management System (KMS). Amazon’s KMS is one among several other solutions including Hashicorp Vault, Google Key Management Service, etc that have aimed at comprehensively addressing the problem of managing keys and secrets. According to me, good Key Management systems must facilitate:
- Key Generation
- Key Rotation and Key Versioning
- Managing Secrets like API Tokens, Passwords, etc
- Comprehensive Access Control
- Logging and Audit Trail
- Dynamic Secrets and leases => Hashicorp Vault rocks this by the way!
With Amazon KMS and Amazon Secrets Manager, you can nearly do all of those things, and quite comprehensively. KMS is also backed by HSMs, which provides the necessary assurance of quality of encryption and so on.
In this article, I’ll be exploring some of the key features of Amazon KMS with Terraformand the AWS SDK for Python, boto3. This is by no means, a comprehensive tutorial, but I’ll be taking you through some of the commonly used features of KMS and Amazon Secrets Manager.
Generate and Rotate Keys
Amazon KMS utilizes the concept of Customer Master Keys (CMKs). Customer Master Keys are the primary resources of Amazon’s KMS. CMKs are symmetric keys that never leave the KMS (i.e. you never have access to the CMK itself). You can use and generate any number of CMKs for different requirements that you may have. You can use CMKs, either to directly encrypt and decrypt data. Or you could use CMKs to generate a data key that is used to encrypt and decrypt data. The latter seems to be more in vogue. In fact, the latter is what is used by Amazon S3 when you need to perform Server-Side Encryption with Amazon KMS. Either way, the CMK never leaves the confines of KMS. If the rotation for the CMK is enabled, AWS also version manages the encryption for you. For example, if you have encrypted some data with the CMK, and subsequently rotate it. Once you need to decrypt the data again, AWS recognizes that the ciphertext has been generated with the old version of the key and handles the decryption process accordingly. Thus making key rotation a simple process.
Let’s look at two examples. One with terraform, where we will use a generated CMK and directly encrypt and decrypt data with that CMK and the other, where we will generate a CMK, subsequently generate a Data Key and use the Data Key to encrypt and decrypt data.
In the above example (Terraform) I am generating a CMK and subsequently using that CMK to encrypt some plaintext. The CMK has been set to auto-rotate (after 365 days). I have also added some context to the key with the `Tags` attribute. Now, the key will be logged in Cloudtrail with this name that it’s been “tagged” with. Once you run this example you should get the payload ciphertext and subsequently the plaintext.
Now, let’s look at the next example
The above code is in Python. I am using the boto3 and pycrypto library to:
- Generate a CMK
- Generate a data key (plaintext and encrypted) with the CMK
- Encrypt some plaintext with the plaintext key, generating a data ciphertext
- Subsequently, decrypt the ciphertext with the data key
In the next example, we will be using Terraform to generate a new CMK and use Server-Side encryption with Amazon S3. In this approach, the CMK generates a data key, that is used to encrypt an object in Amazon S3
Key Policies and Grants
One of the other key aspects of Key Management, is controlling access to the Keys itself. This can be a pretty serious problem, if its not centralized or managed under a single access umbrella.
This is where its beneficial to have the Amazon IAM (Identity and Access Management) framework at our disposal. I will not go too much into the IAM framework of Amazon, but it gives you a comprehensive set of tools to provision access to resources, based on users, groups, roles and so on.
The first type of access control is the KeyPolicy. With the Key Policy, you can define who can do what with the keys in KMS. For instance, you have a serverless function that encrypts and decrypts user data. This function is managed by an IAM role, let’s call it… functionUserRole. This role should allow the serverless function to:
- Read/Write access to a specific DynamoDB Table
- Encrypt/Decrypt Data stored in the DynamoDB Table with Amazon KMS’s CMK
- Generate Data Keys that are stored with each record of the DynamoDB Table, with encrypted entries
For this, you can create an IAM Policy that allows that role to only perform the following actions.
This is an example of a Key Policy
The other way to restrict access privileges for Amazon KMS, is with the use of Grant. The concept of a Grant doesn’t require a user to generate a complex IAM configuration, but localizes the access configurations to keys within Amazon KMS.
This is an example of a Grant with Amazon KMS
In the above example, I am creating a CMK, a user (with a random user name) and a grant, called “hugh-grant” (’cause I was tired of naming resources at this point 😛). This grant allows this user to use a specific CMK for Encrypt, Decrypt and GenerateDataKey functions. The user also needs to add an encryption context (additional attributes) while performing these operations, with a key of Department with a value of finance implying that the Key and user is for the use of the Finance department of the organization.