Does AWS KMS use envelope encryption? - amazon-web-services

Encryption max data size allowed for AWS KMS is 4kb, so whenever we use encryption in AWS services/resources is Encryption done using envelope encryption? , i.e, data is encrypted at resource side itself with the key and key is encrypted with another key(cmk)and stored along with the data and decryption happens in the reverse order of above steps. Is my understanding correct??

Probably. It seems to be at least true for S3:
Server-side encryption protects data at rest. Amazon S3 encrypts each object with a unique key. As an additional safeguard, it encrypts the key itself with a master key that it rotates regularly. Amazon S3 server-side encryption uses one of the strongest block ciphers available to encrypt your data, 256-bit Advanced Encryption Standard (AES-256).

Generally the CMK is not used for encrypting the data that you are looking to encrypt.
Whilst it’s a matter of opinion on the 4kb limit, data encryption keys provide a more secure approach to encrypting the data.
Because each resource could have its own data encryption key, the risk is reduced of having all of your resources decrypted if a single encryption key is compromised (in fact if this happens KMS supports re encryption to generate a new data key).
What you describe is correct for S3 implementation of KMS. A Base64 encoded encrypted key is stored alongside the object it encrypts. To decrypt S3 needs to decrypt the data key for the object using the CMK, then use the decrypted data encryption key to decrypt the object.
Other services will have different implementations, for example DynamoDB does this on a per table basis.
For more information on how each service has implemented KMS take a look at the How AWS Services use AWS KMS page

Aws kms does not store any data it provide you two keys
1 plain key : with the help of it you encrypt the data and delete it(key)(no need to save anywhere).
2.encrypted data key :- you need to save this key to decrypt the data( to decrypt the data first you got plain key from aws using encrypted data key) and with the help of plain key you decrypt the data.
Note you need aws kms credentials like :-
a)serviceEndPoint b)awsKeyForKMS c)kmsConfig
KMS Encryption and Decryption in asp.net mvc
Name space need to add from nuget packeg
using Amazon.KeyManagementService;
using Amazon.KeyManagementService.Model;
**1) Encryption :-**
AmazonKeyManagementServiceConfig kmsConfig = new AmazonKeyManagementServiceConfig();
kmsConfig.UseHttp = true;
kmsConfig.ServiceURL = serviceEndPoint;
//create client, specify Region end point or kms config
AmazonKeyManagementServiceClient kmsClient = new AmazonKeyManagementServiceClient(awsKeyForKMS, awsSecretKeyForKMS, kmsConfig);
GenerateDataKeyRequest dataKeyReq = new GenerateDataKeyRequest();
dataKeyReq.KeyId = keyARNForKMS;
dataKeyReq.KeySpec = DataKeySpec.AES_256;//The length of the data encryption key. AES_256 to generate a 256-bit symmetric key.
GenerateDataKeyResponse dataKeyResponse = kmsClient.GenerateDataKey(dataKeyReq);
//read encrypted data key from memory
MemoryStream streamCipherText = dataKeyResponse.CiphertextBlob;
// need to save this key with encrypted data because with the help of it
// you can decrypt(you got plaindatakey) the data
encryptedDataKey = Convert.ToBase64String(streamCipherText.ToArray());
//read plain data key from memory
MemoryStream streamPlainText = dataKeyResponse.Plaintext;
// use this key to encrypt your data and than forgot this key
plainDataKey = Convert.ToBase64String(streamPlainText.ToArray());
//your encryption logic
Encryption encrypt = new Encryption();
encrypt.EncryptTextForKms(PlainKey, "data to be encrypted")
**2.Decryption Data:-**
AmazonKeyManagementServiceConfig kmsConfig = new AmazonKeyManagementServiceConfig();
kmsConfig.UseHttp = true;
kmsConfig.ServiceURL = serviceEndPoint;
//create client, specify Region end point or kms config
AmazonKeyManagementServiceClient kmsClient = new AmazonKeyManagementServiceClient(awsKeyForKMS, awsSecretKeyForKMS, kmsConfig);
DecryptRequest decryptRequest = new DecryptRequest();
// use hare above created encrypteddatakey to get plaindatakey
MemoryStream streamEncryptedDataKey = new MemoryStream(Convert.FromBase64String(encryptedDataKey));//convert to stream object
decryptRequest.CiphertextBlob = streamEncryptedDataKey;
DecryptResponse decryptResp = kmsClient.Decrypt(decryptRequest);
plainDataKey = Convert.ToBase64String(decryptResp.Plaintext.ToArray());
// your decryption logic
DecryptTexts("encrypted data", PlainKey)

