Initial onfca commit

Updated help target in Makefile
Reworked for IM CAs
Added CRL handling
Added docs/references

Change-Id: I2b1eb541464c157d3626bbe4a6cf7db78c2af533
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e1f3d2b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,227 @@
+# onfca Makefile
+#
+# SPDX-FileCopyrightText: © 2022 Open Networking Foundation <support@opennetworking.org>
+# SPDX-License-Identifier: Apache-2.0
+
+# openssl onfiguration is also given in pki.cnf
+#
+# NOTE: This makefile makes heavy use of Automatic Variables
+#   https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
+
+SHELL = bash -eu -o pipefail
+
+# see bottom for help generation
+.DEFAULT_GOAL := help
+.PHONY: test license help
+
+# common parameters for all options
+KEY_SIZE              ?= 2048
+OPENSSL_CNF           ?= pki.cnf
+
+# base dir
+BASE_DIR               ?= onf_pki
+
+# default CA to use when verifying leaf certs, by default the IM CA
+DEFAULT_CA            ?= im_ca
+
+# root CA
+ROOT_CA_NAME          ?= root_ca
+ROOT_CA_PASSPHRASE    ?= "TestingRootCAPassPhrase"
+ROOT_CA_SUBJECT       ?= /C=US/ST=California/L=Menlo Park/O=ONF/OU=Infra/CN=ONF Test Root CA
+
+# intermediate CA
+IM_CA_NAME            ?= im_ca
+IM_CA_PASSPHRASE      ?= "TestingIMCAPassPhrase"
+IM_CA_SUBJECT         ?= /C=US/ST=California/L=Menlo Park/O=ONF/OU=Infra/CN=ONF Test IM CA
+IM_EXPIRATION_DAYS    ?= 1095
+IM_REVOKE_REASON      ?= CACompromise
+
+# leaf certs
+LEAF_EXPIRATION_DAYS  ?= 730
+LEAF_PURPOSE          ?= server_cert_ext  # alternatively, use client_cert_ext
+LEAF_SUBJECT_PARTIAL  ?= /C=US/ST=California/L=Menlo Park/O=ONF/OU=Infra/CN=
+LEAF_KEYPAIR          ?= core
+LEAF_SAN              ?= DNS:core.example.com,DNS:core.example.net
+LEAF_REVOKE_REASON    ?= keyCompromise
+
+# utility/validation targets
+valid_server: ## Check Server Cert validity (set LEAF_KEYPAIR, DEFAULT_CA)
+	openssl verify -verbose -x509_strict -show_chain \
+	  -purpose sslserver \
+	  -CAfile $(BASE_DIR)/$(DEFAULT_CA)/chain.pem \
+	  -CRLfile $(BASE_DIR)/$(DEFAULT_CA)/ca.crl -crl_check \
+	  $(BASE_DIR)/certout/$(LEAF_KEYPAIR).pem
+
+valid_client: ## Check Client Cert validity (set LEAF_KEYPAIR, DEFAULT_CA)
+	openssl verify -verbose -x509_strict -show_chain \
+	  -purpose sslclient \
+	  -CAfile $(BASE_DIR)/$(DEFAULT_CA)/chain.pem \
+	  -CRLfile $(BASE_DIR)/$(DEFAULT_CA)/ca.crl -crl_check \
+	  $(BASE_DIR)/certout/$(LEAF_KEYPAIR).pem
+
+valid_im: ## Check IM CA validity (set IM_CA_NAME, ROOT_CA_NAME)
+	openssl verify -verbose -x509_strict -purpose any \
+	  -CAfile $(BASE_DIR)/$(ROOT_CA_NAME)/ca.pem \
+	  -CRLfile $(BASE_DIR)/$(ROOT_CA_NAME)/ca.crl -crl_check_all \
+	  $(BASE_DIR)/$(IM_CA_NAME)/ca.pem
+
+printca: $(BASE_DIR)/$(DEFAULT_CA)/ca.pem ## Print CA Public Key (PEM) for DEFAULT_CA
+	openssl x509 -in $< -text -noout
+
+printcrl: $(BASE_DIR)/$(DEFAULT_CA)/ca.crl ## Print CRL (Revoke List) for DEFAULT_CA
+	openssl crl -in $< -text -noout
+
+printkey: ## Print KEY (Private Key) for LEAF_KEYPAIR
+	openssl rsa -in $(BASE_DIR)/certout/$(LEAF_KEYPAIR).key -text -noout -check
+
+printcsr: ## Print CSR (Sign Request) for LEAF_KEYPAIR
+	openssl req -in $(BASE_DIR)/certout/$(LEAF_KEYPAIR).csr -text -noout -verify
+
+printpem: ## Print PEM (Public Key) for LEAF_KEYPAIR
+	openssl x509 -in $(BASE_DIR)/certout/$(LEAF_KEYPAIR).pem -text -noout
+
+revoke_leaf: ## Revoke Leaf Cert (set IM_CA_NAME, LEAF_KEYPAIR, LEAF_REVOKE_REASON)
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(IM_CA_NAME) openssl ca \
+	  -config $(OPENSSL_CNF) \
+	  -passin file:$(BASE_DIR)/$(IM_CA_NAME)/private/ca_passphrase \
+	  -revoke $(BASE_DIR)/certout/$(LEAF_KEYPAIR).pem \
+	  -crl_reason $(LEAF_REVOKE_REASON)
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(IM_CA_NAME) openssl ca -gencrl \
+	  -config $(OPENSSL_CNF) \
+	  -passin file:$(BASE_DIR)/$(IM_CA_NAME)/private/ca_passphrase \
+	  -out $(BASE_DIR)/$(IM_CA_NAME)/ca.crl
+
+revoke_im: ## Revoke Intermediate CA Cert (set IM_CA_NAME, IM_REVOKE_REASON, ROOT_CA_NAME)
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(ROOT_CA_NAME) openssl ca \
+	  -config $(OPENSSL_CNF) \
+	  -passin file:$(BASE_DIR)/$(ROOT_CA_NAME)/private/ca_passphrase \
+	  -revoke $(BASE_DIR)/$(IM_CA_NAME)/ca.pem \
+	  -crl_reason $(IM_REVOKE_REASON)
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(ROOT_CA_NAME) openssl ca -gencrl \
+	  -config $(OPENSSL_CNF) \
+	  -passin file:$(BASE_DIR)/$(ROOT_CA_NAME)/private/ca_passphrase \
+	  -out $(BASE_DIR)/$(ROOT_CA_NAME)/ca.crl
+
+destroy:  ## Delete all certificates and data
+	rm -rf $(BASE_DIR)
+
+# don't delete intermediate files
+.PRECIOUS: ca_passphrase $(BASE_DIR)/certout/%.csr $(BASE_DIR)/certout/%.key
+
+# Generic CA directory creation
+$(BASE_DIR)/%_ca:
+	mkdir -p $@/private $@/db $@/crl/db $@/certs
+	chmod 700 $@/private
+	touch $@/db/ca.db
+	printf "unique_subject = no\ncopy_extensions = copy\n" > $@/db/ca.db.attr
+	echo 01 > $@/db/ca.srl
+	echo 01 > $@/crl/db/ca.crl.srl
+	touch $@/index.txt
+
+# Root CA creation
+$(BASE_DIR)/$(ROOT_CA_NAME)/private/ca_passphrase: | $(BASE_DIR)/$(ROOT_CA_NAME)
+	@echo $(ROOT_CA_PASSPHRASE) > $@
+
+$(BASE_DIR)/$(ROOT_CA_NAME)/private/ca_key.pem: | $(BASE_DIR)/$(ROOT_CA_NAME)/private/ca_passphrase
+	@echo "## Creating root CA private key, $@"
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(ROOT_CA_NAME) openssl genrsa -aes256 \
+	  -passout file:$(BASE_DIR)/$(ROOT_CA_NAME)/private/ca_passphrase \
+	  -out $@ $(KEY_SIZE)
+
+# validity time on root CA is set in the .cnf file
+$(BASE_DIR)/$(ROOT_CA_NAME)/ca.pem: $(BASE_DIR)/$(ROOT_CA_NAME)/private/ca_key.pem
+	@echo "## Creating self-signed root CA cert: $@"
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(ROOT_CA_NAME) openssl req -config $(OPENSSL_CNF) \
+	  -extensions root_ca_ext \
+	  -new -x509 -sha256 \
+	  -key $< \
+	  -passin file:$(BASE_DIR)/$(ROOT_CA_NAME)/private/ca_passphrase \
+	  -subj "$(ROOT_CA_SUBJECT)" \
+	  -out $@
+	@echo "## Creating root CA revocation list: $(@D)/ca.crl"
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(ROOT_CA_NAME) openssl ca -gencrl \
+	  -config $(OPENSSL_CNF) \
+	  -passin file:$(BASE_DIR)/$(ROOT_CA_NAME)/private/ca_passphrase \
+	  -out $(@D)/ca.crl
+
+# Intermediate CA creation
+$(BASE_DIR)/$(IM_CA_NAME)/private/ca_passphrase: | $(BASE_DIR)/$(IM_CA_NAME)
+	@echo $(IM_CA_PASSPHRASE) > $@
+
+$(BASE_DIR)/$(IM_CA_NAME)/private/ca_key.pem: $(BASE_DIR)/$(IM_CA_NAME)/private/ca_passphrase
+	@echo "## Creating intermediate CA private key: $@"
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(IM_CA_NAME) openssl genrsa -aes256 \
+	  -passout file:$(@D)/ca_passphrase \
+	  -out $@ $(KEY_SIZE)
+
+$(BASE_DIR)/$(IM_CA_NAME)/private/im_ca.csr: $(BASE_DIR)/$(IM_CA_NAME)/private/ca_key.pem
+	@echo "## Creating intermediate CA signing request $@ from $<"
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(IM_CA_NAME) openssl req -config $(OPENSSL_CNF) \
+	  -new -sha256 \
+	  -key $< \
+	  -passin file:$(@D)/ca_passphrase \
+	  -subj "$(IM_CA_SUBJECT)" \
+	  -out $@
+
+$(BASE_DIR)/$(IM_CA_NAME)/ca.pem: $(BASE_DIR)/$(IM_CA_NAME)/private/im_ca.csr | $(BASE_DIR)/$(ROOT_CA_NAME)/ca.pem
+	@echo "## Signing $< with root CA key to create intermediate CA cert: $@"
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(ROOT_CA_NAME) openssl ca -config $(OPENSSL_CNF) \
+	  -extensions im_ca_ext \
+	  -notext -batch -md sha256 \
+	  -days $(IM_EXPIRATION_DAYS) \
+	  -passin file:$(BASE_DIR)/$(ROOT_CA_NAME)/private/ca_passphrase \
+	  -in $< \
+	  -out $@
+	@echo "## Creating chain with Root CA and IM CA: $@"
+	  cat $@ $(BASE_DIR)/$(ROOT_CA_NAME)/ca.pem > $(@D)/chain.pem
+	  openssl crl2pkcs7 -nocrl -certfile $(@D)/chain.pem | openssl pkcs7 -print_certs -noout
+	@echo "## Creating IM CA revocation list: $(@D)/ca.crl"
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(IM_CA_NAME) openssl ca -gencrl \
+	  -config $(OPENSSL_CNF) \
+	  -passin file:$(BASE_DIR)/$(IM_CA_NAME)/private/ca_passphrase \
+	  -out $(@D)/ca.crl
+
+# Leaf cert creation
+$(BASE_DIR)/certout:
+	mkdir -p $@
+
+$(BASE_DIR)/certout/%.key: $(BASE_DIR)/certout
+	@echo "## Creating leaf private key: $@"
+	openssl genrsa -out $@ $(KEY_SIZE)
+
+$(BASE_DIR)/certout/%.csr: $(BASE_DIR)/certout/%.key
+	@echo "## Creating signing request $@ from $<"
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(IM_CA_NAME) openssl req -config $(OPENSSL_CNF) \
+	  -new -sha256 \
+	  -key $< \
+	  -subj "$(LEAF_SUBJECT_PARTIAL)$*" \
+	  -addext "subjectAltName = $(LEAF_SAN)" \
+	  -out $@
+
+$(BASE_DIR)/certout/%.pem: $(BASE_DIR)/certout/%.csr | $(BASE_DIR)/$(IM_CA_NAME)/ca.pem
+	@echo "## Signing $< with IM CA key to create signed leaf cert: $@"
+	BASE_DIR=$(BASE_DIR) CA_NAME=$(IM_CA_NAME) openssl ca -config $(OPENSSL_CNF) \
+	  -extensions $(LEAF_PURPOSE) \
+	  -policy any_pol \
+	  -notext -batch -md sha256 \
+	  -days $(LEAF_EXPIRATION_DAYS) \
+	  -passin file:$(BASE_DIR)/$(IM_CA_NAME)/private/ca_passphrase \
+	  -in $< \
+	  -out $@
+	@echo "## Creating bundle with IM CA and Leaf: $(basename $@)_bundle.pem"
+	  cat $@ $(BASE_DIR)/$(IM_CA_NAME)/ca.pem > $(basename $@)_bundle.pem
+	  openssl crl2pkcs7 -nocrl -certfile $(basename $@)_bundle.pem | openssl pkcs7 -print_certs -noout
+
+# testing and license tasks
+test: license
+
+license: $(VENV_NAME) ## Check license with the reuse tool
+	reuse --version ;\
+  reuse --root . lint
+
+help: ## Print help for each target
+	@echo onfca make targets
+	@echo See README.md for more detailed instructions
+	@echo
+	@grep '^[[:alnum:]_-]*:.* ##' $(MAKEFILE_LIST) \
+	  | sort | awk 'BEGIN {FS=":.* ## "}; {printf "%-25s %s\n", $$1, $$2};'