shellpki/shellpki

789 lines
21 KiB
Plaintext
Raw Normal View History

2010-10-06 17:34:30 +02:00
#!/bin/sh
2018-01-17 12:21:39 +01:00
#
2020-05-04 23:12:56 +02:00
# shellpki is a wrapper around OpenSSL to manage a small PKI
2018-01-17 12:21:39 +01:00
#
2010-10-06 17:34:30 +02:00
set -e
2018-01-23 16:52:42 +01:00
VERSION="1.0.0"
show_version() {
cat <<END
shellpki version ${VERSION}
Copyright 2010-2019 Evolix <info@evolix.fr>,
Thomas Martin <tmartin@evolix.fr>,
Gregory Colpart <reg@evolix.fr>,
Romain Dessort <rdessort@evolix.fr>,
Benoit Série <bserie@evolix.fr>,
Victor Laborie <vlaborie@evolix.fr>,
Daniel Jakots <djakots@evolix.fr>,
Patrick Marchand <pmarchand@evolix.fr>,
Jérémy Lecour <jlecour@evolix.fr>
and others.
shellpki comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
See the MIT Licence for details.
END
}
2010-10-06 17:34:30 +02:00
init() {
2018-01-17 12:21:39 +01:00
umask 0177
2010-10-06 17:34:30 +02:00
2020-05-04 18:16:07 +02:00
[ -d "${CA_DIR}" ] || mkdir -m 0750 "${CA_DIR}"
[ -d "${CRT_DIR}" ] || mkdir -m 0750 "${CRT_DIR}"
[ -f "${INDEX_FILE}" ] || touch "${INDEX_FILE}"
2018-06-27 11:45:03 +02:00
[ -f "${CRL}" ] || touch "${CRL}"
2018-01-31 12:43:18 +01:00
[ -f "${SERIAL}" ] || echo "01" > "${SERIAL}"
2018-01-17 12:21:39 +01:00
2018-06-27 11:45:03 +02:00
cn="${1:-}"
if [ -z "${cn}" ]; then
show_usage >&2
exit 1
fi
2018-06-27 11:45:03 +02:00
2020-05-04 18:16:07 +02:00
if [ -f "${CA_KEY}" ]; then
printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_KEY}"
2018-06-27 11:45:03 +02:00
read -r REPLY
2020-05-04 18:07:20 +02:00
resp=$(echo "${REPLY}" | tr 'Y' 'y')
if [ "${resp}" = "y" ]; then
2020-05-04 18:16:07 +02:00
rm -f "${CA_KEY}" "${CA_CERT}"
2020-05-04 18:07:20 +02:00
fi
2018-06-27 11:45:03 +02:00
fi
2020-05-04 18:16:07 +02:00
if [ ! -f "${CA_KEY}" ]; then
"${OPENSSL_BIN}" genrsa \
-out "${CA_KEY}" \
-aes256 ${CA_KEY_LENGTH} \
2020-05-04 18:07:20 +02:00
>/dev/null 2>&1
fi
2018-06-27 11:45:03 +02:00
2020-05-04 18:16:07 +02:00
if [ -f "${CA_CERT}" ]; then
printf "%s already exists, do you really want to erase it ? [y/N] " "${CA_CERT}"
2018-06-27 11:45:03 +02:00
read -r REPLY
2020-05-04 18:07:20 +02:00
resp=$(echo "${REPLY}" | tr 'Y' 'y')
if [ "${resp}" = "y" ]; then
2020-05-04 18:16:07 +02:00
rm "${CA_CERT}"
2020-05-04 18:07:20 +02:00
fi
2018-06-27 11:45:03 +02:00
fi
2020-05-04 18:16:07 +02:00
if [ ! -f "${CA_CERT}" ]; then
2020-05-04 18:07:20 +02:00
ask_ca_password 0
fi
2018-06-27 11:45:03 +02:00
2020-05-04 18:16:07 +02:00
if [ ! -f "${CA_CERT}" ]; then
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" req \
2020-05-04 18:07:20 +02:00
-new \
-batch \
-sha512 \
-x509 \
-days 3650 \
-extensions v3_ca \
-passin env:CA_PASSWORD \
2020-05-04 18:16:07 +02:00
-key "${CA_KEY}" \
-out "${CA_CERT}" \
2020-05-04 18:07:20 +02:00
-config /dev/stdin <<EOF
2020-05-04 18:16:07 +02:00
$(cat "${CONF_FILE}")
2018-06-27 11:45:03 +02:00
commonName_default = ${cn}
EOF
2020-05-04 18:07:20 +02:00
fi
2018-01-17 12:21:39 +01:00
}
2010-10-06 17:34:30 +02:00
2018-06-27 12:52:20 +02:00
ocsp() {
umask 0177
ocsp_uri="${1:-}"
if [ -z "${ocsp_uri}" ]; then
show_usage >&2
exit 1
fi
2020-05-05 00:28:00 +02:00
ocsp_csr_file="${CSR_DIR}/ocsp.csr"
2018-06-27 12:52:20 +02:00
2020-05-04 18:07:20 +02:00
url=$(echo "${ocsp_uri}" | cut -d':' -f1)
port=$(echo "${ocsp_uri}" | cut -d':' -f2)
2018-06-27 12:52:20 +02:00
2020-05-04 18:16:07 +02:00
if [ ! -f "${OCSP_KEY}" ]; then
"${OPENSSL_BIN}" genrsa \
-out "${OCSP_KEY}" \
${KEY_LENGTH} \
2020-05-04 18:07:20 +02:00
>/dev/null 2>&1
fi
2018-06-27 12:52:20 +02:00
2020-05-04 18:16:07 +02:00
"${OPENSSL_BIN}" req \
2020-05-04 18:07:20 +02:00
-batch \
-new \
2020-05-04 18:16:07 +02:00
-key "${OCSP_KEY}" \
2020-05-05 00:28:00 +02:00
-out "${ocsp_csr_file}" \
2018-06-27 12:52:20 +02:00
-config /dev/stdin <<EOF
2020-05-04 18:16:07 +02:00
$(cat "${CONF_FILE}")
2018-06-27 12:52:20 +02:00
commonName_default = ${url}
[ usr_cert ]
authorityInfoAccess = OCSP;URI:http://${ocsp_uri}
EOF
2020-05-04 18:16:07 +02:00
if [ ! -f "${OCSP_CERT}" ]; then
2020-05-04 18:07:20 +02:00
ask_ca_password 0
fi
2018-06-27 12:52:20 +02:00
2020-05-04 18:16:07 +02:00
if [ ! -f "${OCSP_CERT}" ]; then
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \
2020-05-04 18:07:20 +02:00
-extensions v3_ocsp \
2020-05-05 00:28:00 +02:00
-in "${ocsp_csr_file}" \
2020-05-04 18:16:07 +02:00
-out "${OCSP_CERT}" \
2020-05-04 18:07:20 +02:00
-passin env:CA_PASSWORD \
2020-05-04 18:16:07 +02:00
-config "${CONF_FILE}"
2020-05-04 18:07:20 +02:00
fi
2018-06-27 12:52:20 +02:00
2020-05-04 18:16:07 +02:00
exec "${OPENSSL_BIN}" ocsp \
2020-05-04 18:07:20 +02:00
-ignore_err \
2020-05-04 18:16:07 +02:00
-index "${INDEX_FILE}" \
2020-05-04 18:07:20 +02:00
-port "${port}" \
2020-05-04 18:16:07 +02:00
-rsigner "${OCSP_CERT}" \
-rkey "${OCSP_KEY}" \
-CA "${CA_CERT}" \
2020-05-04 18:07:20 +02:00
-text
2018-06-27 12:52:20 +02:00
}
show_usage() {
2018-01-23 16:52:42 +01:00
cat <<EOF
Usage: ${0} <subcommand> [options] [CommonName]
2010-10-06 17:34:30 +02:00
2018-01-23 16:52:42 +01:00
Initialize PKI (create CA key and self-signed cert) :
2018-06-27 11:45:03 +02:00
${0} init <commonName_for_CA>
2018-01-23 16:52:42 +01:00
2020-05-04 18:16:07 +02:00
Run OCSP_D server :
2018-06-27 12:52:20 +02:00
${0} ocsp <ocsp_uri:ocsp_port>
2018-01-23 16:52:42 +01:00
Create a client cert with key and CSR directly generated on server
(use -p or --password-file to set a password on the client key) :
2018-01-23 16:52:42 +01:00
${0} create [-p|--password-file=<FILE>] <commonName>
2018-01-23 16:52:42 +01:00
Create a client cert from a CSR (doesn't need key) :
${0} create -f <path>
Revoke a client cert with is commonName (CN) :
${0} revoke <commonName>
List all actually valid commonName (CN) :
${0} list [-a|v|r]
Check expiration date of valid certificates :
${0} check
2018-01-17 12:21:39 +01:00
EOF
2018-01-23 16:52:42 +01:00
}
2018-01-17 12:21:39 +01:00
2018-01-23 16:52:42 +01:00
error() {
echo "${1}" >&2
exit 1
}
2018-01-17 12:21:39 +01:00
2018-01-23 16:52:42 +01:00
warning() {
echo "${1}" >&2
}
2018-01-17 12:21:39 +01:00
verify_ca_password() {
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" rsa \
-in "${CA_KEY}" \
-passin env:CA_PASSWORD \
>/dev/null 2>&1
}
2018-01-23 16:52:42 +01:00
ask_ca_password() {
attempt=${1:-0}
max_attempt=3
2018-01-23 16:52:42 +01:00
trap 'unset CA_PASSWORD' 0
2020-05-04 18:07:20 +02:00
if [ ! -f "${CA_KEY}" ]; then
error "You must initialize your PKI with \`shellpki init' !"
fi
if [ "${attempt}" -gt 0 ]; then
warning "Invalid password, retry."
fi
if [ "${attempt}" -ge "${max_attempt}" ]; then
error "Maximum number of attempts reached (${max_attempt})."
fi
2020-05-04 18:07:20 +02:00
if [ -z "${CA_PASSWORD}" ]; then
stty -echo
printf "Password for CA key : "
read -r CA_PASSWORD
stty echo
printf "\n"
fi
if [ -z "${CA_PASSWORD}" ] || ! verify_ca_password; then
unset CA_PASSWORD
attempt=$(( attempt + 1 ))
2020-05-04 18:07:20 +02:00
ask_ca_password "${attempt}"
fi
2018-01-23 16:52:42 +01:00
}
2018-01-17 12:21:39 +01:00
2018-01-23 16:52:42 +01:00
create() {
2020-05-04 14:21:58 +02:00
from_csr=0
2020-05-04 17:58:13 +02:00
ask_pass=0
2018-01-23 16:52:42 +01:00
2020-05-04 14:21:58 +02:00
# Parse options
# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
2020-05-04 14:21:58 +02:00
case $1 in
-f|--file|--csr-file)
# csr-file option, with value separated by space
if [ -n "$2" ]; then
from_csr=1
csr_file=$(readlink -f -- "${2}")
shift
else
printf 'ERROR: "--csr-file" requires a non-empty option argument.\n' >&2
exit 1
fi
;;
--file=?*|--csr-file=?*)
from_csr=1
# csr-file option, with value separated by =
csr_file=$(readlink -f -- "${1#*=}")
;;
--file=|--csr-file=)
# csr-file options, without value
printf 'ERROR: "--csr-file" requires a non-empty option argument.\n' >&2
exit 1
;;
-p|--password)
2020-05-04 14:21:58 +02:00
ask_pass=1
;;
--password-file)
# password-file option, with value separated by space
if [ -n "$2" ]; then
password_file=$(readlink -f -- "${2}")
shift
else
printf 'ERROR: "--password-file" requires a non-empty option argument.\n' >&2
exit 1
fi
;;
--password-file=?*)
# password-file option, with value separated by =
password_file=$(readlink -f -- "${1#*=}")
;;
--password-file=)
# password-file options, without value
printf 'ERROR: "--password-file" requires a non-empty option argument.\n' >&2
exit 1
;;
--days)
# days option, with value separated by space
if [ -n "$2" ]; then
days=${2}
shift
else
printf 'ERROR: "--days" requires a non-empty option argument.\n' >&2
exit 1
fi
;;
--days=?*)
# days option, with value separated by =
days=${1#*=}
;;
--days=)
# days options, without value
printf 'ERROR: "--days" requires a non-empty option argument.\n' >&2
exit 1
;;
--end-date)
# end-date option, with value separated by space
if [ -n "$2" ]; then
end_date=${2}
shift
else
printf 'ERROR: "--end-date" requires a non-empty option argument.\n' >&2
exit 1
fi
;;
--end-date=?*)
# end-date option, with value separated by =
end_date=${1#*=}
;;
--end-date=)
# end-date options, without value
printf 'ERROR: "--end-date" requires a non-empty option argument.\n' >&2
exit 1
;;
--)
2020-05-04 14:21:58 +02:00
# End of all options.
shift
2020-05-04 14:21:58 +02:00
break
;;
-?*)
2020-05-04 14:21:58 +02:00
# ignore unknown options
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
;;
*)
2020-05-04 14:21:58 +02:00
# Default case: If no more options then break out of the loop.
break
;;
2018-01-23 16:52:42 +01:00
esac
2020-05-04 14:21:58 +02:00
shift
2018-01-23 16:52:42 +01:00
done
# The name of the certificate
2018-01-23 16:52:42 +01:00
cn="${1:-}"
# Set expiration argument
crt_expiration_arg=""
if [ -n "${days}" ] && [ "${days}" -gt 0 ]; then
crt_expiration_arg="-days ${days}"
fi
if [ -n "${end_date}" ]; then
cert_end_date=$(TZ=:Zulu date --date "${end_date}" +"%Y%m%d%H%M%SZ" 2> /dev/null)
if [ "$?" -ne 0 ]; then
error "Invalid end date format : \`${end_date}' can't be parsed by date(1)"
else
crt_expiration_arg="-enddate ${cert_end_date}"
fi
fi
2020-05-04 14:21:58 +02:00
if [ "${from_csr}" -eq 1 ]; then
2020-05-04 18:07:20 +02:00
if [ "${ask_pass}" -eq 1 ]; then
warning "Warning: -p|--password is ignored with -f|--file|--crt-file"
fi
if [ -n "${password_file}" ]; then
warning "Warning: --password-file is ignored with -f|--file|--crt-file"
fi
2018-01-23 16:52:42 +01:00
2020-05-05 00:28:00 +02:00
crt_file="${CRT_DIR}/${cn}.crt"
2018-01-23 16:52:42 +01:00
# ask for CA passphrase
ask_ca_password 0
# check if csr_file is a CSR
2020-05-04 18:16:07 +02:00
"${OPENSSL_BIN}" req \
2020-05-04 23:08:19 +02:00
-noout \
-subject \
2020-05-04 18:07:20 +02:00
-in "${csr_file}" \
2020-05-04 23:06:51 +02:00
>/dev/null 2>&1
if [ "$?" -ne 0 ]; then
error "${csr_file} is not a valid CSR !"
fi
2018-01-23 16:52:42 +01:00
# check if csr_file contain a CN
2020-05-04 18:16:07 +02:00
"${OPENSSL_BIN}" req \
2020-05-04 23:08:19 +02:00
-noout \
-subject \
2020-05-04 18:07:20 +02:00
-in "${csr_file}" \
| grep -Eo "CN\s*=[^,/]*" \
2020-05-04 23:06:51 +02:00
>/dev/null 2>&1
if [ "$?" -ne 0 ]; then
error "${csr_file} doesn't contain a CommonName !"
fi
2018-01-23 16:52:42 +01:00
# get CN from CSR
2020-05-04 18:16:07 +02:00
cn=$("${OPENSSL_BIN}" req -noout -subject -in "${csr_file}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs)
2018-01-23 16:52:42 +01:00
# check if CN already exist
2020-05-04 18:16:07 +02:00
if [ -f "${CRT_DIR}/${cn}.crt" ]; then
printf "%s already exists, do you revoke and recreate it ? [y/N] " "${cn}"
read -r REPLY
resp=$(echo "${REPLY}" | tr 'Y' 'y')
if [ "${resp}" = "y" ]; then
revoke "${cn}"
else
error "Abort"
fi
2020-05-04 18:07:20 +02:00
fi
2018-01-23 16:52:42 +01:00
# ca sign and generate cert
2020-05-04 18:16:07 +02:00
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \
-config "${CONF_FILE}" \
2020-05-04 18:07:20 +02:00
-in "${csr_file}" \
-passin env:CA_PASSWORD \
2020-05-05 00:28:00 +02:00
-out "${crt_file}" \
${crt_expiration_arg}
2018-01-31 12:43:18 +01:00
2020-05-05 00:28:00 +02:00
echo "The CRT file is available in ${crt_file}"
2018-01-23 16:52:42 +01:00
else
if [ -z "${cn}" ]; then
show_usage >&2
exit 1
fi
2020-05-05 00:28:00 +02:00
csr_file="${CSR_DIR}/${cn}-${SUFFIX}.csr"
crt_file="${CRT_DIR}/${cn}.crt"
key_file="${KEY_DIR}/${cn}-${SUFFIX}.key"
ovpn_file="${OVPN_DIR}/${cn}-${SUFFIX}.ovpn"
pkcs12_file="${PKCS12_DIR}/${cn}-${SUFFIX}.p12"
2018-01-23 16:52:42 +01:00
# check if CN already exist
2020-05-05 00:28:00 +02:00
if [ -f "${crt_file}" ]; then
printf "%s already exists, do you revoke and recreate it ? [y/N] " "${cn}"
read -r REPLY
resp=$(echo "${REPLY}" | tr 'Y' 'y')
if [ "${resp}" = "y" ]; then
revoke "${cn}"
else
error "Abort"
fi
2020-05-04 18:07:20 +02:00
fi
2018-01-23 16:52:42 +01:00
# ask for CA passphrase
ask_ca_password 0
if [ "${ask_pass}" -eq 1 ]; then
2018-01-23 16:52:42 +01:00
trap 'unset PASSWORD' 0
stty -echo
printf "Password for user key : "
2018-01-24 17:29:55 +01:00
read -r PASSWORD
2018-01-23 16:52:42 +01:00
stty echo
printf "\n"
2020-05-04 18:07:20 +02:00
2020-05-04 14:21:58 +02:00
if [ -z "${PASSWORD}" ]; then
warning "Warning: empty password from input"
fi
2018-01-23 16:52:42 +01:00
fi
# generate private key
2020-05-05 09:42:54 +02:00
OPENSSL_ENV=""
PASS_ARGS=""
if [ -n "${password_file}" ]; then
2020-05-05 09:42:54 +02:00
PASS_ARGS="-aes256 -passout file:${password_file}"
elif [ -n "${PASSWORD}" ]; then
2020-05-05 09:42:54 +02:00
OPENSSL_ENV="PASSWORD=${PASSWORD}"
PASS_ARGS="-aes256 -passout env:PASSWORD"
2018-01-23 16:52:42 +01:00
fi
2020-05-05 09:42:54 +02:00
"${OPENSSL_ENV}" "${OPENSSL_BIN}" genrsa \
-out "${key_file}" \
${PASS_ARGS} \
${KEY_LENGTH} \
>/dev/null 2>&1
2018-01-23 16:52:42 +01:00
2020-05-05 09:42:54 +02:00
# generate csr req
OPENSSL_ENV=""
PASS_ARGS=""
if [ -n "${password_file}" ]; then
2020-05-05 09:42:54 +02:00
PASS_ARGS="-passin file:${password_file}"
elif [ -n "${PASSWORD}" ]; then
2020-05-05 09:42:54 +02:00
OPENSSL_ENV="PASSWORD=${PASSWORD}"
PASS_ARGS="-passin env:PASSWORD"
fi
"${OPENSSL_ENV}" "${OPENSSL_BIN}" req \
-batch \
-new \
-key "${key_file}" \
-out "${csr_file}" \
${PASS_ARGS} \
-config /dev/stdin <<EOF
2020-05-04 18:16:07 +02:00
$(cat "${CONF_FILE}")
2018-01-23 16:52:42 +01:00
commonName_default = ${cn}
EOF
# ca sign and generate cert
2020-05-04 18:16:07 +02:00
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \
-config "${CONF_FILE}" \
2020-05-04 18:07:20 +02:00
-passin env:CA_PASSWORD \
2020-05-05 00:28:00 +02:00
-in "${csr_file}" \
-out "${crt_file}" \
${crt_expiration_arg}
2018-01-23 16:52:42 +01:00
# check if CRT is a valid
2020-05-04 18:16:07 +02:00
"${OPENSSL_BIN}" x509 \
2020-05-04 18:07:20 +02:00
-noout \
-subject \
2020-05-05 00:28:00 +02:00
-in "${crt_file}" \
2020-05-04 23:06:51 +02:00
>/dev/null 2>&1
if [ "$?" -ne 0 ]; then
2020-05-05 00:28:00 +02:00
rm -f "${crt_file}"
2020-05-04 23:06:51 +02:00
fi
2020-05-05 00:28:00 +02:00
if [ ! -f "${crt_file}" ]; then
2020-05-04 18:07:20 +02:00
error "Error in CSR creation"
fi
2018-01-23 16:52:42 +01:00
2020-05-05 00:28:00 +02:00
chmod 640 "${crt_file}"
2018-01-23 16:52:42 +01:00
2020-05-05 00:28:00 +02:00
echo "The CRT file is available in ${crt_file}"
2018-01-23 16:52:42 +01:00
# generate pkcs12 format
2020-05-05 09:42:54 +02:00
OPENSSL_ENV=""
PASS_ARGS=""
if [ -n "${password_file}" ]; then
2020-05-05 09:42:54 +02:00
PASS_ARGS="-passin file:${password_file} -passout file:${password_file}"
elif [ -n "${PASSWORD}" ]; then
2020-05-05 09:42:54 +02:00
OPENSSL_ENV="PASSWORD=${PASSWORD}"
PASS_ARGS="-passin env:PASSWORD -passout env:PASSWORD"
2018-01-23 16:52:42 +01:00
else
2020-05-05 09:42:54 +02:00
PASS_ARGS="-passout pass:"
2018-01-23 16:52:42 +01:00
fi
2020-05-05 09:42:54 +02:00
"${OPENSSL_ENV}" "${OPENSSL_BIN}" pkcs12 \
-export \
-nodes \
-inkey "${key_file}" \
-in "${crt_file}" \
-out "${pkcs12_file}"
${PASS_ARGS}
2018-01-23 16:52:42 +01:00
2020-05-05 00:28:00 +02:00
chmod 640 "${pkcs12_file}"
echo "The PKCS12 config file is available in ${pkcs12_file}"
2018-01-31 12:43:18 +01:00
2018-01-23 16:52:42 +01:00
# generate openvpn format
2020-05-04 18:16:07 +02:00
if [ -e "${CA_DIR}/ovpn.conf" ]; then
2020-05-05 00:28:00 +02:00
cat "${CA_DIR}/ovpn.conf" - > "${ovpn_file}" <<EOF
2014-12-05 16:28:56 +01:00
<ca>
2020-05-04 18:16:07 +02:00
$(cat "${CA_CERT}")
2014-12-05 16:28:56 +01:00
</ca>
2010-10-06 17:34:30 +02:00
2014-12-05 16:28:56 +01:00
<cert>
2020-05-05 00:28:00 +02:00
$(cat "${crt_file}")
2014-12-05 16:28:56 +01:00
</cert>
<key>
2020-05-05 00:28:00 +02:00
$(cat "${key_file}")
2014-12-05 16:28:56 +01:00
</key>
2018-01-17 12:21:39 +01:00
EOF
2020-05-05 00:28:00 +02:00
chmod 640 "${ovpn_file}"
echo "The OpenVPN config file is available in ${ovpn_file}"
2018-01-23 16:52:42 +01:00
fi
2018-01-17 12:21:39 +01:00
fi
2010-10-06 17:34:30 +02:00
}
2018-01-23 16:52:42 +01:00
revoke() {
if [ "${1}" = "" ]; then
show_usage >&2
exit 1
fi
2020-05-05 00:28:00 +02:00
crt_file="${CRT_DIR}/${cn}.crt"
2010-10-06 17:34:30 +02:00
2018-01-23 16:52:42 +01:00
# get CN from param
cn="${1}"
2010-10-06 17:34:30 +02:00
2018-01-23 16:52:42 +01:00
# check if CRT exists
2020-05-05 00:28:00 +02:00
if [ ! -f "${crt_file}" ]; then
error "Unknow CN : ${cn} (\`${crt_file}' not found)"
2020-05-04 18:07:20 +02:00
fi
2010-10-06 17:34:30 +02:00
2018-01-23 16:52:42 +01:00
# check if CRT is a valid
2020-05-04 18:16:07 +02:00
"${OPENSSL_BIN}" x509 \
2020-05-04 18:07:20 +02:00
-noout \
-subject \
2020-05-05 00:28:00 +02:00
-in "${crt_file}" \
2020-05-04 23:06:51 +02:00
>/dev/null 2>&1
if [ "$?" -ne 0 ]; then
2020-05-05 00:28:00 +02:00
error "${crt_file} is not a valid CRT, you must delete it !"
2020-05-04 23:06:51 +02:00
fi
2010-10-06 17:34:30 +02:00
2018-01-23 16:52:42 +01:00
# ask for CA passphrase
ask_ca_password 0
2010-10-06 17:34:30 +02:00
2020-05-05 00:28:00 +02:00
echo "Revoke certificate ${crt_file} :"
2020-05-04 18:16:07 +02:00
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \
-config "${CONF_FILE}" \
2020-05-04 18:07:20 +02:00
-passin env:CA_PASSWORD \
2020-05-05 00:28:00 +02:00
-revoke "${crt_file}"
if [ "$?" -eq 0 ]; then
rm "${crt_file}"
fi
2010-10-06 17:34:30 +02:00
2020-05-04 18:16:07 +02:00
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL_BIN}" ca \
-config "${CONF_FILE}" \
2020-05-04 18:07:20 +02:00
-passin env:CA_PASSWORD \
2020-05-05 00:28:00 +02:00
-gencrl \
-out "${CRL}"
2010-10-06 17:34:30 +02:00
}
2018-01-17 12:21:39 +01:00
list() {
2020-05-04 18:16:07 +02:00
if [ ! -f "${INDEX_FILE}" ]; then
2020-05-04 18:07:20 +02:00
exit 0
fi
list_valid=0
list_revoked=1
while :; do
case "${1}" in
-a|--all)
list_valid=0
list_revoked=0
shift;;
-v|--valid)
list_valid=0
list_revoked=1
shift;;
-r|--revoked)
list_valid=1
list_revoked=0
shift;;
--)
shift
break;;
-?*)
warning "unknow option ${1} (ignored)"
shift;;
*)
break;;
esac
done
2020-05-04 18:07:20 +02:00
if [ "${list_valid}" -eq 0 ]; then
2020-05-04 18:16:07 +02:00
certs=$(grep "^V" "${INDEX_FILE}")
2020-05-04 18:07:20 +02:00
fi
2020-05-04 18:07:20 +02:00
if [ "${list_revoked}" -eq 0 ]; then
2020-05-04 18:16:07 +02:00
certs=$(grep "^R" "${INDEX_FILE}")
2020-05-04 18:07:20 +02:00
fi
2020-05-04 18:07:20 +02:00
if [ "${list_valid}" -eq 0 ] && [ "${list_revoked}" -eq 0 ]; then
2020-05-04 18:16:07 +02:00
certs=$(cat "${INDEX_FILE}")
2020-05-04 18:07:20 +02:00
fi
echo "${certs}" | grep -Eo "CN\s*=[^,/]*" | cut -d'=' -f2 | xargs -n1
2018-01-17 12:21:39 +01:00
}
2010-10-06 17:34:30 +02:00
2020-05-04 23:12:48 +02:00
cert_end_date() {
"${OPENSSL_BIN}" x509 -noout -enddate -in "${1}" | cut -d'=' -f2
}
check() {
# default expiration alert
# TODO : permit override with parameters
min_day=90
cur_epoch=$(date -u +'%s')
2020-05-04 18:16:07 +02:00
for cert in ${CRT_DIR}/*; do
2020-05-04 23:12:48 +02:00
end_date=$(cert_end_date "${cert}")
end_epoch=$(date -ud "${end_date}" +'%s')
2020-05-04 23:08:19 +02:00
diff_epoch=$(( end_epoch - cur_epoch ))
diff_day=$(( diff_epoch / 60 / 60 / 24 ))
if [ "${diff_day}" -lt "${min_day}" ]; then
if [ "${diff_day}" -le 0 ]; then
echo "${cert} has expired"
else
echo "${cert} expire in ${diff_day} days"
fi
fi
done
}
is_user() {
getent passwd "${1}" >/dev/null
}
is_group() {
getent group "${1}" >/dev/null
}
2018-01-17 12:21:39 +01:00
main() {
2018-01-31 12:43:18 +01:00
# default config
# TODO : override with /etc/default/shellpki
2020-05-04 18:16:07 +02:00
CONF_FILE="/etc/shellpki/openssl.cnf"
2020-05-04 14:21:58 +02:00
if [ "$(uname)" = "OpenBSD" ]; then
2020-05-04 18:16:07 +02:00
PKI_USER="_shellpki"
2020-05-04 14:21:58 +02:00
else
2020-05-04 18:16:07 +02:00
PKI_USER="shellpki"
2020-05-04 14:21:58 +02:00
fi
2018-01-31 12:43:18 +01:00
2020-05-04 18:16:07 +02:00
if [ "${USER}" != "root" ] && [ "${USER}" != "${PKI_USER}" ]; then
2020-05-04 17:42:01 +02:00
error "Please become root before running ${0} !"
fi
2018-06-27 14:48:11 +02:00
2018-01-31 12:43:18 +01:00
# retrieve CA path from config file
2020-05-04 18:16:07 +02:00
CA_DIR=$(grep -E "^dir" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1)
CA_KEY=$(grep -E "^private_key" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
CA_CERT=$(grep -E "^certificate" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
OCSP_KEY="${CA_DIR}/ocsp.key"
OCSP_CERT="${CA_DIR}/ocsp.pem"
CRT_DIR=$(grep -E "^certs" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
TMP_DIR=$(grep -E "^new_certs_dir" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
INDEX_FILE=$(grep -E "^database" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
SERIAL=$(grep -E "^serial" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
CRL=$(grep -E "^crl" "${CONF_FILE}" | cut -d'=' -f2 | xargs -n1 | sed "s~\$dir~${CA_DIR}~")
2018-01-31 12:43:18 +01:00
# directories for clients key, csr, crt
2020-05-04 18:16:07 +02:00
KEY_DIR="${CA_DIR}/private"
CSR_DIR="${CA_DIR}/requests"
PKCS12_DIR="${CA_DIR}/pkcs12"
OVPN_DIR="${CA_DIR}/openvpn"
2018-01-31 12:43:18 +01:00
CA_KEY_LENGTH=4096
if [ "${CA_KEY_LENGTH}" -lt 4096 ]; then
error "CA key must be at least 4096 bits long."
fi
2020-05-04 18:16:07 +02:00
KEY_LENGTH=2048
if [ "${KEY_LENGTH}" -lt 2048 ]; then
error "User key must be at least 2048 bits long."
fi
2018-01-17 12:21:39 +01:00
2020-05-04 18:16:07 +02:00
OPENSSL_BIN=$(command -v openssl)
SUFFIX=$(/bin/date +"%s")
if ! is_user "${PKI_USER}" || ! is_group "${PKI_USER}"; then
2020-05-04 18:16:07 +02:00
error "You must create ${PKI_USER} user and group !"
2018-01-17 12:21:39 +01:00
fi
2014-12-05 16:28:56 +01:00
2020-05-04 18:16:07 +02:00
if [ ! -e "${CONF_FILE}" ]; then
error "${CONF_FILE} is missing"
2020-05-04 18:07:20 +02:00
fi
2010-10-06 17:34:30 +02:00
2020-05-04 18:16:07 +02:00
mkdir -p "${CA_DIR}" "${CRT_DIR}" "${KEY_DIR}" "${CSR_DIR}" "${PKCS12_DIR}" "${OVPN_DIR}" "${TMP_DIR}"
2018-01-17 12:21:39 +01:00
2018-01-23 16:52:42 +01:00
command=${1:-help}
case "${command}" in
2018-01-17 12:21:39 +01:00
init)
2018-01-23 16:52:42 +01:00
shift
2018-06-27 11:45:03 +02:00
init "$@"
2018-01-17 12:21:39 +01:00
;;
2018-06-27 12:52:20 +02:00
ocsp)
shift
ocsp "$@"
;;
2018-01-17 12:21:39 +01:00
create)
2018-01-23 16:52:42 +01:00
shift
create "$@"
2018-01-17 12:21:39 +01:00
;;
revoke)
2018-01-23 16:52:42 +01:00
shift
revoke "$@"
2018-01-17 12:21:39 +01:00
;;
list)
2018-01-23 16:52:42 +01:00
shift
list "$@"
2018-01-17 12:21:39 +01:00
;;
check)
shift
check "$@"
;;
version)
show_version
exit 0
;;
help)
show_usage
exit 0
;;
2018-01-17 12:21:39 +01:00
*)
show_usage >&2
2018-01-17 12:21:39 +01:00
exit 1
;;
esac
2018-01-31 12:43:18 +01:00
# fix right
2020-05-04 18:16:07 +02:00
chown -R "${PKI_USER}":"${PKI_USER}" "${CA_DIR}"
chmod 750 "${CA_DIR}" "${CRT_DIR}" "${KEY_DIR}" "${CSR_DIR}" "${PKCS12_DIR}" "${OVPN_DIR}" "${TMP_DIR}"
chmod 600 "${INDEX_FILE}"* "${SERIAL}"* "${CA_KEY}" "${CRL}"
chmod 640 "${CA_CERT}"
2018-01-17 12:21:39 +01:00
}
2010-10-06 17:34:30 +02:00
2018-01-17 12:21:39 +01:00
main "$@"