Odyssey
aws_get_certificate.py
1 #!/usr/bin/env python
2 '''Retrieve Certificates and Private Keys from AWS
3 
4 Query Amazon Certificate Manager and Secrets Manager to query for both
5 the certificate, certificate chain, and the private key. This results
6 in a reconstruction of a typical PEM file.
7 '''
8 
9 import json
10 import os
11 import sys
12 import boto3
13 from Crypto.Cipher import AES
14 from Crypto.Protocol.KDF import PBKDF2
15 from Crypto.Util import Counter
16 
17 SALT = 'LTzkm1w/p3ReDm9kmfmnwQ=='
18 ENCODING = 'utf-8'
19 AES_KEY_BYTES = 32
20 
21 AWS_REGION = os.environ.get('AWS_REGION', 'us-east-2')
22 CERTIFICATE_OUTPUT_DIR = os.environ.get('CERTIFICATE_OUTPUT_DIR',
23  '/tmp/odyssey')
24 
25 
26 def __derive_key__(password, salt=None):
27  '''Return key using PBKDF2'''
28  if not salt:
29  salt = SALT
30  return PBKDF2(
31  password,
32  salt.encode(ENCODING),
33  AES_KEY_BYTES
34  )
35 
36 
37 def __decrypt_data__(password, salt, data):
38  '''Decrypt given data using password and PBKDF2 key stretching'''
39 
40  key = __derive_key__(password, salt)
41  aes_cipher = AES.new(key, AES.MODE_CTR, counter=Counter.new(128))
42  return aes_cipher.decrypt(data).decode(ENCODING)
43 
44 
45 def main(certificate_path, secret_id, region=None):
46  '''Download and decrypt certificate file from EFS
47 
48  Use AWS Secrets Manager to download shared key for decryption.
49 
50  Arguments:
51 
52  - `certificate_path`: Absolute path to encrypted certificate (PEM)
53  file. The basename of this file is used for the output of the
54  unencrypted certificate contents.
55 
56  - `secret_id`: AWS Secrets Manager secret identifier. This is
57  often referred to as the "secrets path" in conversation. E.g.,
58  `test/certs/rdc/foobar.homecu.io.pem`. The `secret_id` will
59  also be used for the path and name of the unencrypted certificate file.
60 
61  - `region`: AWS Region to download secrets from, defaults to
62  environment variable `AWS_REGION`.
63 
64  '''
65 
66  assert os.path.exists(certificate_path)
67  if not region:
68  region = AWS_REGION
69  secrets = get_password_from_sm(secret_id, region)
70  salt = secrets['salt']
71  password = secrets['password']
72 
73  with open(certificate_path, 'rb') as fh:
74  encrypted_certificate = fh.read()
75 
76  certificate = __decrypt_data__(password, salt, encrypted_certificate)
77 
78  certificate_output_name = os.path.join(CERTIFICATE_OUTPUT_DIR,
79  secret_id)
80 
81  make_directory(os.path.dirname(certificate_output_name))
82 
83  with open(certificate_output_name, 'w') as fh:
84  fh.write(certificate)
85 
86 
87 def make_directory(directory_name, mode=0o700):
88  if not os.path.exists(directory_name):
89  os.makedirs(directory_name, mode=mode)
90 
91 
92 def get_password_from_sm(secret_id, region):
93  client = boto3.client('secretsmanager', region_name=region)
94  response = client.get_secret_value(SecretId=secret_id)
95  return json.loads(response['SecretString'])
96 
97 
98 if __name__ == '__main__':
99  assert len(sys.argv[1:]) >= 2
100  main(sys.argv[1], sys.argv[2])
def __decrypt_data__(password, salt, data)
def main(certificate_path, secret_id, region=None)
def __derive_key__(password, salt=None)