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'

This last step, to replace the current CA certificate, is NOT implemented
in this version of the patch. In order to ease testing, the original CA
certificate is left in place; The new CA certificate is placed at
'pki/ca.crt-ReneweD' and the expired CA list is generated and updated.

Signed-off-by: Richard T Bonhomme <[email protected]>
  • Loading branch information
TinCanTech committed Nov 15, 2024
1 parent 104b44c commit e03ec14
Showing 1 changed file with 229 additions and 0 deletions.
229 changes: 229 additions & 0 deletions dev/easyrsa-tools.lib
Original file line number Diff line number Diff line change
Expand Up @@ -1009,4 +1009,233 @@ 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-crt.list
renewed_dir="$EASYRSA_PKI"/renewed
ca_cert_file_old="$renewed_dir"/ca.crt

# Forbid second renewal of CA
if [ -f "$ca_cert_file_old" ]; then
user_error "\
Explanation
"

fi


# make sure renewed dir exists
easyrsa_mkdir "$renewed_dir"

# create local SSL cnf
write_easyrsa_ssl_cnf_tmp

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

# Set fixed variables
x509=1
date_stamp=1
f_name="renew_ca_cert -"

# 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"

# 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 exp_cert_list_tmp"
fi

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

# Default CA commonName
if [ "$EASYRSA_REQ_CN" = ChangeMe ]; then
export EASYRSA_REQ_CN="$ca_subj"
else
user_error "\
$cmd does not support setting an external commonName."
fi

# 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 the CA keypair:
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

# 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

# TODO
# move old CA cert file
#mv "$ca_cert_file" "$ca_cert_file_old" || \
# die "Failed to move CA cert-file!"

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

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

# vim: ft=sh nu ai sw=8 ts=8 noet

0 comments on commit e03ec14

Please sign in to comment.