Related

How do I store a private key using Google Cloud's Cloud KMS?

I've been trying to read documentation, and watching a few of their videos but I'm not entirely clear on how I store a private key using GCP's Cloud KMS.
Is the idea for me to store the private key in storage, then use Cloud KMS to encrypt it? How can I make this key available as a secret to my application?
I know this is a very basic question, but I couldn't find an easy breakdown on how to do this - I'm looking for a simple explanation about the concept. Thanks!
Please read for yourself: https://cloud.google.com/kms/docs ...maybe you'll come up with a more focused question. And I think that there's a slight misunderstanding - you'd only be able to retrieve these on the server-side, but not client-side (else the client would need to have the RSA private key of the service account, which has access to Cloud KMS, which is a security breach by itself). So this is generally only useful for a) server-side applications and b) eg. Google Cloud Build.
Generally one has to:
create the keyring with gcloud kms keyrings create
create the key with gcloud kms keys create
then use gcloud kms encrypt and gcloud kms decrypt
I can also provide a usage example (it assumes a key-ring with a key).
Just to show, that one doesn't necessarily have to setup secrets.
gcloud kms can well provide build secrets - assuming that one can use a service-account with role roles/cloudkms.cryptoKeyEncrypterDecrypter. The given example decrypts all kinds of build secrets - without having to deal with any base64 encoded binary files in meta-data (which is rather a workaround than an actual solution).
This is a high level description of storing a private key on Google Cloud Platform (GCP):
I ended up using Google KMS, specifically asymmetric encryption feature.
The general steps to create an asymmetric key are:
Create a keyring within your project
Create a key with ENCRYPT_DECRYPT purpose (if like me, you're trying to do this using Terraform, there's some documentation here
Once we've created the key, we can now encrypt some data we want to secure using the public key from the asymmetric key we created in the previous step.
It is important to note that with an asymmetric key, there is a public-private key pair, and we never handle the private key (i.e. only GCP knows the private key).
Here's how you'd encrypt some data from your local computer:
echo -n my-secret-password | gcloud kms encrypt \
> --project my-project \
> --location us-central1 \
> --keyring my-key-ring \
> --key my-crypto-key \
> --plaintext-file - \
> --ciphertext-file - \
> | base64
This will output some cyphertext with base64 encoding, for eg:
CiQAqD+xX4SXOSziF4a8JYvq4spfAuWhhYSNul33H85HnVtNQW4SOgDu2UZ46dQCRFl5MF6ekabviN8xq+F+2035ZJ85B+xTYXqNf4mZs0RJitnWWuXlYQh6axnnJYu3kDU=
This cyphertext then needs to be stored as a secret. Once stored as a secret, we need to do the following in our application code in order to decrypt the cyphertext into a usable format.
Here is an example of decrypting the cyphertext using the #google-cloud/kms module: https://cloud.google.com/kms/docs/hsm#kms-decrypt-symmetric-nodejs
This is what it looks like in Nodejs:
//
// TODO(developer): Uncomment these variables before running the sample.
//
// const projectId = 'my-project';
// const locationId = 'us-east1';
// const keyRingId = 'my-key-ring';
// const keyId = 'my-key';
// Ciphertext must be either a Buffer object or a base-64 encoded string
// const ciphertext = Buffer.from('...');
// Imports the Cloud KMS library
const {KeyManagementServiceClient} = require('#google-cloud/kms');
// Instantiates a client
const client = new KeyManagementServiceClient();
// Build the key name
const keyName = client.cryptoKeyPath(projectId, locationId, keyRingId, keyId);
// Optional, but recommended: compute ciphertext's CRC32C.
const crc32c = require('fast-crc32c');
const ciphertextCrc32c = crc32c.calculate(ciphertext);
async function decryptSymmetric() {
const [decryptResponse] = await client.decrypt({
name: keyName,
ciphertext: ciphertext,
ciphertextCrc32c: {
value: ciphertextCrc32c,
},
});
// Optional, but recommended: perform integrity verification on decryptResponse.
// For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
// https://cloud.google.com/kms/docs/data-integrity-guidelines
if (
crc32c.calculate(decryptResponse.plaintext) !==
Number(decryptResponse.plaintextCrc32c.value)
) {
throw new Error('Decrypt: response corrupted in-transit');
}
const plaintext = decryptResponse.plaintext.toString();
console.log(`Plaintext: ${plaintext}`);
return plaintext;
}
return decryptSymmetric();

Managing key rotations with GCP_KMS with BYOK solution

We have RSA key pairs generated on on-prem and plan to sync them to GCP-KMS. There is an yearly key rotation policy which would be done on on-prem and new key_versions would be synced to KMS. My concern is with the KMS API.
Problem: The API always asks for the 'key_version' as an argument to encrypt/decrypt a file.
Desired behaviour: During decryption, is it not possible that the KMS sees the certificate thumbprint and returns the appropriate key version to decrypt a given encrypted file? e.g. a DEK wrapped with the RSA_public when supplied to KMS gets decrypted by the RSA_Private(or KEK) of the correct version.
If yes, is there any documentation that elaborates on this use case?
According to the documentation, you can achieve that with symmetric signature (no key version specified), but you can't with the asymetricDecrypt (key version is required in the URL path of the API)

Deterministic encryption using AWS KMS

I need to build an identity service that uses a customer supplied key to encrypt sensitive ID values for storage in RDS but also has to allow us to look up a record later using the plaintext ID. We'd like to use a simple deterministic encryption algorithm for this but it looks like KMS API doesn't allow you to specify the IV so you can never get identical plaintext to encrypt to the same value twice.
We also have the requirement to look up the data using another non-secure value and retrieve the encrypted secure value and decrypt it - so one-way hashing is unfortunately not going to work.
Taken together, this means we won't be able to perform our lookup of the secure ID without brute force iterating through all records and decrypting them and comparing to the plaintext value, instead of simply encrypting the plaintext search value using a known IV and using that encrypted value as an index to look up the matching record in the database.
I'm guessing this is a pretty common requirement for things like SSN's and such so how do people solve for it?
Thanks in advance.
look up a record later using the plaintext ID
Then you are loosing quite a bit of security. Maybe you could store a hash (e. g. sha-256) of the ID along the encrypted data, which would make easier to lookup the record, but not revert the value
This approach assumes that the ID is from a reasonably large message space (there are potentially a lot of IDs) so it is not feasible to create a map for every possible value
KMS API doesn't allow you to specify the IV so you can never get identical plaintext to encrypt to the same value twice.
yes, KMS seems to provide its own IV for ciphertext enforcing good security practice
if I understand your use case correctly, your flow is like this:
The customer provides a key K and you use this key to encrypt a secret S, which is stored in RDS with an associated ID.
Given a non-secret key K, you want to be able to look up S and decrypt it.
If the customer is reusing the key, this is actually not all that hard to accomplish.
Create a KMS key for the customer.
Use this KMS key to encrypt the customer's IV and the key the customer has specified, and store them in Amazon Secrets Manager - preferably namespaced in some way by customer. A Json structure like this:
{
"iv": "somerandomivvalue",
"key": "somerandomkey"
}
would allow you to easily parse the values out. ASM also allows you to seamlessly perform key rotation - which is really nifty.
If you're paranoid, you could take a cryptographic hash of the customer name (or whatever) and namespace by that.
RDS now stores the numeric ID of the customer, the insecure values, and a namespace value (or some method of deriving the location) in ASM.
It goes without saying that you need to limit access to the secrets manager vault.
To employ the solution:
Customer issues request to read secure value.
Service accesses ASM and decrypts the secret for customer.
Service extracts IV and key
Service initialises cipher scheme with IV and key and decrypts customer data.
Benefits: You encrypt and decrypt the secret values in ASM with a KMS key under your full control, and you can store and recover whatever state you need to decrypt the customer values in a secure manner.
Others will probably have cryptographically better solutions, but this should do for a first attempt.
In the end we decided to continue to use KMS for the customer supplied key encrypt/decrypt of the sensitive ID column but also enabled the PostgreSQL pgcrypt extension to provide secure hashes for lookups. So in addition to our encrypted column we added an id_hash column and we operate on the table something like this:
`INSERT INTO employee VALUES ..., id_hash = ENCODE(HMAC('SENSITIVE_ID+SECRET_SALT', 'SECRET_PASSPHRASE', 'sha256'), 'hex');
SELECT FROM employee WHERE division_id = ??? AND id_hash = ENCODE(HMAC('SENSITIVE_ID+SECRET_SALT', 'SECRET_PASSPHRASE', 'sha256'), 'hex');`
We could have done the hashing client-side but since the algorithm is key to later lookups we liked the simplicity of having the DB do the hashing for us.
Hope this is of use to anyone else looking for a solution.

AES 256 encryption and decryption

I am new to AWS.How does AES 256 encryption work in s3 bucket?I have uploaded a file in to my s3 bucket and encrypted it with AES 256 Encryption.But i'm able to download the file easily.Is that the expected scenario?It should be asking me to decrypt right?
Yes, thats works as expected. When you enable encryption on S3, it will store data encrypted form at rest. When you download or view files it will decrypt automatically.
Overall idea to encrypt at rest is that if someone steals hardrive from AWS data centre they will not be able to get access to data as it is encrypted.

AWS Encryption SDK Header Mismatch between Regions

I'm using the Amazon Encryption SDK to encrypt data before storing it in a database. I'm also using Amazon KMS. As part of the encryption process, the SDK stores the Key Provider ID of the data key used to encrypt in the generated cipher-text header.
As described in the documentation here http://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html#header-structure
The encryption operations in the AWS Encryption SDK return a single
data structure or message that contains the encrypted data
(ciphertext) and all encrypted data keys. To understand this data
structure, or to build libraries that read and write it, you need to
understand the message format.
The message format consists of at least two parts: a header and a
body. In some cases, the message format consists of a third part, a
footer.
The Key Provider ID value contains the Amazon Resource Name (ARN) of the AWS KMS customer master key (CMK).
Here is where the issue comes in. Right now I have two different KMS regions available for encryption. Each Key Provider ID has the exact same Encrypted Data Key value. So either key could be used to decrypt the data. However, the issue is with the ciphertext headers. Let's say I have KMS1 and KMS2. If I encrypt the data with the key provided by KMS1, then the Key Provider ID will be stored in the ciphertext header. If I attempt to decrypt the data with KMS2, even though the Encrypted Data Key is the same, the decryption will fail because the header does not contain the Key Provider for KMS2. It has the Key Provider ID for KMS1. It fails with this error:
com.amazonaws.encryptionsdk.exception.BadCiphertextException: Header integrity check failed.
at com.amazonaws.encryptionsdk.internal.DecryptionHandler.verifyHeaderIntegrity(DecryptionHandler.java:312) ~[application.jar:na]
at com.amazonaws.encryptionsdk.internal.DecryptionHandler.readHeaderFields(DecryptionHandler.java:389) ~[application.jar:na]
...
com.amazonaws.encryptionsdk.internal.DecryptionHandler.verifyHeaderIntegrity(DecryptionHandler.java:310) ~[application.jar:na]
... 16 common frames omitted
Caused by: javax.crypto.AEADBadTagException: Tag mismatch!
It fails to verify the header integrity and fails. This is not good, because I was planning to have multiple KMS's in case of one region KMS failing. We duplicate our data across all our regions, and we thought that we could use any KMS from the regions to decrypt as long as the encrypted data keys match. However, it looks like I'm locked into using only the original KMS that was encrypting the data? How on earth can we scale this to multiple regions if we can only rely on a single KMS?
I could include all the region master keys in the call to encrypt the data. That way, the headers would always match, although it would not reflect which KMS it's actually using. However, that's also not scalable, since we could add/remove regions in the future, and that would cause issues with all the data that's already encrypted.
Am I missing something? I've thought about this, and I want to solve this problem without crippling any integrity checks provided by the SDK/Encryption.
Update:
Based on a comment from #jarmod
Using an alias doesn't work either because we can only associate an alias to a key in the region, and it stores the resolved name of the key ARN it's pointing to anyway.
I'm reading this document and it says
Additionally, envelope encryption can help to design your application
for disaster recovery. You can move your encrypted data as-is between
Regions and only have to reencrypt the data keys with the
Region-specific CMKs
However, that's not accurate at all, because the encryption SDK will fail to decrypt on a different region because the Key Provider ID of the re-encrypted data keys will be totally different!
Apologies since I'm not familiar with Java programming, but I believe there is confusion how you are using the KMS CMKs to encrypt (or decrypt) the data using keys from more than one-region for DR.
When you use multiple master keys to encrypt plaintext, any one of the master keys can be used to decrypt the plaintext. Note that, only one master key (let's say MKey1) generates the plaintext data key which is used to encrypt the data. This plaintext data key is then encrypted by the other master key (MKey2) as well.
As a result, you will have encrypted data + encrypted data key (using MKey1) + encrypted data key (using MKey2).
If for some reason MKey1 is unavailable and you want to decrypt the ciphertext, SDK can be used to decrypt the encrypted data key using MKey2, which can decrypt the ciphertext.
So, yes, you have to specify multiple KMS CMK ARN in your program if you want to use multiple KMS. The document shared by you has an example as well which I'm sure you are aware of.