Skip to content

Commit

Permalink
Add profile to use one intermediate CA
Browse files Browse the repository at this point in the history
  • Loading branch information
lukebakken committed May 15, 2024
1 parent 1f30af2 commit 92175f8
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 0 deletions.
3 changes: 3 additions & 0 deletions one_intermediate/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
root_ca/openssl.cnf
intermediate_ca1/openssl.cnf
intermediate_ca2/openssl.cnf
1 change: 1 addition & 0 deletions one_intermediate/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../common.mk
63 changes: 63 additions & 0 deletions one_intermediate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Chained (With One Intermediate) Certificates

This tls-gen variation generates a root CA, one intermediary CA and two
certificate/key pairs signed by the intermediate CA:

* Chain 1: root CA => intermediate 1 => client certificate/key pair
* Chain 2: root CA => intermediate 1 => server certificate/key pair

## Generating

``` shell
# pass a password using the PASSWORD env variable
make
# results will be under the ./result directory
ls -lha ./result
```

Generated CA certificate as well as client and server certificate and private keys will be
under the `result` directory.

It possible to use [ECC][ecc-intro] for intermediate and leaf keys:

```
# pass a private key password using the PASSWORD variable if needed
make USE_ECC=true ECC_CURVE="prime256v1"
# results will be under the ./result directory
ls -lha ./result
```

The list of available curves can be obtained with

``` shell
openssl ecparam -list_curves
```

### Regeneration

To regenerate, use

``` shell
# pass a private key password using the PASSWORD variable if needed
make regen
```

The `regen` target accepts the same variables as `gen` (default target) above.

### Verification

You can verify the generated client and server certificates against the generated CA one with

``` shell
make verify
```

## Certificate Information

To display client and server certificate information, use

``` shell
make info
```

This assumes the certificates were previously generated.
94 changes: 94 additions & 0 deletions one_intermediate/openssl.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Copied over from the Basic profile
# Note: LibreSSL 2.2.7 does not correctly support environment variables
# here and that is the version that ships with OS X High Sierra. So, we
# replace text using Python and generate a temporary cnf file

common_name = @COMMON_NAME@
client_alt_name = @CLIENT_ALT_NAME@
server_alt_name = @SERVER_ALT_NAME@

[ ca ]
default_ca = test_root_ca

[ test_root_ca ]
root_ca_dir = testca

certificate = $root_ca_dir/cacert.pem
database = $root_ca_dir/index.txt
new_certs_dir = $root_ca_dir/certs
private_key = $root_ca_dir/private/cakey.pem
serial = $root_ca_dir/serial

default_crl_days = 7
default_days = 1825
default_md = sha256

policy = test_root_ca_policy
x509_extensions = certificate_extensions

[ test_root_ca_policy ]
commonName = supplied
stateOrProvinceName = optional
countryName = optional
emailAddress = optional
organizationName = optional
organizationalUnitName = optional
domainComponent = optional

[ certificate_extensions ]
basicConstraints = CA:false

[ req ]
default_bits = 4096
default_md = sha256
prompt = yes
distinguished_name = root_ca_distinguished_name
x509_extensions = root_ca_extensions

[ root_ca_distinguished_name ]
commonName = hostname

[ root_ca_extensions ]
basicConstraints = critical,CA:true
keyUsage = keyCertSign, cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer

[ ca_extensions ]
basicConstraints = critical,CA:true,pathlen:0
keyUsage = keyCertSign, cRLSign
# nameConstraints = critical,@name_constraints

[ client_extensions ]
basicConstraints = CA:false
keyUsage = digitalSignature,keyEncipherment
extendedKeyUsage = clientAuth
subjectAltName = @client_alt_names

[ server_extensions ]
basicConstraints = CA:false
keyUsage = digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @server_alt_names
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer

