Skip to content

Commit

Permalink
ca/: a sample PKI, 3-level CAs
Browse files Browse the repository at this point in the history
Based on https://pki-tutorial.readthedocs.io/en/latest/expert/index.html

Changes: bits: 4096 - that's good for decades
Default MD: sha512, even if sha256 is fine
  • Loading branch information
LA-Toth committed Apr 13, 2017
1 parent e1c56b3 commit 7880be6
Show file tree
Hide file tree
Showing 7 changed files with 612 additions and 0 deletions.
72 changes: 72 additions & 0 deletions ca/bin/init-component-ca.py
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
import os
import shlex

import subprocess

import sys

BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
os.chdir(BASEDIR)


def create_file(path: str, content: str = ''):
full_path = os.path.join(BASEDIR, path)

if os.path.exists(full_path):
return

with open(full_path, 'w') as f:
f.write(content)


def split_and_run(command: str):
if subprocess.call(shlex.split(command, posix=True)):
sys.exit(1)


def init_ca_files(ca_name: str):
root_ca_dirs = [
'ca/{}/db'.format(ca_name),
'crl',
'certs',
]

for d in root_ca_dirs:
os.makedirs(os.path.join(BASEDIR, d), 0o755, exist_ok=True)
private_dir = os.path.join(BASEDIR, 'ca/{ca_name}/private'.format(ca_name=ca_name))
os.makedirs(private_dir, 0o700, exist_ok=True)

os.chmod(private_dir, 0o700)

create_file('ca/{ca_name}/db/{ca_name}.db'.format(ca_name=ca_name))
create_file('ca/{ca_name}/db/{ca_name}.db.attr'.format(ca_name=ca_name))
create_file('ca/{ca_name}/db/{ca_name}.crt.srl'.format(ca_name=ca_name), '01')
create_file('ca/{ca_name}/db/{ca_name}.crl.srl'.format(ca_name=ca_name), '01')

if not os.path.exists('{root}/ca/{ca_name}/private/{ca_name}.key'.format(root=BASEDIR, ca_name=ca_name)):
split_and_run('''openssl req -new \
-config {root}/etc/{ca_name}.conf \
-out {root}/ca/{ca_name}.csr \
-keyout {root}/ca/{ca_name}/private/{ca_name}.key'''.format(root=BASEDIR, ca_name=ca_name))


def sign_and_create_crl(ca_name: str, parent_ca_name: str, extension_prefix: str, *, selfsign: bool = False):
if not os.path.exists('{root}/ca/{ca_name}.crt'.format(root=BASEDIR, ca_name=ca_name)):
selfsign_param = '-selfsign' if selfsign else ''
split_and_run('''openssl ca {selfsign}\
-config {root}/etc/{parent_ca_name}.conf \
-in {root}/ca/{ca_name}.csr \
-out {root}/ca/{ca_name}.crt \
-extensions {extension}_ext \
-enddate 20361231235959Z'''.format(root=BASEDIR, ca_name=ca_name, parent_ca_name=parent_ca_name,
extension=extension_prefix, selfsign=selfsign_param))

if not os.path.exists('{root}/crl/{ca_name}.crl'.format(root=BASEDIR, ca_name=ca_name)):
split_and_run('''openssl ca -gencrl \
-config {root}/etc/{ca_name}.conf \
-out {root}/crl/{ca_name}.crl'''.format(root=BASEDIR, ca_name=ca_name))


init_ca_files('component-ca')
sign_and_create_crl('component-ca', 'network-ca', 'signing_ca')
72 changes: 72 additions & 0 deletions ca/bin/init-network-ca.py
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
import os
import shlex

import subprocess

import sys

BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
os.chdir(BASEDIR)


def create_file(path: str, content: str = ''):
full_path = os.path.join(BASEDIR, path)

if os.path.exists(full_path):
return

with open(full_path, 'w') as f:
f.write(content)


def split_and_run(command: str):
if subprocess.call(shlex.split(command, posix=True)):
sys.exit(1)


def init_ca_files(ca_name: str):
root_ca_dirs = [
'ca/{}/db'.format(ca_name),
'crl',
'certs',
]

for d in root_ca_dirs:
os.makedirs(os.path.join(BASEDIR, d), 0o755, exist_ok=True)
private_dir = os.path.join(BASEDIR, 'ca/{ca_name}/private'.format(ca_name=ca_name))
os.makedirs(private_dir, 0o700, exist_ok=True)

os.chmod(private_dir, 0o700)

