This project is no longer actively maintained. Consider migrating over to AWS Encryption SDK.
--
Library for encrypting and decrypting secrets within the AWS ecosystem.
- Interoperable - Interoperable implementations for Bash, NodeJS, Python and Rust.
- Secure - AES encryption with KMS generated data keys (details).
- Simple - Simple API for encrypting and decrypting sensitive data from all supported languages.
curl -LO https://raw.githubusercontent.com/sjakthol/aws-kms-crypt/master/shell/aws-kms-crypt.sh && chmod +x aws-kms-crypt.sh
npm install aws-kms-crypt
pip install aws-kms-crypt
In cargo.toml
[dependencies]
aws_kms_crypt = "0.1.0"
All implementations require access to AWS credentials.
When encrypting data, the credentials must allow the following actions:
kms:GenerateDataKey
kms:GenerateRandom
When decrypting data, the credentials must allow the following actions:
kms:Decrypt
In both cases, the access can (and should in the case of kms:Decrypt
) be
further limited with IAM policy conditions (see here
for details).
The shell script at shell/aws-kms-crypt.sh
provides an interface for shell
scripts to encrypt and decrypt data. The script needs the following commands /
tools to function:
aws
base64
cut
jq
od
openssl
sed
# No encryption context
echo -n "secretp4ssw0rd!" | ./aws-kms-crypt.sh encrypt --kms-key-id alias/common > encrypted-plan.json
# With encryption context
echo -n "secretp4ssw0rd!" | ./aws-kms-crypt.sh encrypt --kms-key-id alias/common --encryption-context type=plan,entity=admins > encrypted-plan.json
$ cat encrypted-plan.json | ./aws-kms-crypt.sh decrypt
secretp4ssw0rd!
The nodejs
directory contains a Node package that implements the KMS based encryption
and decryption functionality.
A recent version of Node (>= 4) is required.
Use the encrypt()
function of the module to encrypt any stringified data:
const kmscrypt = require('aws-kms-crypt')
kmscrypt.encrypt('secretp4ssw0rd!', {
key: 'alias/common', // Change your key here
region: 'eu-west-1', // AWS SDK needs to know this
encryptionContext: { purpose: 'automation' } // optional, can be left out
}, function (err, result) {
if (err) {
return console.log('Encryption failed:', err)
}
console.log(JSON.stringify(result, null, 2))
// Console output:
// {
// "EncryptedData": "DPQ0OZ8auGY6ohQb/pypAHJTAPaQre7RrEtziIhRgB8=",
// "EncryptedDataKey": "<snip>CBZogG5a",
// "EncryptionContext": {
// "purpose": "automation"
// },
// "Iv": "6f93b293f7f77ddf7525bf43038f01c4"
// }
})
To decrypt previously encrypted data, feed the parsed JSON document
into the decrypt()
function of the module:
const kmscrypt = require('aws-kms-crypt')
kmscrypt.decrypt({
'EncryptedData': 'TSHgAb4MYkacM9qtdO5OeLQax6jze3P7+zIeUDpakC4=',
'EncryptedDataKey': '<snip>KqnVhLZY+8',
'EncryptionContext': {
'purpose': 'automation'
},
'Iv': '6cfbac80d90df12a6357a8f91b57f907'
}, { region: 'eu-west-1' }, function (err, result) {
if (err) {
return console.log('Encryption failed:', err)
}
console.log(result)
// => secretp4ssw0rd!
})
The python
directory contains a Python package that implements the KMS based encryption
and decryption functionality. It supports Python 3.6 and newer.
import kmscrypt
res = kmscrypt.encrypt('secretp4ssw0rd!', key_id='alias/common', encryption_context={
'purpose': 'automation'
})
# res is now a dict of form
# {
# 'EncryptedData': 'Su00srm/ru5kd9DLDvi0EdEjjBGUrRBJ06vUmL8QHUU=',
# 'EncryptedDataKey': 'AQIDAHhyrbU/fP<snip>',
# 'EncryptionContext': {'purpose': 'automation'},
# 'Iv': 'd07acff1e2301c468cd3164b8858e477'
# }
secret = kmscrypt.decrypt(res)
print(secret) # => b'secretp4ssw0rd!'
The rust
directory contains a rust crate that implements KMS based encryption
and decryption functionality.
extern crate aws_kms_crypt;
extern crate serde_json;
use std::collections::HashMap;
fn main() {
let mut encryption_context = HashMap::new();
encryption_context.insert("entity".to_owned(), "admin".to_owned());
let options = aws_kms_crypt::EncryptOptions {
encryption_context: encryption_context,
key: "alias/common".into(),
region: "eu-west-1".into()
};
let data = "secret".into();
let res = aws_kms_crypt::encrypt(&data, &options);
println!("{}", serde_json::to_string(&res.unwrap()).unwrap());
}
Here's a full example that uses serde_json to deserialize the JSON encoded secret into a struct:
extern crate aws_kms_crypt;
extern crate serde_json;
fn main() {
let raw = r#"{
"EncryptedData": "vRhu+D5LrwNctyhxDvUoqL51YH2LclgUKtDz/2Nxy6Y=",
"EncryptedDataKey": "<snip>KBFRpvDvpXNXu3e/tTO6Jfi",
"EncryptionContext": {
"entity": "admin"
},
"Iv": "31bf06a8e0d15a26f1325da6f4f33a9c"
}"#;
let data: aws_kms_crypt::EncryptedSecret = serde_json::from_str(raw).unwrap();
let options = aws_kms_crypt::DecryptOptions {
region: "eu-west-1".to_owned()
};
let res = aws_kms_crypt::decrypt(&data, &options);
println!("Secret is: {:?}", res.unwrap());
}
The following steps are taken when the data is encrypted:
- A random 16 byte initialization vector is generated with the GenerateRandom KMS API call (shell) or through a platform-specific randomness API (Node, Python)
- A data encryption key for AES-128 algorithm is generated with the GenerateDataKey` KMS API call
- The input data is encrypted locally with AES-128-CBC algorithm using the plaintext version of the generated data key together with the generated IV for encryption.
- The encrypted data, encrypted data key and the initialization vector is outputted into
a JSON document of following format:
{ "EncryptedData": "<base64>", "EncryptedDataKey": "<base64>", "EncryptionContext": { "KeyName1": "1", "KeyName2": "2" }, "Iv": "<hex>" }
This JSON output can be saved to a file and stored in (semi) publicly available location as it does not reveal anything about the encrypted data.
The decryption phase extracts the data from the JSON document the encryption phase produced and takes the following steps to decrypt the data:
- The encrypted data key is decrypted using the Decrypt KMS API call to retrieve the plaintext data encryption key
- The plaintext data key and the IV is used to decrypt the encrypted data locally.
- Support other algorithms than AES-128-CBC
- Automated testing of interoperability