Skip to content

Instantly share code, notes, and snippets.

@major
Last active September 13, 2024 02:36
Show Gist options
  • Save major/8ac9f98ae8b07f46b208 to your computer and use it in GitHub Desktop.
Save major/8ac9f98ae8b07f46b208 to your computer and use it in GitHub Desktop.
Making a certificate authority (CA) with python cryptography
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
import datetime
import uuid
one_day = datetime.timedelta(1, 0, 0)
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public_key = private_key.public_key()
builder = x509.CertificateBuilder()
builder = builder.subject_name(x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, u'openstack-ansible Test CA'),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'openstack-ansible'),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u'Default CA Deployment'),
]))
builder = builder.issuer_name(x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, u'openstack-ansible Test CA'),
]))
builder = builder.not_valid_before(datetime.datetime.today() - one_day)
builder = builder.not_valid_after(datetime.datetime(2018, 8, 2))
builder = builder.serial_number(int(uuid.uuid4()))
builder = builder.public_key(public_key)
builder = builder.add_extension(
x509.BasicConstraints(ca=True, path_length=None), critical=True,
)
certificate = builder.sign(
private_key=private_key, algorithm=hashes.SHA256(),
backend=default_backend()
)
print(isinstance(certificate, x509.Certificate))
with open("ca.key", "wb") as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.BestAvailableEncryption(b"openstack-ansible")
))
with open("ca.crt", "wb") as f:
f.write(certificate.public_bytes(
encoding=serialization.Encoding.PEM,
))
$ openssl asn1parse -in ca.crt
0:d=0 hl=4 l= 801 cons: SEQUENCE
4:d=1 hl=4 l= 521 cons: SEQUENCE
8:d=2 hl=2 l= 3 cons: cont [ 0 ]
10:d=3 hl=2 l= 1 prim: INTEGER :02
13:d=2 hl=2 l= 16 prim: INTEGER :7024B38452CF480FBA3E5DF5937A1B58
31:d=2 hl=2 l= 13 cons: SEQUENCE
33:d=3 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
44:d=3 hl=2 l= 0 prim: NULL
46:d=2 hl=2 l= 36 cons: SEQUENCE
48:d=3 hl=2 l= 34 cons: SET
50:d=4 hl=2 l= 32 cons: SEQUENCE
52:d=5 hl=2 l= 3 prim: OBJECT :commonName
57:d=5 hl=2 l= 25 prim: UTF8STRING :openstack-ansible Test CA
84:d=2 hl=2 l= 30 cons: SEQUENCE
86:d=3 hl=2 l= 13 prim: UTCTIME :151111141635Z
101:d=3 hl=2 l= 13 prim: UTCTIME :180802000000Z
116:d=2 hl=2 l= 96 cons: SEQUENCE
118:d=3 hl=2 l= 34 cons: SET
120:d=4 hl=2 l= 32 cons: SEQUENCE
122:d=5 hl=2 l= 3 prim: OBJECT :commonName
127:d=5 hl=2 l= 25 prim: UTF8STRING :openstack-ansible Test CA
154:d=3 hl=2 l= 26 cons: SET
156:d=4 hl=2 l= 24 cons: SEQUENCE
158:d=5 hl=2 l= 3 prim: OBJECT :organizationName
163:d=5 hl=2 l= 17 prim: UTF8STRING :openstack-ansible
182:d=3 hl=2 l= 30 cons: SET
184:d=4 hl=2 l= 28 cons: SEQUENCE
186:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName
191:d=5 hl=2 l= 21 prim: UTF8STRING :Default CA Deployment
214:d=2 hl=4 l= 290 cons: SEQUENCE
218:d=3 hl=2 l= 13 cons: SEQUENCE
220:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
231:d=4 hl=2 l= 0 prim: NULL
233:d=3 hl=4 l= 271 prim: BIT STRING
508:d=2 hl=2 l= 19 cons: cont [ 3 ]
510:d=3 hl=2 l= 17 cons: SEQUENCE
512:d=4 hl=2 l= 15 cons: SEQUENCE
514:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Basic Constraints
519:d=5 hl=2 l= 1 prim: BOOLEAN :255
522:d=5 hl=2 l= 5 prim: OCTET STRING [HEX DUMP]:30030101FF
529:d=1 hl=2 l= 13 cons: SEQUENCE
531:d=2 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
542:d=2 hl=2 l= 0 prim: NULL
544:d=1 hl=4 l= 257 prim: BIT STRING
@AndryAsh
Copy link

Hello, I am trying to create a root certificate, your code works fine.
But I need to add additional extensions (when using openssl these extensions are described in the configuration file):

subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always

I add it like this:

builder.add_extension(
    x509.AuthorityKeyIdentifier(aki.key_identifier,
                                authority_cert_issuer=None,
                                authority_cert_serial_number=None),
    critical=False,
)

I can't figure out how to get authority_cert_issuer and authority_cert_serial_number.

If I create a certificate using openssl, I can see the extensions when viewing the certificate:

X509v3 Authority Key Identifier: 
                keyid:56:D6:4A:1E:1B:CD:25:62:6C:F2:BB:63:37:2B:1C:09:96:9D:21:11
                DirName:/C=RU/ST=Saint-Petersburg/L=Saint-Petersburg/O=LABICS/[email protected]/CN=Example Certificate Authority
                serial:07:5E:20:63:B0:B1:68:30:39:FE:A2:6A:4C:69:08:64:5A:A7:0C:A6

How can I get the same result using cryptography?
Please help me to understand.

@milahu
Copy link

milahu commented Jun 1, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment