Create self-signed Certificate with Cloudformation
AWS Cloudformation allows you to create self-signed certificates during the stack creation process which you can then use to attach it to other AWS resources like an ELB.
I recently discovered two ways of creating self-signed certificates automated with Cloudformation.
AWS Certificate Manager Private CA (ACMPCA)
AWS Certificate Manager Private CA(ACMPCA) Service allows you to create and manage a Private CA as easy as it can get. With a Private CA in ACMPCA you are able to create a Self-Signed Certificate in AWS Certificate Manager (ACM).
The integration of ACMPCA in Cloudformation allows you to integrate the creation of a Private CA & the creation of a Self-Signed certificate with ACM into you stack creation process. But keep in mind one Private CA costs around 400$ a month.
Create Private CA
The process of creating a Private CA with ACMPCA is as follows
- Create Internal Root CA
- Create Internal Root CA Certificate
- Activate internal Root CA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
InternalRootCA:
Type: 'AWS::ACMPCA::CertificateAuthority'
Properties:
Type: ROOT
KeyAlgorithm: RSA_2048
SigningAlgorithm: SHA256WITHRSA
Subject:
Country: US
State: California
Locality: San Francisco
Organization: MyOrganization
OrganizationalUnit: MyOrganizationalUnit
CommonName: My own Root CA
SerialNumber: '12345678'
RevocationConfiguration:
CrlConfiguration:
Enabled: false
InternalRootCACertificate:
Type: 'AWS::ACMPCA::Certificate'
Properties:
CertificateAuthorityArn: !Ref InternalRootCA
CertificateSigningRequest: !GetAtt
- InternalRootCA
- CertificateSigningRequest
SigningAlgorithm: SHA256WITHRSA
TemplateArn: 'arn:aws:acm-pca:::template/RootCACertificate/V1'
Validity:
Type: YEARS
Value: 5
InternalRootCAActivation:
Type: 'AWS::ACMPCA::CertificateAuthorityActivation'
Properties:
CertificateAuthorityArn: !Ref InternalRootCA
Certificate: !GetAtt
- InternalRootCACertificate
- Certificate
Status: ACTIVE
Create Self-Signed Certificate in ACM
To create your Self-Signed Certificate in ACM you need to wait until the Root CA was activated & you need the ARN of the created Root CA.
1
2
3
4
5
6
7
8
ACMInternalCertificate:
DependsOn: InternalRootCAActivation
Type: AWS::CertificateManager::Certificate
Properties:
CertificateAuthorityArn: !Ref InternalRootCA
DomainName: 'example.com'
SubjectAlternativeNames:
- "*.example.com"
is and the other one is to use a Python Lambda function to create a self-signed certificate and upload it to AWS Certificate Manager.
Lambda and Custom Resource
If you don’t want to create your Private CA in ACMPCA you can use Lambda and Custom Resources to create a Self-Signed Certificate and store it in ACM. The Python module pyopenssl
allows you to create a Private CA & Self-Signed Certificate with Python.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import random
from OpenSSL import crypto
# Create CA
ca_key = crypto.PKey()
ca_key.generate_key(crypto.TYPE_RSA, 2048)
ca_cert = crypto.X509()
ca_cert.set_version(2)
ca_cert.set_serial_number(random.randrange(100000))
ca_subj = ca_cert.get_subject()
ca_subj.C = "US"
ca_subj.ST = "California"
ca_subj.L = "San Francisco"
ca_subj.O = "MyOrganization"
ca_subj.OU = "MyOrganizationalUnit"
ca_subj.CN = "My own Root CA"
ca_cert.add_extensions([
crypto.X509Extension(b"subjectKeyIdentifier", False, b"hash", subject=ca_cert),
])
ca_cert.add_extensions([
crypto.X509Extension(b"authorityKeyIdentifier", False, b"keyid:always", issuer=ca_cert),
])
ca_cert.add_extensions([
crypto.X509Extension(b"basicConstraints", False, b"CA:TRUE"),
crypto.X509Extension(b"keyUsage", False, b"keyCertSign, cRLSign"),
])
ca_cert.set_issuer(ca_subj)
ca_cert.set_pubkey(ca_key)
ca_cert.sign(ca_key, 'sha256')
ca_cert.gmtime_adj_notBefore(0)
ca_cert.gmtime_adj_notAfter(10*365*24*60*60)
ca_certifictate_pem = crypto.dump_certificate(crypto.FILETYPE_PEM, ca_cert).decode('utf-8')
# Create Self-Signed Certificate
client_key = crypto.PKey()
client_key.generate_key(crypto.TYPE_RSA, 2048)
client_cert = crypto.X509()
client_cert.set_version(2)
client_cert.set_serial_number(random.randrange(100000))
client_subj = client_cert.get_subject()
client_subj.C = "US"
client_subj.ST = "California"
client_subj.L = "San Francisco"
client_subj.O = "MyOrganization"
client_subj.OU = "MyOrganizationalUnit"
client_subj.CN = "example.com"
client_cert.add_extensions([
crypto.X509Extension(b"basicConstraints", False, b"CA:FALSE"),
crypto.X509Extension(b"subjectKeyIdentifier", False, b"hash", subject=client_cert),
])
client_cert.add_extensions([
crypto.X509Extension(b"authorityKeyIdentifier", False, b"keyid:always", issuer=ca_cert),
crypto.X509Extension(b"extendedKeyUsage", False, b"serverAuth"),
crypto.X509Extension(b"keyUsage", False, b"digitalSignature"),
])
client_cert.add_extensions([
crypto.X509Extension(b'subjectAltName', False,
','.join([
'DNS:*.example.com'
]).encode())])
client_cert.set_issuer(ca_subj)
client_cert.set_pubkey(client_key)
client_cert.gmtime_adj_notBefore(0)
client_cert.gmtime_adj_notAfter(9*365*24*60*60)
client_cert.sign(ca_key, 'sha256')
certifictate_pem = crypto.dump_certificate(crypto.FILETYPE_PEM, client_cert).decode('utf-8')
private_key_pem = crypto.dump_privatekey(crypto.FILETYPE_PEM, client_key).decode('utf-8')
response = {
'certificate': certifictate_pem,
'private_key': private_key_pem,
'certificate_chain': ca_certifictate_pem
}
Now that you have the self-signed certificate, private key & certificate chain you can use boto to upload it to ACM.
1
2
3
4
5
6
7
import boto3
acm = boto3.client('acm')
response = acm.import_certificate(
Certificate=certificate,
PrivateKey=private_key,
CertificateChain=certificate_chain,
)