If that is a normal cert (like the one used by httpd / apache) service then answer is yes.
For more than 7 years I used my custom apache start script that creates a ROOT CA, Intermediary CA and leaf certificates for Apache (and no longer available) cups services
I ditched that 6 month ago and moved to internal Step CA (using ACME protocol) for generating certs for Ipfire Apache, Cups (docker inside a Debian), Monit, etc
Personally, it was a waste of time to transport the ROOT CA & Intermediate CA to each of my IP fire machines so I moved to a centralized CA - using STEP-CA (they provide a docker image that works from first launch). Plus, for Step-CA you no longer need the CSR (that part is covered by ACME protocol)
Functions I’ve created for OpenSSL based cers
generate_key() - # Pseudocode: Check key existence; generate based on ${type}.
generate_CSR() - # Pseudocode: Create CSR using ${type} key.
check_and_generate_ipfire_cnf() - custom cnf file with aliases (might not be needed for IPSEC?) - this one uses a template file manually created for each client…
generate_ipfire_openssl_cnf_from_unbound() - uses unbound responses to extract Aliases for an IP address (usually green/LAN) and adds them to cnf Alias section
generate_ipfire_openssl_cnf_from_hosts() - same as above but uses Ipfire hosts
openssl_generate_CERT() -
local _days_numeric=“${days_valid%d}”
#Create server certificate using above CSR and server_cert extensions from openssl.cnf of Intermediate CA
generate_ipfire_apache_certificates_OpenSSL() - uses all above: once for RSA cert and once for ECDSA cert.
I’ll try to clean up the openssl_functions file (I am not a developr so trust me when I say that elephants might live happily in my openssl_functions file), translate the comments from latin to english (
) and share it with you if you find that usefull.
Plan B: I share the pseudocode of each function and you can use them to ask one of the Smart AIs to build better functions that I’ve build 7 years ago…
late edit: WARNING: the forum loses some “#” - I tried different formating styles, but some of the # are lost no matter formatting I have applied.) I tried to double them, but I might missed some.
Here is my beta version prior to automate cnf file generation (i.e I was manually editing the cnf files on each ipfire box I had - server_cert and alias sections)
You can extrat from it the function for keys, csr and cert generation - and adapt them to your needs.
#!/bin/sh
#Begin $rc_base/init.d/apache_CA
#########################################################
#Based on /etc/init.d/apache script from IPFire #
#Adapted to use ROOT CA and Intermediary CA #
#Modified by H&M #
#Version 0.2 15.08.2019 #
#########################################################
. /etc/sysconfig/rc
. $rc_functions
cd /var/ipfire/
#pwd
#chmod 400 intermediate/private/intermediate.key.pem
#Variable definition
type=“”
days_valid=700
generate_rsa_key() {
#Generate server key - with password, then remove password
if [ ! -f “intermediate/private/hostname.rsa.key.pem” ]; then
boot_mesg “Generate rsa server key”
openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096
-out intermediate/private/hostname.rsa.key.pem
evaluate_retval
else
boot_mesg “rsa server key already existing”
fi
}
generate_ecdsa_key() {
#Generate ecdsa server key
if [ ! -f “intermediate/private/hostname.ecdsa.key.pem” ]; then
boot_mesg “Generate ecdsa server key”
openssl genpkey -algorithm EC
-pkeyopt ec_paramgen_curve:P-384
-pkeyopt ec_param_enc:named_curve
-out intermediate/private/hostname.ecdsa.key.pem
evaluate_retval
else
boot_mesg “ecdsa server key already existing”
fi
}
generate_CSR() {
#Create server CSR using special edited server_cert extensions in intermediate/openssl.cnf
if [ ! -f “intermediate/csr/hostname.$type.csr.pem” ]; then
boot_mesg “Create server CSR using special edited server_cert extensions in intermediate/openssl.cnf and $type key”
openssl req -config intermediate/openssl.cnf
-key intermediate/private/hostname.$type.key.pem
-new -sha256
-out intermediate/csr/hostname.$type.csr.pem
evaluate_retval
else
boot_mesg “CSR based on $type key already existing”
fi
}
generate_CERT() {
#Create server certificate using above CSR and server_cert extensions from openssl.cnf of Intermediate CA
if [ -f “intermediate/certs/hostname.$type.cert.pem” ]; then
if openssl x509 -checkend 604800 -noout -in intermediate/certs/hostname.$type.cert.pem ; then
boot_mesg “generate_CERT:Server certificate using $type key exists at Intermediate CA and is good for at least one week!”
openssl x509 -dates -noout -in intermediate/certs/hostname.$type.cert.pem
return
else
boot_mesg “generate_CERT:Certificate using $type key EXIST at Intermediate CA but is expired! Starting renewal process…”
yes| openssl ca -config intermediate/openssl.cnf
-extensions server_cert -days $days_valid -notext
-md sha256 -in intermediate/csr/hostname.$type.csr.pem
-out intermediate/certs/hostname.$type.cert.pem
evaluate_retval
fi
else
boot_mesg “generate_CERT:Certificate using $type key is MISSING from Intermediate CA! Starting renewal process…”
yes | openssl ca -config intermediate/openssl.cnf
-extensions server_cert -days $days_valid -notext
-md sha256 -in intermediate/csr/hostname.$type.csr.pem
-out intermediate/certs/hostname.$type.cert.pem
evaluate_retval
fi
}
generate_certificates() {
if [ ! -f “/etc/httpd/server.key” ]; then
generate_rsa_key
\cp intermediate/private/hostname.rsa.key.pem /etc/httpd/server.key
#\cp intermediate/private/hostname.rsa.key.pem /var/ipfire/cups/ssl/hostname.key
evaluate_retval
fi
#if [ ! -f "/var/ipfire/cups/ssl/`hostname`.key" ]; then
# generate_rsa_key
# \cp intermediate/private/`hostname`.rsa.key.pem /var/ipfire/cups/ssl/`hostname`.key
# evaluate_retval
#fi
if [ ! -f "/etc/httpd/server-ecdsa.key" ]; then
generate_ecdsa_key
\cp intermediate/private/`hostname`.ecdsa.key.pem /etc/httpd/server-ecdsa.key
evaluate_retval
fi
# Generate rsa CSR
if [ ! -f "/etc/httpd/server.csr" ]; then
type=rsa
generate_CSR
\cp intermediate/csr/`hostname`.$type.csr.pem /etc/httpd/server.csr
evaluate_retval
fi
# Generate ecdsa CSR
if [ ! -f "/etc/httpd/server-ecdsa.csr" ]; then
type=ecdsa
generate_CSR
\cp intermediate/csr/`hostname`.$type.csr.pem /etc/httpd/server-ecdsa.csr
evaluate_retval
fi
if [ -f "/etc/httpd/server.crt" ]; then
#We have the file, let's check its validity!
if openssl x509 -checkend 0 -noout -in /etc/httpd/server.crt; then
type=rsa
boot_mesg "generate_certificates:Server certificate using $type key exists in /etc/httpd/ and is valid !"
openssl x509 -dates -noout -in /etc/httpd/server.crt
else
#Exists but expired
type=rsa
boot_mesg "generate_certificates:Alert: Server certificate using $type key EXISTS in /etc/httpd/ but is expired!"
generate_CERT
\cp intermediate/certs/`hostname`.$type.cert.pem /etc/httpd/server.crt
evaluate_retval
fi
else
#File is missing
type=rsa
boot_mesg "generate_certificates:Alert: Server certificate using $type key mising in /etc/httpd/!"
generate_CERT
\cp intermediate/certs/`hostname`.$type.cert.pem /etc/httpd/server.crt
evaluate_retval
fi
# if [ -f "/var/ipfire/cups/ssl/`hostname`.crt" ]; then
#We have the file, let's check its validity!
# if openssl x509 -checkend 0 -noout -in /var/ipfire/cups/ssl/`hostname`.crt; then
# type=rsa
# boot_mesg "generate_certificates:Server certificate using $type key exists in /var/ipfire/cups/ssl/ and is valid!"
# openssl x509 -dates -noout -in /etc/httpd/server.crt
# else
# #Exists but expired
# type=rsa
# boot_mesg "generate_certificates:Alert: Server certificate using $type key EXISTS in /var/ipfire/cups/ssl/ but is expired!"
# generate_CERT
# \cp intermediate/certs/`hostname`.$type.cert.pem /var/ipfire/cups/ssl/`hostname`.crt
# evaluate_retval
# fi
#else
# #File is missing
# type=rsa
# boot_mesg "generate_certificates:Alert: Server certificate using $type key mising in /var/ipfire/cups/ssl/!"
# generate_CERT
# \cp intermediate/certs/`hostname`.$type.cert.pem /var/ipfire/cups/ssl/`hostname`.crt
# evaluate_retval
#fi
if [ -f "/etc/httpd/server-ecdsa.crt" ]; then
#We have the file, let's check its validity!
if openssl x509 -checkend 0 -noout -in /etc/httpd/server-ecdsa.crt; then
type=ecdsa
boot_mesg "generate_certificates:Server certificate using $type key exists in /etc/httpd/ and is valid!"
openssl x509 -dates -noout -in /etc/httpd/server-ecdsa.crt
else
#Exists but expired
type=ecdsa
boot_mesg "generate_certificates:Alert: Server certificate using $type key EXISTS in /etc/httpd/ but is expired!"
generate_CERT
\cp intermediate/certs/`hostname`.$type.cert.pem /etc/httpd/server-ecdsa.crt
evaluate_retval
fi
else
#File is missing
type=ecdsa
boot_mesg "Alert: Server certificate using $type key mising in /etc/httpd/!"
generate_CERT
\cp intermediate/certs/`hostname`.$type.cert.pem /etc/httpd/server-ecdsa.crt
evaluate_retval
fi
type=""
}
case “$1” in
start)
# Generate all required certificates
generate_certificates
boot_mesg “Starting Apache daemon…”
/usr/sbin/apachectl -k start
evaluate_retval
exit 0
;;
stop)
boot_mesg "Stopping Apache daemon..."
/usr/sbin/apachectl -k stop
evaluate_retval
exit 0
;;
restart)
boot_mesg "Restarting Apache daemon..."
generate_certificates
/usr/sbin/apachectl -k restart
evaluate_retval
exit 0
;;
reload)
boot_mesg "Reloading Apache daemon..."
/usr/sbin/apachectl -k graceful
evaluate_retval
exit 0
;;
status)
statusproc /usr/sbin/httpd
exit 0
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
#End $rc_base/init.d/apache_CA
And this is a later version where you can generate key, csr and cert with any validity you want in local folde. Note: the cnf file is still manually edited.
PS: this version no longer contains the cups part (i.e. the cups cert is no longer generated // cups was removed from list of supported add-ons in ipfire)
#!/bin/bash
#Begin $rc_base/init.d/apache_CA
#########################################################
#Based on /etc/init.d/apache script from IPFire #
#Adapted to use ROOT CA and Intermediary CA #
#Modified by H&M #
#Version 1.1 06.02.2026 #
#########################################################
if ! declare -F evaluate_retval > /dev/null; then
evaluate_retval() {
if [ $? -eq 0 ]; then
printf " [ OK ]\n"
else
printf " [FAILED]\n"
exit 1
fi
}
fi
shellcheck source=/dev/null
[[ -f /etc/sysconfig/rc ]] && . /etc/sysconfig/rc
shellcheck disable=SC1090,SC2154,SC2034
. “$rc_functions”
##— Global Config & Rules —
DEBUG=true
LOG_PREFIX=“CA_CERTIFICATE_MANAGER”
#CA structure: certs, private and openssl.cnf file
BASEDIR=“/var/ipfire/”
CA_DIR=“$BASEDIR/ca”
#CA_CERTS_DIR=“$CA_DIR/certs”
#CA_PRIVATE_KEYS_DIR=“$CA_DIR/private”
CA_OPENSSL_CNF=“$CA_DIR/openssl.cnf”
#INTERMEDIATE structure: csr, certs, crl, private and openssl.cnf file
INTERMEDIATE_DIR=“${BASEDIR}intermediate”
INTERMEDIATE_CERTS_DIR=“${INTERMEDIATE_DIR}/certs”
INTERMEDIATE_CSR_DIR=“${INTERMEDIATE_DIR}/csr”
INTERMEDIATE_PRIVATE_KEYS_DIR=“${INTERMEDIATE_DIR}/private”
INTERMEDIATE_OPENSSL_CNF=“${INTERMEDIATE_DIR}/openssl.cnf”
#INTERMEDIATE_CRL_DIR=“$INTERMEDIATE_DIR/crl”
CERT_PREFIX=“$(hostname)”
##— Metadata —
VERSION=‘1.1 06-02-2026’
AUTHOR=‘H&M’
timestamp=“”
SELF=$(basename “$0”)
days_valid=700
type=“ecdsa”
##— History v1.1 —
HISTORY=$(cat <<EOF
Version: ${VERSION} Author: ${AUTHOR}
Summary:
Changes in v1.1 2026-02-06:
Variables used intead of PATHS
parse_args to be able to control the type pf cert and validity period of the certs
v1.0 2019-11-10:
initial script version & commented the CUPS part after IPFIRE no longer include CUPS in the distribution
generate RSA and ECDSA keys and certificates for Apache using Intermediate CA
EOF
)
##— Help message —
HELP=$(cat <<EOF
Usage: $SELF [options]
Options:
-h, --help Show this help message
-v, --version Show script version and author
-H, --history Show script history
EOF
)
##— Print a variable —
list_variable() {
_variable=“$1”
echo “$_variable”
}
#cd $BASEDIR || exit
#pwd
#chmod 400 $INTERMEDIATE_PRIVATE_KEYS_DIR/intermediate.key.pem
log_action() {
# Pseudocode: Dual logging (Syslog + STDOUT if DEBUG).
local msg=“$1”
logger -t “$LOG_PREFIX” “$msg”
[[ “$DEBUG” == “true” ]] && printf “[DEBUG] %s\n” “$msg”
}
##— Generic Logic Functions —
generate_key() {
# Pseudocode: Check key existence; generate based on $type.
local key_file=“${INTERMEDIATE_PRIVATE_KEYS_DIR}/${CERT_PREFIX}.${type}.key.pem”
local params=“”
if [[ ! -f “$key_file” ]]; then
case “$type” in
rsa) params=“-algorithm rsa -pkeyopt rsa_keygen_bits:4096” ;;
ecdsa) params=“-algorithm EC -pkeyopt ec_paramgen_curve:P-384 -pkeyopt ec_param_enc:named_curve” ;;
*) log_action “ERR: Invalid type $type”; exit 1 ;;
esac
log_action “GENERATE: Creating $type key”
# shellcheck disable=SC2086
openssl genpkey $params -out “$key_file” && evaluate_retval
else
log_action “SKIP: $type key exists”
fi
}
generate_CSR() {
# Pseudocode: Create CSR using $type key.
local key_file=“${INTERMEDIATE_PRIVATE_KEYS_DIR}/${CERT_PREFIX}.${type}.key.pem”
[[ ! -f “$key_file” ]] && generate_key
local csr_file=“${INTERMEDIATE_CSR_DIR}/${CERT_PREFIX}.${type}.csr.pem”
if [[ ! -f “$csr_file” ]]; then
log_action “GENERATE: Creating $type CSR”
openssl req -config “$INTERMEDIATE_OPENSSL_CNF”
-key “${key_file}”
-new -sha256 -out “$csr_file”
-subj “put your Subject here!”
-batch && evaluate_retval
fi
}
generate_CERT() {
# Pseudocode: Issue/Renew cert.
local csr_file=“${INTERMEDIATE_CSR_DIR}/${CERT_PREFIX}.${type}.csr.pem”
[[ ! -f “$csr_file” ]] && generate_CSR
local cert_file=“${INTERMEDIATE_CERTS_DIR}/${CERT_PREFIX}.${type}.${timestamp}.cert.pem”
log_action “ISSUE: Creating $type cert ($days_valid days)”
yes | openssl ca -config “$INTERMEDIATE_OPENSSL_CNF” -extensions server_cert
-days “$days_valid” -notext -md sha256
-in “${INTERMEDIATE_CSR_DIR}/${CERT_PREFIX}.${type}.csr.pem”
-out “$cert_file” -batch && evaluate_retval
}
##— Service Logic —
generate_ipfire_apache_certificates() {
log_action “SYNC: Updating /etc/httpd/ certs”
# Process RSA
type=rsa
timestamp=""
generate_key && generate_CSR && generate_CERT
cp "${INTERMEDIATE_PRIVATE_KEYS_DIR}/${CERT_PREFIX}.${type}.key.pem" "/etc/httpd/server.key"
cp "${INTERMEDIATE_CERTS_DIR}/${CERT_PREFIX}.${type}.cert.pem" "/etc/httpd/server.crt"
# Process ECDSA
type=ecdsa
timestamp=""
generate_key && generate_CSR && generate_CERT
cp "${INTERMEDIATE_PRIVATE_KEYS_DIR}/${CERT_PREFIX}.${type}.key.pem" "/etc/httpd/server-ecdsa.key"
cp "${INTERMEDIATE_CERTS_DIR}/${CERT_PREFIX}.${type}.cert.pem" "/etc/httpd/server-ecdsa.crt"
}
set_paths() {
export BASEDIR=“${BASEDIR}”
export CERT_CN=“${CERT_PREFIX}”
CA_DIR="${BASEDIR}/ca"
INTERMEDIATE_DIR="${BASEDIR}/intermediate"
if [[ "$BASEDIR" != "/var/ipfire/" ]]; then
log_action "DEV: Non-standard BASEDIR detected. Routing outputs to PWD."
INTERMEDIATE_CERTS_DIR="."
INTERMEDIATE_CSR_DIR="."
INTERMEDIATE_PRIVATE_KEYS_DIR="."
export INTERMEDIATE_CERTS_DIR="${INTERMEDIATE_CERTS_DIR}"
export INTERMEDIATE_CSR_DIR="${INTERMEDIATE_CSR_DIR}"
else
INTERMEDIATE_CERTS_DIR="${INTERMEDIATE_DIR}/certs"
INTERMEDIATE_CSR_DIR="${INTERMEDIATE_DIR}/csr"
INTERMEDIATE_PRIVATE_KEYS_DIR="${INTERMEDIATE_DIR}/private"
fi
CA_OPENSSL_CNF="${CA_DIR}/openssl.cnf"
}
##— Parse command line arguments —
parse_args() {
local RUN_GENERATE=false
# shellcheck disable=SC2034
while [[ $# -gt 0 ]]; do
case “${1}” in
-d|–days) days_valid=“${2}”; shift 2 ;;
-t|–type) type=“${2}”; shift 2 ;;
-b|–base-dir) BASEDIR=“${2}”; set_paths; shift 2 ;;
-p|–prefix) CERT_PREFIX=“${2}”; INTERMEDIATE_OPENSSL_CNF=“$INTERMEDIATE_DIR/${CERT_PREFIX}_openssl.cnf”; shift 2 ;;
-i|–intermediate-openssl-cnf) INTERMEDIATE_OPENSSL_CNF=“${2}”; shift 2 ;;
-c|–ca-openssl-cnf) CA_OPENSSL_CNF=“${2}”; shift 2 ;;
-g|–generate) RUN_GENERATE=true;
case “${2}” in
key) GENERATE_KEY=true ;;
csr) GENERATE_CSR=true ;;
cert) GENERATE_CERT=true ;;
*) echo “Err: Unknown task ${2}”; exit 1 ;;
esac
shift 2 ;;
–timestamp) timestamp=$(date +“%Y-%m-%d”); shift ;;
-h|--help)
echo "$SELF v$VERSION, Author: $AUTHOR"
list_variable "$HELP"
exit 0
;;
-v|--version)
echo "$SELF v$VERSION, Author: $AUTHOR"
exit 0
;;
-H|--history)
echo "$SELF v$VERSION, Author: $AUTHOR"
list_variable "$HISTORY"
exit 0
;;
start)
# Generate all required certificates
generate_ipfire_apache_certificates
boot_mesg "Starting Apache daemon..."
/usr/sbin/apachectl -k start
evaluate_retval
exit 0
;;
stop)
boot_mesg "Stopping Apache daemon..."
/usr/sbin/apachectl -k stop
evaluate_retval
exit 0
;;
restart)
boot_mesg "Restarting Apache daemon..."
generate_ipfire_apache_certificates
/usr/sbin/apachectl -k restart
evaluate_retval
exit 0
;;
reload)
boot_mesg "Reloading Apache daemon..."
/usr/sbin/apachectl -k graceful
evaluate_retval
exit 0
;;
status)
statusproc /usr/sbin/httpd
exit 0
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
done
if [[ "$RUN_GENERATE" == "true" ]]; then
log_action "DEV: Executing requested tasks for ${CERT_PREFIX}..."
# Generate key if it was explicitely requested to be generated
[[ "$GENERATE_KEY" == "true" ]] && generate_key
# Generate CSR if it was explicitely requested to be generated
[[ "$GENERATE_CSR" == "true" ]] && generate_CSR
# Generate cert if was requested
[[ "$GENERATE_CERT" == "true" ]] && generate_CERT
exit 0
fi
}
##— Run CLI parser —
parse_args “$@”
#End $rc_base/init.d/apache_CA