Assuming we have cert-chain and private key files extracted from TLSPC via DigiCert (let's call them my-cert.chain
and my-cert.key
),
how do we get them prepared for AWS ACM import.
In this case my-cert.chain
is a full chain and my-cert.key
is an encrypted private key.
As such we need to cope with two requirements:
- Extract the leaf-level cert from the cert chain
- Decrypt the private key
We'll show these steps separately before showing how they can be combined to complete the import.
The ACM import feature requires the PEM text to include only the leaf-level certificate.
For clarity, the format of a full chain is as follows.
-----BEGIN CERTIFICATE-----
<LEAF>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<INTERMEDIATE>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<ROOT>
-----END CERTIFICATE-----
IMO the easiest way to extract just the leaf certificate from the full chain is as follows:
openssl x509 -in my-cert.chain
... this is much simpler than the examples you'll find online!
Certificates extracted from TLSPC via DigiCert will be accompanied by an encrypted private key which starts with the following PEM header:
-----BEGIN ENCRYPTED PRIVATE KEY-----
The ACM import feature requires the PEM text to be unencrypted. Encrypted private key files can be decrypted as follows:
password=<PRIVATE_KEY_PASSWORD>
openssl rsa -in my-cert.key -passin pass:${password}
We bring all this together to complete the ACM import:
aws acm import-certificate \
--certificate-chain fileb://my-cert.chain \
--certificate fileb://<(openssl x509 -in my-cert.chain) \
--private-key fileb://<(openssl rsa -in my-cert.key -passin pass:${password})
Code to perform the same operations in Python as follows
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
def read_file(path):
try:
with open(path, "rb") as file:
file_content = file.read()
return file_content
except FileNotFoundError:
print(f"File '{path}' not found.")
except Exception as e:
print("An error occurred:", e)
return None
def chain_to_leaf(chain_content):
cert = x509.load_pem_x509_certificate(chain_content, default_backend())
cert_text = cert.public_bytes(serialization.Encoding.PEM).decode('utf-8')
return cert_text
def decrypt_private_key(encrypted_key_content, password):
key = serialization.load_pem_private_key(
encrypted_key_content,
password=password,
backend=default_backend()
)
key_pem = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
key_text = key_pem.decode('utf-8')
return key_text
# EXTRACT LEAF CERT FROM CHAIN
chain_file = "my-cert.chain"
chain_content = read_file(chain_file)
print(chain_to_leaf(chain_content))
# DECRYPT ENCRYPTED PRIVATE KEY
password=b"<PASSWORD>"
encrypted_key_file = "my-cert.key"
encrypted_key_content = read_file(encrypted_key_file)
print(decrypt_private_key(encrypted_key_content, password))