create_file('ca/{ca_name}/db/{ca_name}.db'.format(ca_name=ca_name))
create_file('ca/{ca_name}/db/{ca_name}.db.attr'.format(ca_name=ca_name))
create_file('ca/{ca_name}/db/{ca_name}.crt.srl'.format(ca_name=ca_name), '01')
create_file('ca/{ca_name}/db/{ca_name}.crl.srl'.format(ca_name=ca_name), '01')

if not os.path.exists('{root}/ca/{ca_name}/private/{ca_name}.key'.format(root=BASEDIR, ca_name=ca_name)):
split_and_run('''openssl req -new \
-config {root}/etc/{ca_name}.conf \
-out {root}/ca/{ca_name}.csr \
-keyout {root}/ca/{ca_name}/private/{ca_name}.key'''.format(root=BASEDIR, ca_name=ca_name))


def init_using_root_ca_config(ca_name: str, extension_prefix: str, *, selfsign: bool = False):
if not os.path.exists('{root}/ca/{ca_name}.crt'.format(root=BASEDIR, ca_name=ca_name)):
selfsign_param = '-selfsign' if selfsign else ''
split_and_run('''openssl ca {selfsign}\
-config {root}/etc/root-ca.conf \
-in {root}/ca/{ca_name}.csr \
-out {root}/ca/{ca_name}.crt \
-extensions {extension}_ext \
-enddate 20361231235959Z'''.format(root=BASEDIR, ca_name=ca_name,
extension=extension_prefix, selfsign=selfsign_param))

if not os.path.exists('{root}/crl/{ca_name}.crl'.format(root=BASEDIR, ca_name=ca_name)):
split_and_run('''openssl ca -gencrl \
-config {root}/etc/{ca_name}.conf \
-out {root}/crl/{ca_name}.crl'''.format(root=BASEDIR, ca_name=ca_name))


init_ca_files('network-ca')
init_using_root_ca_config('network-ca', 'intermediate_ca')
72 changes: 72 additions & 0 deletions ca/bin/init-root-ca.py
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
import os
import shlex

import subprocess

import sys

BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
os.chdir(BASEDIR)


def create_file(path: str, content: str = ''):
full_path = os.path.join(BASEDIR, path)

if os.path.exists(full_path):
return

with open(full_path, 'w') as f:
f.write(content)


def split_and_run(command: str):
if subprocess.call(shlex.split(command, posix=True)):
sys.exit(1)


def init_ca_files(ca_name: str):
root_ca_dirs = [
'ca/{}/db'.format(ca_name),
'crl',
'certs',
]

for d in root_ca_dirs:
os.makedirs(os.path.join(BASEDIR, d), 0o755, exist_ok=True)
private_dir = os.path.join(BASEDIR, 'ca/{ca_name}/private'.format(ca_name=ca_name))
os.makedirs(private_dir, 0o700, exist_ok=True)

os.chmod(private_dir, 0o700)

create_file('ca/{ca_name}/db/{ca_name}.db'.format(ca_name=ca_name))
create_file('ca/{ca_name}/db/{ca_name}.db.attr'.format(ca_name=ca_name))
create_file('ca/{ca_name}/db/{ca_name}.crt.srl'.format(ca_name=ca_name), '01')
create_file('ca/{ca_name}/db/{ca_name}.crl.srl'.format(ca_name=ca_name), '01')

if not os.path.exists('{root}/ca/{ca_name}/private/{ca_name}.key'.format(root=BASEDIR, ca_name=ca_name)):
split_and_run('''openssl req -new \
-config {root}/etc/{ca_name}.conf \
-out {root}/ca/{ca_name}.csr \
-keyout {root}/ca/{ca_name}/private/{ca_name}.key'''.format(root=BASEDIR, ca_name=ca_name))


def init_using_root_ca_config(ca_name: str, extension_prefix: str, *, selfsign: bool = False):
if not os.path.exists('{root}/ca/{ca_name}.crt'.format(root=BASEDIR, ca_name=ca_name)):
selfsign_param = '-selfsign' if selfsign else ''
split_and_run('''openssl ca {selfsign}\
-config {root}/etc/root-ca.conf \
-in {root}/ca/{ca_name}.csr \
-out {root}/ca/{ca_name}.crt \
-extensions {extension}_ext \
-enddate 20361231235959Z'''.format(root=BASEDIR, ca_name=ca_name,
extension=extension_prefix, selfsign=selfsign_param))

if not os.path.exists('{root}/crl/{ca_name}.crl'.format(root=BASEDIR, ca_name=ca_name)):
split_and_run('''openssl ca -gencrl \
-config {root}/etc/{ca_name}.conf \
-out {root}/crl/{ca_name}.crl'''.format(root=BASEDIR, ca_name=ca_name))


