Encryption Keys In The Cloud

Using Golang and CLI

Photo by Amol Tyagi on Unsplash

Encryption Keys In The Cloud

Using Golang and CLI

As we increasing move to the public cloud, the usage of encryption keys becomes ever more important. This includes encrypting data and for digital signing. Some advantages include:

  • Encryption in the cloud. This allows us to encrypt data in the cloud, and not have it processed on a client machine.
  • Access policies. With a cloud-based system, it is possible to define a strict access policy on the usage of encryption keys and strongly based on cloud-based roles
  • Auditing. Through AWS CloudTrail, it is possible to log the access to encryption keys, and which can support regulatory and compliance needs.
  • BYOK (Bring Your Own Keys). This allows users to create their own keys, and then upload them to the cloud.

In AWS, we have KMS (Key Management Service), and which generates and stores encryption keys. These keys never leave Amazon’s HSM (Hardware Security Modules) (HSMs) and which have been validated under FIPS 140–2. When exported, they are then only available in an encrypted form. Overall, it costs $1 per month to store a key (but keys that are generated for AWS services are stored for free). The usage of the key is free up to certain access thresholds. Also, the keys can only be used in the geographical region they are setup for (such as for “us-east-1”).

Symmetric or asymmetric keys

Overall, the encryption keys stay within a trusted hardware environment within AWS and are locked to a specific region and have a strict access policy applied related to AWS IAM (Identity and Access Management) identifier. These are related to least-privilege permissions — and where no permission is granted unless it is defined in the access policy.

Initially, we can create either a symmetric key (to be used for encryption/decryption or to generate an HMAC) or an asymmetric key (to be used for encryption or digital signing). With HMAC, we create a symmetric key which can be used to sign a message and then use the same key to verify the signature. With digital signing, we use a private (secret) key to sign a message, which is then verified by a public key.

In the AWS control, we can create a symmetric key with:

The generated key then has a Key ID, and where we define its usage (such as for encryption and decryption):

The user then defines the key alias and an ARN (Amazon Resource Names) identity. Next, we can define a policy for the usage of the key:

{
"Id": "key-consolepolicy-3",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::960039751898:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow access for Key Administrators",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::960039751898:user/bill"
},
"Action": [
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:TagResource",
"kms:UntagResource",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
],
"Resource": "*"
},
{
"Sid": "Allow use of the key",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::960039751898:user/bill"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
},
{
"Sid": "Allow attachment of persistent resources",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::960039751898:user/bill"
},
"Action": [
"kms:CreateGrant",
"kms:ListGrants",
"kms:RevokeGrant"
],
"Resource": "*",
"Condition": {
"Bool": {
"kms:GrantIsForAWSResource": "true"
}
}
}
]
}

In this case we can see that the user (bill) is allowed to encrypt and decrypt, but no other user is allowed to do these operations. The created key is then described by a Key ID and an ARN:

Now we can write a Golang program to use this key to encrypt plaintext, and then decrypt it. We first need to define the AWS region and the ARN. Along with this we also define the AWS credentials — for AWS_Access_Key_ID and AWS_Secret_Access_Key. These can either be defined in the program or within the ~/.aws/credentials folder [here]:

package main
import (
"fmt"
"os"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/aws/aws-sdk-go/aws/credentials"
)
func main() {
message := "This is a secret"
const keyID = "arn:aws:kms:us-east-1:904269751807:key/9954354-f122-45f9-b6c6-29e571562a22"
argCount := len(os.Args[1:])
if argCount > 0 {
message = os.Args[1]
}
creds := credentials.NewStaticCredentials("AWS_Access_Key_ID", "AWS_Secret_Access_Key", "")
sess, _ := session.NewSession(&aws.Config{Region: aws.String("us-east-1"), Credentials: creds})
svc := kms.New(sess)
encMethod := &kms.EncryptInput{KeyId: aws.String(keyID), Plaintext: []byte(message)}
cipherText, _:= svc.Encrypt(encMethod)
fmt.Printf("Input: %s\n", message)
fmt.Printf("Encrypted: %x\n", cipherText.CiphertextBlob)

inputDecrypt := &kms.DecryptInput{CiphertextBlob: cipherText.CiphertextBlob}
respDecrypt, _ := svc.Decrypt(inputDecrypt)
fmt.Printf("Decrypted: %s\n", string(respDecrypt.Plaintext))
}

