Skip to content

Commit

Permalink
easyrsa-tools.lib: New command 'renew-ca'
Browse files Browse the repository at this point in the history
Sign a new CA certificate from the original CA private key.
Support all options provided by Easy-RSA, eg. 'critical' attribute.

The code is very similar to the standard 'build-ca' command, without
the generation of a new private key.

The new CA certificate will replace the old one.
The old certificate is kept in a list of expired CA certificates:
This new file is 'pki/exipred-ca-cert.list'

The final replacement of the old CA is guarded by a confirmation.
If the confirmation fails then all new data is discarded.

easyrsa: Integrate 'renew-ca' into command selection

Signed-off-by: Richard T Bonhomme <[email protected]>
  • Loading branch information
TinCanTech committed Nov 26, 2024
1 parent 104b44c commit ba32b0d
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 1 deletion.
219 changes: 219 additions & 0 deletions dev/easyrsa-tools.lib
Original file line number Diff line number Diff line change
Expand Up @@ -1009,4 +1009,223 @@ Input is not a valid certificate:
fi
} # => verify_cert()

# Renew CA certificate
renew_ca_cert() {
# dirs and files
ca_key_file="$EASYRSA_PKI"/private/ca.key
ca_cert_file="$EASYRSA_PKI"/ca.crt
exp_ca_cert_list="$EASYRSA_PKI"/expired-ca.list

# Set fixed variables
x509=1
date_stamp=1
f_name="renew_ca_cert()"

# Set default CA commonName
[ "$EASYRSA_REQ_CN" = ChangeMe ] || \
warn "\
$cmd does not support setting an external commonName."

# Copy Old CA commonName as default
export EASYRSA_REQ_CN="$(
"$EASYRSA_OPENSSL" x509 -in "$ca_cert_file" \
-noout -subject -nameopt utf8,multiline | \
grep 'commonName' | sed -e \
s\`^[[:blank:]]*commonName[[:blank:]]*=[[:blank:]]\`\`
)"

# Set ssl batch mode, as required
[ "$EASYRSA_BATCH" ] && ssl_batch=1

# create local SSL cnf
write_easyrsa_ssl_cnf_tmp

# Assign new cert temp-file
out_cert_tmp=
easyrsa_mktemp out_cert_tmp || \
die "$f_name easyrsa_mktemp out_cert_tmp"

# Assign old cert temp-file
old_cert_tmp=
easyrsa_mktemp old_cert_tmp || \
die "$f_name easyrsa_mktemp old_cert_tmp"

# Write complete CA cert to old cert temp-file
"$EASYRSA_OPENSSL" x509 -in "$ca_cert_file" \
-text > "$old_cert_tmp" || \
die "$f_name Write CA cert to temp-file"

# Find or create x509 CA file
if [ -f "$EASYRSA_EXT_DIR/ca" ]; then
# Use the x509-types/ca file
x509_type_file="$EASYRSA_EXT_DIR/ca"
else
# Use a temp file
write_x509_type_tmp ca
x509_type_file="$write_x509_file_tmp"
fi

# basicConstraints critical
if grep -q 'Basic Constraints: critical' "$old_cert_tmp"
then
crit_tmp=
easyrsa_mktemp crit_tmp || \
die "$f_name easyrsa_mktemp BC crit_tmp"

add_critical_attrib basicConstraints "$x509_type_file" \
"$crit_tmp" || die "$f_name BC add_critical_attrib"

# Use the new tmp-file with critical attribute
x509_type_file="$crit_tmp"
verbose "renew_ca_cert: basicConstraints critical OK"
fi

# keyUsage critical
if grep -q 'Key Usage: critical' "$old_cert_tmp"
then
crit_tmp=
easyrsa_mktemp crit_tmp || \
die "$f_name easyrsa_mktemp KU crit_tmp"

add_critical_attrib keyUsage "$x509_type_file" \
"$crit_tmp" || die "$f_name KU add_critical_attrib"

# Use the new tmp-file with critical attribute
x509_type_file="$crit_tmp"
verbose "renew_ca_cert: keyUsage critical OK"
fi

# Find or create x509 COMMON file
if [ -f "$EASYRSA_EXT_DIR/COMMON" ]; then
# Use the x509-types/COMMON file
x509_COMMON_file="$EASYRSA_EXT_DIR/COMMON"
else
# Use a temp file
write_x509_type_tmp COMMON
x509_COMMON_file="$write_x509_file_tmp"
fi

# Check for insert-marker in ssl config file
if ! grep -q '^#%CA_X509_TYPES_EXTRA_EXTS%' \
"$EASYRSA_SSL_CONF"
then
die "\
This openssl config file does not support X509-type 'ca'.
* $EASYRSA_SSL_CONF
Please update 'openssl-easyrsa.cnf' to the latest Easy-RSA release."
fi

# Assign awkscript to insert EASYRSA_EXTRA_EXTS
# shellcheck disable=SC2016 # No expand '' - build_ca()
awkscript='\
{if ( match($0, "^#%CA_X509_TYPES_EXTRA_EXTS%") )
{ while ( getline<"/dev/stdin" ) {print} next }
{print} }'

# Assign tmp-file for config
adjusted_ssl_cnf_tmp=""
easyrsa_mktemp adjusted_ssl_cnf_tmp || \
die "$f_name easyrsa_mktemp adjusted_ssl_cnf_tmp"

# Insert x509-types COMMON and 'ca' and EASYRSA_EXTRA_EXTS
{
# X509 files
cat "$x509_type_file" "$x509_COMMON_file"

# User extensions
[ "$EASYRSA_EXTRA_EXTS" ] && \
print "$EASYRSA_EXTRA_EXTS"

} | awk "$awkscript" "$EASYRSA_SSL_CONF" \
> "$adjusted_ssl_cnf_tmp" || \
die "$f_name Copy X509_TYPES to config failed"
verbose "$f_name insert x509 and extensions OK"

# Use this new SSL config for the rest of this function
EASYRSA_SSL_CONF="$adjusted_ssl_cnf_tmp"

# Generate new CA cert:
easyrsa_openssl req -utf8 -new \
-key "$ca_key_file" \
-out "$out_cert_tmp" \
${ssl_batch:+ -batch} \
${x509:+ -x509} \
${date_stamp:+ -days "$EASYRSA_CA_EXPIRE"} \
${EASYRSA_DIGEST:+ -"$EASYRSA_DIGEST"} \
${EASYRSA_NO_PASS:+ "$no_password"} \
${EASYRSA_PASSIN:+ -passin "$EASYRSA_PASSIN"} \
# EOL

# Collect New CA text
new_ca_text="$(
"$EASYRSA_OPENSSL" x509 -in "$out_cert_tmp" -noout -text
)"

# Confirm renewed certificate installation
confirm "Install the new CA certificate ? " yes "
NEW CA CERTIFICATE:
$new_ca_text
WARNING !!!
Your CA certificate is ready to be renewed. (Details above)
This new CA certificate will completely replace the old one.
The old CA will be archived to the 'expired-ca.list' file.
Please check the details above are correct, before continuing."

# Prepare header file for updated old CA list
header_tmp=
easyrsa_mktemp header_tmp || \
die "$f_name easyrsa_mktemp header_tmp"

# header and separator text
hdr='# Easy-RSA expired CA certificate list:'
spr='# ====================================='

# make full header temp-file
printf '%s\n%s\n\n' "$hdr" "$spr" > "$header_tmp" || \
die "$f_name printf header to header-temp"

# Prepare old cert list
if [ -f "$exp_ca_cert_list" ]; then
# Assign old cert list temp file
exp_cert_list_tmp=
easyrsa_mktemp exp_cert_list_tmp || \
die "$f_name easyrsa_mktemp exp_cert_list_tmp"

# write list to temp-fie, remove header not separators
sed -e s/"^${hdr}$"// \
"$exp_ca_cert_list" > "$exp_cert_list_tmp" || \
die "$f_name sed exp_ca_cert_list"
fi

# Add full old CA Cert to old CA Cert list file
if [ -f "$exp_cert_list_tmp" ]; then
cat "$header_tmp" "$old_cert_tmp" "$exp_cert_list_tmp" \
> "$exp_ca_cert_list" || \
die "$f_name cat exp_cert_list_tmp"
else
cat "$header_tmp" "$old_cert_tmp" \
> "$exp_ca_cert_list" || \
die "$f_name cat old_cert_tmp"
fi

# Install renewed CA Cert temp-file as current CA cert
mv -f "$out_cert_tmp" "$ca_cert_file" || \
die "Failed to install renewed CA temp-file!"

notice "\
CA certificate has been successfully renewed.
Your old CA cerificate has been added to the expired CA list at:
* $exp_ca_cert_list
Your renewed CA cerificate is at:
* $ca_cert_file"
} # => renew_ca_cert()

# vim: ft=sh nu ai sw=8 ts=8 noet
8 changes: 7 additions & 1 deletion easyrsa3/easyrsa
Original file line number Diff line number Diff line change
Expand Up @@ -6011,13 +6011,19 @@ case "$cmd" in
verify_working_env
show_host "$@"
;;
renew|show-expire|show-revoke|show-renew|verify-cert)
renew-ca|renew|show-expire|show-revoke|show-renew|verify-cert)
verify_working_env

# easyrsa-tools.lib is required
source_easyrsa_tools_lib || tools_error=1

case "$cmd" in
renew-ca)
[ "$tools_error" ] && user_error "$tools_error_txt"
[ -z "$alias_days" ] || \
export EASYRSA_CA_EXPIRE="$alias_days"
renew_ca_cert "$@"
;;
renew)
[ "$tools_error" ] && user_error "$tools_error_txt

Expand Down

0 comments on commit ba32b0d

Please sign in to comment.