init_ca_files('root-ca')
init_using_root_ca_config('root-ca', 'root_ca', selfsign=True)
141 changes: 141 additions & 0 deletions ca/etc/component-ca.conf
@@ -0,0 +1,141 @@
# Blue Component CA

[ default ]
ca = component-ca # CA name
dir = . # Top dir
base_url = http://pki.panthernet # CA base URL
aia_url = $base_url/$ca.cer # CA certificate URL
crl_url = $base_url/$ca.crl # CRL distribution point
ocsp_url = http://ocsp.blue.se # OCSP responder URL
name_opt = multiline,-esc_msb,utf8 # Display UTF-8 characters
openssl_conf = openssl_init # Library config section

# CA certificate request

[ req ]
default_bits = 4096 # RSA key size
encrypt_key = yes # Protect private key
default_md = sha1 # MD to use
utf8 = yes # Input is UTF-8
string_mask = utf8only # Emit UTF-8 strings
prompt = no # Don't prompt for DN
distinguished_name = ca_dn # DN section
req_extensions = ca_reqext # Desired extensions

[ ca_dn ]
countryName = "HU"
organizationName = "Panther's Co."
organizationalUnitName = "Panther's Component CA"
commonName = "Panther's Component CA"

[ ca_reqext ]
keyUsage = critical,keyCertSign,cRLSign
basicConstraints = critical,CA:true,pathlen:0
subjectKeyIdentifier = hash

# CA operational settings

[ ca ]
default_ca = component_ca # The default CA section

[ component_ca ]
certificate = $dir/ca/$ca.crt # The CA cert
private_key = $dir/ca/$ca/private/$ca.key # CA private key
new_certs_dir = $dir/ca/$ca # Certificate archive
serial = $dir/ca/$ca/db/$ca.crt.srl # Serial number file
crlnumber = $dir/ca/$ca/db/$ca.crl.srl # CRL number file
database = $dir/ca/$ca/db/$ca.db # Index file
unique_subject = no # Require unique subject
default_days = 730 # How long to certify for
default_md = sha512 # MD to use
policy = match_pol # Default naming policy
email_in_dn = no # Add email to cert DN
preserve = no # Keep passed DN ordering
name_opt = $name_opt # Subject DN display options
cert_opt = ca_default # Certificate display options
copy_extensions = copy # Copy extensions from CSR
x509_extensions = server_ext # Default cert extensions
default_crl_days = 1 # How long before next CRL
crl_extensions = crl_ext # CRL extensions

[ match_pol ]
countryName = match
stateOrProvinceName = optional
localityName = optional
organizationName = match
organizationalUnitName = optional
commonName = supplied

[ any_pol ]
domainComponent = optional
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = optional
emailAddress = optional

# Extensions

[ server_ext ]
keyUsage = critical,digitalSignature,keyEncipherment
basicConstraints = CA:false
extendedKeyUsage = serverAuth,clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
authorityInfoAccess = @ocsp_info
crlDistributionPoints = @crl_info
certificatePolicies = blueMediumDevice

[ client_ext ]
keyUsage = critical,digitalSignature
basicConstraints = CA:false
extendedKeyUsage = clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
authorityInfoAccess = @ocsp_info
crlDistributionPoints = @crl_info
certificatePolicies = blueMediumDevice

[ timestamp_ext ]
keyUsage = critical,digitalSignature
basicConstraints = CA:false
extendedKeyUsage = critical,timeStamping
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
authorityInfoAccess = @issuer_info
crlDistributionPoints = @crl_info
certificatePolicies = blueMediumDevice

[ ocspsign_ext ]
keyUsage = critical,digitalSignature
basicConstraints = CA:false
extendedKeyUsage = critical,OCSPSigning
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
authorityInfoAccess = @issuer_info
noCheck = null
certificatePolicies = blueMediumDevice

[ crl_ext ]
authorityKeyIdentifier = keyid:always
authorityInfoAccess = @issuer_info

[ ocsp_info ]
caIssuers;URI.0 = $aia_url
OCSP;URI.0 = $ocsp_url

[ issuer_info ]
caIssuers;URI.0 = $aia_url

[ crl_info ]
URI.0 = $crl_url

# Policy OIDs

[ openssl_init ]
oid_section = additional_oids

[ additional_oids ]
MediumDevice = Medium Device Assurance, 1.3.6.1.4.1.0.1.7.9

0 comments on commit 7880be6

Please sign in to comment.