|
###################################################################### |
|
# |
|
# Let's create certificates easily. |
|
# |
|
###################################################################### |
|
|
|
HOSTNAME = |
|
USERNAME = |
|
|
|
openssl := openssl |
|
dhKeySize := 2048 |
|
|
|
caConf := ca.conf |
|
svConf := server.conf |
|
clConf := client.conf |
|
|
|
basedir := $(shell grep -E '^dir +=' $(caConf) | sed -e 's/.*=//' -e 's/^ *//') |
|
caDays := $(shell grep default_days $(caConf) | sed -e 's/.*=//' -e 's/^ *//') |
|
|
|
caDir := $(basedir)/CA |
|
caKey := $(caDir)/inca.key |
|
caCsr := $(caDir)/inca.csr |
|
caPem := $(caDir)/inca.pem |
|
caExt := v3_ca |
|
|
|
dbDir := $(caDir)/db |
|
crlDir := $(caDir)/crl |
|
dbIndex := $(dbDir)/index.txt |
|
dbSerial := $(dbDir)/serial |
|
crlSerial := $(crlDir)/crlserial |
|
crlPem := $(crlDir)/crl.pem |
|
|
|
HOST_DIR = $(basedir)/$(subst *,_,$(HOSTNAME)) |
|
HOST_KEY = $(HOST_DIR)/server.key |
|
HOST_CSR = $(HOST_DIR)/server.csr |
|
HOST_PEM = $(HOST_DIR)/server.pem |
|
HOST_FULLPEM = $(HOST_DIR)/fullchain.pem |
|
HOST_EXT = $(HOST_DIR)/ext.txt |
|
HOST_P12 = $(basedir)/$(addsuffix .p12,$(subst *,_,$(HOSTNAME))) |
|
|
|
USER_DIR = $(basedir)/$(USERNAME) |
|
USER_KEY = $(USER_DIR)/client.key |
|
USER_CSR = $(USER_DIR)/client.csr |
|
USER_PEM = $(USER_DIR)/client.pem |
|
|
|
distName := server |
|
|
|
svSbj := $(shell grep -A7 '\[ $(distName) \]' $(svConf) | sed 's/ //g') |
|
svC := $(word 2,$(subst =, ,$(filter countryName=%,$(svSbj)))) |
|
svST := $(word 2,$(subst =, ,$(filter stateOrProvinceName=%,$(svSbj)))) |
|
svL := $(word 2,$(subst =, ,$(filter localityName=%,$(svSbj)))) |
|
svO := $(word 2,$(subst =, ,$(filter 0.organizationName=%,$(svSbj)))) |
|
svOU := $(word 2,$(subst =, ,$(filter organizationalUnitName=%,$(svSbj)))) |
|
svEA := $(word 2,$(subst =, ,$(filter emailAddress=%,$(svSbj)))) |
|
SV_SUBJ = "/emailAddress=$(svEA)/C=$(svC)/ST=$(svST)/L=$(svL)/O=$(svO)/OU=$(svOU)/CN=$(HOSTNAME)" |
|
|
|
clSbj := $(shell grep -A7 '\[ $(distName) \]' $(clConf) | sed 's/ //g') |
|
clC := $(word 2,$(subst =, ,$(filter countryName=%,$(clSbj)))) |
|
clST := $(word 2,$(subst =, ,$(filter stateOrProvinceName=%,$(clSbj)))) |
|
clL := $(word 2,$(subst =, ,$(filter localityName=%,$(clSbj)))) |
|
clO := $(word 2,$(subst =, ,$(filter 0.organizationName=%,$(clSbj)))) |
|
clOU := $(word 2,$(subst =, ,$(filter organizationalUnitName=%,$(clSbj)))) |
|
clEA := $(word 2,$(subst =, ,$(filter emailAddress=%, $(clSbj)))) |
|
CL_SUBJ = "/emailAddress=$(clEA)/C=$(clC)/ST=$(clST)/L=$(clL)/O=$(clO)/OU=$(clOU)/CN=$(USERNAME)" |
|
|
|
|
|
###################################################################### |
|
# |
|
# Make the each certificates |
|
# |
|
###################################################################### |
|
init: |
|
@[ -d $(caDir) ] || mkdir $(caDir) |
|
@[ -d $(dbDir) ] || mkdir $(dbDir) |
|
@[ -d $(crlDir) ] || mkdir $(crlDir) |
|
@$(MAKE) __require_db |
|
|
|
.PHONY: ca.new |
|
ca.new: ca.key ca.csr ca.pem |
|
|
|
.PHONY: ca.out |
|
ca.out: ca.p12 |
|
|
|
.PHONY: server.init |
|
server.init: server.dir |
|
@echo "subjectAltName = DNS:$(HOSTNAME)" > $(HOST_EXT) |
|
|
|
.PHONY: server.new |
|
server.new: server.check.key server.key server.csr server.check.pem server.pem server.verify |
|
|
|
.PHONY: server.upd |
|
server.upd: server.revoke server.key server.csr server.pem server.verify |
|
|
|
.PHONY: server.out |
|
server.out: server.p12 |
|
|
|
.PHONY: client.init |
|
client.init: client.dir |
|
|
|
.PHONY: client.new |
|
client.new: client.check.key client.key client.csr client.check.pem client.pem client.verify |
|
|
|
.PHONY: client.upd |
|
client.upd: client.revoke client.key client.csr client.pem client.verify |
|
|
|
.PHONY: client.out |
|
client.out: client.p12 |
|
|
|
###################################################################### |
|
# |
|
# Miscellaneous rules. |
|
# |
|
###################################################################### |
|
__require_HOSTNAME: |
|
@[ "$(HOSTNAME)" != "" ] || \ |
|
(echo -e "HOSTNAME is not defined.\n# make HOSTNAME=xxx <target>\n"; exit 127) |
|
@[ "$(HOSTNAME)" != "CA" ] || (echo "Unacceptable value: $(HOSTNAME)"; exit 125) |
|
|
|
__require_USERNAME: |
|
@[ "$(USERNAME)" != "" ] || \ |
|
(echo -e "USERNAME is not defined.\n# make USERNAME=xxx <target>\n"; exit 126) |
|
@[ "$(USERNAME)" != "CA" ] || (echo "Unacceptable value: $(USERNAME)"; exit 125) |
|
|
|
__require_db: |
|
@[ -f $(dbIndex) ] || touch $(dbIndex) |
|
@[ -f $(dbSerial) ] || echo '01' > $(dbSerial) |
|
@[ -f $(crlSerial) ] || echo '01' > $(crlSerial) |
|
|
|
printsv: __require_HOSTNAME |
|
$(openssl) x509 -text -in $(HOST_PEM) |
|
|
|
printcl: __require_USERNAME |
|
$(openssl) x509 -text -in $(USER_PEM) |
|
|
|
printca: |
|
$(openssl) x509 -text -in $(caPem) |
|
|
|
clean: |
|
@rm -f $(dbDir)/*old $(crlDir)/*old |
|
|
|
# |
|
# Make a target that people won't run too often. |
|
# |
|
destroy: |
|
@find . -mindepth 1 -maxdepth 1 -not -name 'Makefile' -a -not -name '*.conf' | xargs rm -rf |
|
@$(MAKE) init |
|
|
|
###################################################################### |
|
# |
|
# Diffie-Hellman parameters |
|
# |
|
###################################################################### |
|
.PHONY: dh |
|
dh: |
|
$(openssl) dhparam -out dh -2 $(dhKeySize) |
|
|
|
###################################################################### |
|
# |
|
# Create a new self-signed CA certificate |
|
# |
|
###################################################################### |
|
.PHONY: ca.key |
|
ca.key: |
|
@[ ! -f $(caKey) ] || (echo "$(caKey) already exists."; exit 1) |
|
$(openssl) ecparam -genkey -name prime256v1 -out $(caKey) |
|
@chmod 600 $(caKey) |
|
|
|
.PHONY: ca.csr |
|
ca.csr: $(caConf) $(caKey) |
|
$(openssl) req -new -sha256 -key $(caKey) -config $(caConf) -out $(caCsr) |
|
|
|
.PHONY: ca.pem |
|
ca.pem: __require_db $(caConf) $(caCsr) |
|
@[ ! -f $(caPem) ] || (echo "$(caPem) already exists."; exit 2) |
|
$(openssl) x509 -req -in $(caCsr) -signkey $(caKey) -days $(caDays) \ |
|
-extensions $(caExt) -extfile $(caConf) -out $(caPem) |
|
@chmod g+r $(caPem) |
|
@rm -f $(caCsr) |
|
|
|
.PHONY: ca.der |
|
ca.der: $(caPem) |
|
$(openssl) x509 -inform PEM -outform DER -in $(caPem) -out $(notdir $(basedir))_ca.der |
|
@chmod g+r $(notdir $(basedir))_ca.der |
|
|
|
.PHONY: ca.p12 |
|
ca.p12: $(caPem) |
|
$(openssl) pkcs12 -export -in $(caPem) -inkey $(caKey) -out $(notdir $(basedir))_ca.p12 |
|
@chmod g+r $(notdir $(basedir))_ca.p12 |
|
|
|
.PHONY: ca.updatedb |
|
ca.updatedb: $(caConf) |
|
$(openssl) ca -config $(caConf) -updatedb |
|
|
|
###################################################################### |
|
# |
|
# Create a new server certificate, signed by the above CA. |
|
# |
|
###################################################################### |
|
.PHONY: server.dir |
|
server.dir: __require_HOSTNAME |
|
@[ -d $(HOST_DIR) ] || mkdir $(HOST_DIR) |
|
@[ -f $(HOST_EXT) ] || touch $(HOST_EXT) |
|
|
|
.PHONY: server.check.key |
|
server.check.key: server.dir |
|
@[ ! -f $(HOST_KEY) ] || (echo "$(HOST_KEY) already exists."; exit 3) |
|
|
|
.PHONY: server.check.pem |
|
server.check.pem: server.dir |
|
@[ ! -f $(HOST_PEM) ] || (echo "$(HOST_PEM) already exists."; exit 4) |
|
|
|
.PHONY: server.check.revoke |
|
server.check.revoke: server.dir |
|
@[ -f $(HOST_PEM) ] || (echo "$(HOST_PEM) does not exist."; exit 5) |
|
|
|
.PHONY: server.key |
|
server.key: server.dir |
|
$(openssl) ecparam -genkey -name prime256v1 -out $(HOST_KEY) |
|
@chmod 600 $(HOST_KEY) |
|
|
|
.PHONY: server.csr |
|
server.csr: server.dir $(svConf) $(HOST_KEY) |
|
$(openssl) req -new -sha256 -key $(HOST_KEY) -subj $(SV_SUBJ) -config $(svConf) -out $(HOST_CSR) |
|
|
|
.PHONY: server.pem |
|
server.pem: __require_db server.dir $(svConf) $(caKey) $(caPem) $(HOST_CSR) |
|
$(openssl) ca -in $(HOST_CSR) -cert $(caPem) -keyfile $(caKey) -extfile $(HOST_EXT) \ |
|
-config $(svConf) -subj $(SV_SUBJ) -outdir $(HOST_DIR) -out $(HOST_PEM) |
|
@chmod g+r $(HOST_PEM) |
|
@rm -f $(HOST_CSR) |
|
|
|
.PHONY: server.p12 |
|
server.p12: server.dir $(HOST_PEM) |
|
$(openssl) pkcs12 -export -in $(HOST_PEM) -inkey $(HOST_KEY) -out $(HOST_P12) |
|
@chmod g+r $(HOST_P12) |
|
|
|
.PHONY: server.fullchain |
|
server.fullchain: server.dir $(caPem) $(HOST_PEM) |
|
@cat $(caPem) > $(HOST_FULLPEM) |
|
@cat $(HOST_PEM) | sed -n '/-----BEGIN CERTIFICATE-----/,$$p' >> $(HOST_FULLPEM) |
|
@chmod g+r $(HOST_FULLPEM) |
|
|
|
.PHONY: server.revoke |
|
server.revoke: __require_db server.check.revoke $(caConf) |
|
@[ -d $(HOST_DIR)/old ] || mkdir $(HOST_DIR)/old |
|
$(openssl) ca -config ca.conf -gencrl -revoke $(HOST_PEM) -out $(crlPem) |
|
@[ ! -f $(HOST_KEY) ] || command mv -f $(HOST_KEY) $(HOST_DIR)/old/ |
|
@[ ! -f $(HOST_PEM) ] || command mv -f $(HOST_PEM) $(HOST_DIR)/old/ |
|
@[ ! -f $(HOST_FULLPEM) ] || command rm -f $(HOST_FULLPEM) |
|
|
|
.PHONY: server.verify |
|
server.verify: __require_HOSTNAME $(caPem) $(HOST_PEM) |
|
$(openssl) verify -CAfile $(caPem) $(HOST_PEM) |
|
|
|
###################################################################### |
|
# |
|
# Create a new client certificate, signed by the the above CA. |
|
# |
|
###################################################################### |
|
.PHONY: client.dir |
|
client.dir: __require_USERNAME |
|
@[ -d $(USER_DIR) ] || mkdir $(USER_DIR) |
|
|
|
.PHONY: client.check.key |
|
client.check.key: client.dir |
|
@[ ! -f $(USER_KEY) ] || (echo "$(USER_KEY) already exists."; exit 6) |
|
|
|
.PHONY: client.check.pem |
|
client.check.pem: client.dir |
|
@[ ! -f $(USER_PEM) ] || (echo "$(USER_PEM) already exists."; exit 7) |
|
|
|
.PHONY: client.check.revoke |
|
client.check.revoke: client.dir |
|
@[ -f $(USER_PEM) ] || (echo "$(USER_PEM) does not exist."; exit 8) |
|
|
|
.PHONY: client.key |
|
client.key: client.dir |
|
$(openssl) ecparam -genkey -name prime256v1 -out $(USER_KEY) |
|
@chmod 600 $(USER_KEY) |
|
|
|
.PHONY: client.csr |
|
client.csr: client.dir $(clConf) $(USER_KEY) |
|
$(openssl) req -new -sha256 -key $(USER_KEY) -subj $(CL_SUBJ) -config $(clConf) -out $(USER_CSR) |
|
|
|
.PHONY: client.pem |
|
client.pem: __require_db client.dir $(clConf) $(caKey) $(caPem) $(USER_CSR) |
|
$(openssl) ca -in $(USER_CSR) -cert $(caPem) -keyfile $(caKey) -config $(clConf) \ |
|
-subj $(CL_SUBJ) -outdir $(USER_DIR) -out $(USER_PEM) |
|
@chmod g+r $(USER_PEM) |
|
@rm -f $(USER_CSR) |
|
|
|
.PHONY: client.p12 |
|
client.p12: client.dir $(USER_PEM) |
|
$(openssl) pkcs12 -export -in $(USER_PEM) -inkey $(USER_KEY) -out $(USERNAME).p12 |
|
@chmod g+r $(USERNAME).p12 |
|
|
|
.PHONY: client.revoke |
|
client.revoke: __require_db client.check.revoke $(caConf) |
|
@[ -d $(USER_DIR)/old ] || mkdir $(USER_DIR)/old |
|
$(openssl) ca -config $(caConf) -gencrl -revoke $(USER_PEM) -out $(crlPem) |
|
@[ ! -f $(USER_KEY) ] || command mv -f $(USER_KEY) $(USER_DIR)/old/ |
|
@[ ! -f $(USER_PEM) ] || command mv -f $(USER_PEM) $(USER_DIR)/old/ |
|
|
|
.PHONY: client.verify |
|
client.verify: __require_USERNAME $(caPem) $(USER_PEM) |
|
$(openssl) verify -CAfile $(caPem) $(USER_PEM) |