A sample run is [here]:

Input: Testing 12345
Encrypted: 0102020078eba286059ff61e64dc8414c5fc8ff716dda2c8c47903983373a262fcc3a1503a01bfe78037a671257e78e683cd5483b3f00000006b306906092a864886f70d010706a05c305a020100305506092a864886f70d010701301e060960864801650304012e3011040c0516bdc01f1edea632e7fd6102011080282531e65f367891af4ac6c418bf804bbe0bed2e008e30cb93670089c83dc44e7e94950770b8a70488
Decrypted: Testing 12345

AWS CLI Encryption and Decryption

Along with using a programming language, we can also perform the operations with the CLI. To create a key:

aws kms create-key --tags TagKey=Purpose, TagValue=MyKey --description "For testing"

We can list our key from an alias:

> aws kms list-aliases
{
"Aliases": [
{
"AliasName": "alias/Test01",
"AliasArn": "arn:aws:kms:us-east-1:904269751807:alias/Test01",
"TargetKeyId": "9954354-f122-45f9-b6c6-29e571562a22",
"CreationDate": 1657877917.647,
"LastUpdatedDate": 1657877917.647
},

We can then encrypt a plaintext message of “Hello 123” with:

aws kms encrypt --plaintext hello.txt --key-id="9954354-f122-45f9-b1c3-29e571562a22" --encryption-context purpose=test 
{
"CiphertextBlob": "AQICAHjrooYFn/YeZNyEFMX8j/cW3aLIxHkDmDNzomL8w6FQOgE+rt03vmCwrYR5fP153zPjAAAAZzBlBgkqhkiG9w0BBwagWDBWAgEAMFEGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMNvsXMqFmyq7j5tMeAgEQgCSrzEA0EJEgrstoNvnbVfH/EaptXzqeOwADaRLMvhDo7F9G6nM=",
"KeyId": "arn:aws:kms:us-east-1:904269751807:key/9954354-f122-45f9-b6c6-29e571562a22",
"EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}

We can see we have ciphertext of “AQICAH … m4sE=”. Next, we can decrypt with:

aws kms decrypt --ciphertext-blob "AQICAHjrooYFn/YeZNyEFMX8j/cW3aLIxHkDmDNzomL8w6FQOgEnx2HXcpIIObK9qjHFGit3AAAAZzBlBgkqhkiG9w0BBwagWDBWAgEAMFEGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM/qzr60lyQS5A5yZ8AgEQgCQueKSLpCjdQiYHKWPX3NUT87cOvcmrT/RDYlBcDge7Ymzm4sE=" --key-id "9954354-d28-45f9-b1c3-29e571562a22"

Asymmetric keys and HMAC

With asymmetric keys, we can select RSA 2K, 3K or 4K:

For digital signing, we can add the elliptic curve methods of P256, P521 and sepc256k1:

Conclusions

One of the most costly events within cybersecurity is the breach of a trust infrastructure. The management and control of keys is thus so important, and storing them in the Cloud brings many benefits, including a method of controlling and auditing their usage. Along with this, we see too many data breaches, and the usage of encryption allows the scope of any data leakage to reduce to almost zero — but we must ensure that our keys are kept securely and are, at least, backed up in a secret way.

But … beware … these keys are you most important secrets, so don’t get locked into one cloud provider, and there is still a risk that someone can get into your AWS environment and discover your keys

I have created a demo here:

https://asecuritysite.com/golang/go_aws