[ client_alt_names ]
DNS.1 = $common_name
DNS.2 = $client_alt_name
DNS.3 = localhost
# examples of more Subject Alternative Names
# DNS.4 = guest
# email = [email protected]
# URI = amqps://123.client.warp10.local
# otherName = 1.3.6.1.4.1.54392.5.436;FORMAT:UTF8,UTF8String:other-username

[ server_alt_names ]
DNS.1 = $common_name
DNS.2 = $server_alt_name
DNS.3 = localhost

[ name_constraints ]
permitted;DNS.1 = .your.domain.name
permitted;DNS.2 = $server_alt_name
permitted;DNS.3 = localhost
105 changes: 105 additions & 0 deletions one_intermediate/profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
#
# Copyright (c) 2007-2014 VMware, Inc. or its affiliates. All rights reserved.
# Copyright (c) 2014-2020 Michael Klishin and contributors.
# Copyright (c) 2022 VMware, Inc. or its affiliates. All rights reserved.

import sys
import os
import shutil
from subprocess import call


def _copy_artifacts_to_results():
os.makedirs(p.relative_path("result"), exist_ok=True)
g.copy_root_ca_certificate_and_key_pair()
g.copy_leaf_certificate_and_key_pair("server")
g.copy_leaf_certificate_and_key_pair("client")


def _concat_certificates():
# concat [intermediate CA 1] [root CA] > full_chain
print("Will concatenate all CA certificates into {}".format(p.result_chained_certificate_path()))
chain_file = open(p.result_chained_certificate_path(), "w")
call(["cat",
p.intermediate_ca_certificate_path("1"),
p.root_ca_certificate_path()],
stdout=chain_file)
chain_file.close()


def generate(opts):
cli.validate_password_if_provided(opts)
print("Will generate a root CA and two certificate/key pairs (server and client)")
g.generate_root_ca(opts)
print("Will generate intermediate CA signed by the root CA")
g.generate_intermediate_ca(opts,
parent_certificate_path=p.root_ca_certificate_path(),
parent_key_path=p.root_ca_key_path(),
suffix="1")
print("Will generate server certificate/key pair signed by the intermediate CA")
g.generate_server_certificate_and_key_pair(opts,
parent_certificate_path=p.intermediate_ca_certificate_path("1"),
parent_key_path=p.intermediate_ca_key_path("1"))
print("Will generate client certificate/key pair signed by the intermediate CA")
g.generate_client_certificate_and_key_pair(opts,
parent_certificate_path=p.intermediate_ca_certificate_path("1"),
parent_key_path=p.intermediate_ca_key_path("1"))
_copy_artifacts_to_results()
_concat_certificates()
print("Done! Find generated certificates and private keys under ./result!")


def clean(opts):
for s in [p.root_ca_path(),
p.intermediate_ca_path("1"),
p.result_path(),
p.leaf_pair_path("server"),
p.leaf_pair_path("client")]:
print("Removing {}".format(s))
try:
shutil.rmtree(s)
except FileNotFoundError:
pass


def regenerate(opts):
clean(opts)
generate(opts)


def verify(opts):
print("Will verify generated certificates against the CA certificate chain...")
v.verify_leaf_certificate_against_ca_chain("client")
v.verify_leaf_certificate_against_ca_chain("server")


def info(opts):
i.leaf_certificate_info("client")
i.leaf_certificate_info("server")


def alias_leaf_artifacts(opts):
print("This command is not supported by this profile")


commands = {"generate": generate,
"gen": generate,
"clean": clean,
"regenerate": regenerate,
"regen": regenerate,
"verify": verify,
"info": info,
"alias-leaf-artifacts": alias_leaf_artifacts}

if __name__ == "__main__":
sys.path.append(os.path.realpath('..'))
import tls_gen.cli as cli
import tls_gen.gen as g
import tls_gen.info as i
import tls_gen.paths as p
import tls_gen.verify as v
cli.run(commands)

0 comments on commit 92175f8

Please sign in to comment.