Shellpki is no more interactive

This commit is contained in:
Victor LABORIE 2018-01-23 16:52:42 +01:00
parent 1921f9e5e5
commit ef12ea3bb9
2 changed files with 231 additions and 116 deletions

View file

@ -13,28 +13,29 @@ install -m 0755 shellpki.sh /usr/local/sbin/shellpki
## Usage ## Usage
Initialize PKI creating CA key and certificate :
~~~
shellpki init
~~~ ~~~
Usage: ./shellpki.sh <subcommand> [options] [CommonName]
Create a certificate and key on the server : Initialize PKI (create CA key and self-signed cert) :
~~~ ./shellpki.sh init
shellpki create
~~~
Create a certificate without key from a CSR : Create a client cert with key and CSR directly generated on server
(use -p for set a password on client key) :
~~~ ./shellpki.sh create [-p] <commonName>
shellpki fromcsr
~~~
Revoke a certificate : Create a client cert from a CSR (doesn't need key) :
~~~ ./shellpki.sh create -f <path>
shellpki revoke
Revoke a client cert with is commonName (CN) :
./shellpki.sh revoke <commonName>
List all actually valid commonName (CN) :
./shellpki.sh list
~~~ ~~~
## License ## License

View file

@ -3,6 +3,8 @@
# shellpki is a wrapper around openssl to manage a small PKI # shellpki is a wrapper around openssl to manage a small PKI
# #
set -eu
init() { init() {
umask 0177 umask 0177
@ -27,64 +29,200 @@ init() {
-out "${CADIR}/cacert.pem" -out "${CADIR}/cacert.pem"
} }
check_cn() { usage() {
cn="${1}" cat <<EOF
if [ -f "${CADIR}/certs/${cn}.crt" ]; then Usage: ${0} <subcommand> [options] [CommonName]
echo "Please revoke actual ${cn} cert before creating one"
echo Initialize PKI (create CA key and self-signed cert) :
echo "Press return to continue..."
read -r REPLY ${0} init
exit 1
fi Create a client cert with key and CSR directly generated on server
(use -p for set a password on client key) :
${0} create [-p] <commonName>
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
EOF
}
error() {
echo "${1}" >&2
exit 1
}
warning() {
echo "${1}" >&2
}
ask_ca_password() {
[ ! -f "${CADIR}/private.key" ] && error "You must initialize your's PKI with shellpki init !"
attempt=$((${1} + 1))
[ "${attempt}" -gt 1 ] && warning "Invalid password, retry."
trap 'unset CA_PASSWORD' 0
stty -echo
printf "Password for CA key : "
read CA_PASSWORD
stty echo
printf "\n"
[ "${CA_PASSWORD}" != "" ] || ask_ca_password "${attempt}"
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" rsa \
-in "${CADIR}/private.key" \
-passin env:CA_PASSWORD \
>/dev/null 2>&1 \
|| ask_ca_password "${attempt}"
} }
create() { create() {
umask 0137 from_csr=1
echo "Please enter your CN (Common Name)" with_pass=1
read -r cn
echo
echo "Your CN is '${cn}'"
echo "Press return to continue..."
read -r REPLY
echo
# check if CN already exist while getopts ":f:p" opt; do
check_cn "${cn}" case "$opt" in
f)
[ ! -f "${OPTARG}" ] && error "${OPTARG} must be a file"
from_csr=0
csr_file=$(readlink -f "${OPTARG}")
shift 2;;
p)
with_pass=0
shift;;
:)
error "Option -$OPTARG requires an argument."
esac
done
# generate private key cn="${1:-}"
echo "Should private key be protected by a passphrase? [y/N] "
read -r REPLY [ "${cn}" = "--" ] && shift
if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then
"$OPENSSL" genrsa -aes256 -out "${KEYDIR}/${cn}-${TIMESTAMP}.key" 2048 if [ "${from_csr}" -eq 0 ]; then
[ "${with_pass}" -eq 0 ] && warning "Warning: -p made nothing with -f"
# ask for CA passphrase
ask_ca_password 0
# check if csr_file is a CSR
"${OPENSSL}" req \
-noout -subject \
-in "${csr_file}" \
>/dev/null 2>&1 \
|| error "${csr_file} is not a valid CSR !"
# check if csr_file contain a CN
"${OPENSSL}" req \
-noout -subject \
-in "${csr_file}" \
| grep -Eo "CN\s*=[^,/]*" \
>/dev/null 2>&1 \
|| error "${csr_file} don't contain a CommonName !"
# get CN from CSR
cn=$("${OPENSSL}" req -noout -subject -in "${csr_file}"|grep -Eo "CN\s*=[^,/]*"|cut -d'=' -f2|xargs)
# check if CN already exist
[ -f "${CADIR}/certs/${cn}.crt" ] && error "${cn} already used !"
# ca sign and generate cert
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" ca \
-config "${CONFFILE}" \
-in "${csr_file}" \
-passin env:CA_PASSWORD \
-out "${CADIR}/certs/${cn}.crt"
else else
"$OPENSSL" genrsa -out "${KEYDIR}/${cn}-${TIMESTAMP}.key" 2048 [ -z "${cn}" ] && usage >&2 && exit 1
fi
# generate csr req # check if CN already exist
"$OPENSSL" req -batch \ [ -f "${CADIR}/certs/${cn}.crt" ] && error "${cn} already used !"
-new \
-key "${KEYDIR}/${cn}-${TIMESTAMP}.key" \ # ask for client key passphrase
-out "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \ if [ "${with_pass}" -eq 0 ]; then
-config /dev/stdin <<EOF trap 'unset PASSWORD' 0
stty -echo
printf "Password for user key : "
read PASSWORD
stty echo
printf "\n"
fi
# ask for CA passphrase
ask_ca_password 0
# generate private key
if [ "${with_pass}" -eq 0 ]; then
PASSWORD="${PASSWORD}" "$OPENSSL" genrsa \
-aes256 -passout env:PASSWORD \
-out "${KEYDIR}/${cn}-${TIMESTAMP}.key" \
2048 >/dev/null 2>&1
else
"$OPENSSL" genrsa \
-out "${KEYDIR}/${cn}-${TIMESTAMP}.key" \
2048 >/dev/null 2>&1
fi
if [ "${with_pass}" -eq 0 ]; then
# generate csr req
PASSWORD="${PASSWORD}" "$OPENSSL" req \
-batch -new \
-key "${KEYDIR}/${cn}-${TIMESTAMP}.key" \
-passin env:PASSWORD \
-out "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
-config /dev/stdin <<EOF
$(cat "${CONFFILE}") $(cat "${CONFFILE}")
commonName_default = ${cn} commonName_default = ${cn}
EOF EOF
else
# generate csr req
"$OPENSSL" req \
-batch -new \
-key "${KEYDIR}/${cn}-${TIMESTAMP}.key" \
-out "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
-config /dev/stdin <<EOF
$(cat "${CONFFILE}")
commonName_default = ${cn}
EOF
fi
# ca sign and generate cert # ca sign and generate cert
"${OPENSSL}" ca \ CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" ca \
-config "${CONFFILE}" \ -config "${CONFFILE}" \
-in "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \ -passin env:CA_PASSWORD \
-out "${CADIR}/certs/${cn}.crt" -in "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
-out "${CADIR}/certs/${cn}.crt"
# generate pem format # check if CRT is a valid
cat "${CADIR}/certs/${cn}.crt" "${CADIR}/cacert.pem" "${KEYDIR}/${cn}-${TIMESTAMP}.key" >> "${PEMDIR}/${cn}-${TIMESTAMP}.pem" "${OPENSSL}" x509 \
-noout -subject \
-in "${CADIR}/certs/${cn}.crt" \
>/dev/null 2>&1 \
|| rm -f "${CADIR}/certs/${cn}.crt"
# generate pkcs12 format [ -f "${CADIR}/certs/${cn}.crt" ] || error "Error in CSR creation"
openssl pkcs12 -export -nodes -passout pass: -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" -in "${CADIR}/certs/${cn}.crt" -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12"
# generate openvpn format # generate pem format
if [ -e "${PREFIX}/ovpn.conf" ]; then cat "${CADIR}/certs/${cn}.crt" "${CADIR}/cacert.pem" "${KEYDIR}/${cn}-${TIMESTAMP}.key" >> "${PEMDIR}/${cn}-${TIMESTAMP}.pem"
cat "${PREFIX}/ovpn.conf" > "${OVPNDIR}/${cn}-${TIMESTAMP}.ovpn" <<EOF
# generate pkcs12 format
if [ "${with_pass}" -eq 0 ]; then
PASSWORD="${PASSWORD}" "${OPENSSL}" pkcs12 -export -nodes -passin env:PASSWORD -passout env:PASSWORD -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" -in "${CADIR}/certs/${cn}.crt" -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12"
else
"${OPENSSL}" pkcs12 -export -nodes -passout pass: -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" -in "${CADIR}/certs/${cn}.crt" -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12"
fi
# generate openvpn format
if [ -e "${PREFIX}/ovpn.conf" ]; then
cat "${PREFIX}/ovpn.conf" > "${OVPNDIR}/${cn}-${TIMESTAMP}.ovpn" <<EOF
<ca> <ca>
$(cat "${CADIR}/cacert.pem") $(cat "${CADIR}/cacert.pem")
</ca> </ca>
@ -97,67 +235,41 @@ $(cat "${CADIR}/certs/${cn}.crt")
$(cat "${KEYDIR}/${cn}-${TIMESTAMP}.key") $(cat "${KEYDIR}/${cn}-${TIMESTAMP}.key")
</key> </key>
EOF EOF
echo "The configuration file is available in ${OVPNDIR}/${cn}.ovpn" echo "The configuration file is available in ${OVPNDIR}/${cn}.ovpn"
fi
fi fi
} }
fromcsr() {
echo "Please enter path for your CSR request file"
read -r path
echo
if [ ! -e "${path}" ]; then
echo "Error in path..." >&2
echo >&2
echo "Press return to continue..." >&2
read -r REPLY
exit 1
fi
path=$(readlink -f "${path}")
# get CN from CSR
cn=$(openssl req -noout -subject -in "${path}"|grep -Eo "CN=[^/]*"|cut -d'=' -f2)
# check if CN already exist
check_cn "${cn}"
# copy CSR to CSRDIR
cp "$path" "${CSRDIR}/${cn}-${TIMESTAMP}.csr"
# ca sign and generate cert
"${OPENSSL}" ca \
-config "${CONFFILE}" \
-in "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
-out "${CADIR}/certs/${cn}.crt"
}
revoke() { revoke() {
echo "Please enter CN (Common Name) to revoke" [ "${1}" = "" ] && usage >&2 && exit 1
read -r cn
echo
echo "CN '${cn}' will be revoked"
echo "Press return to continue..."
read -r REPLY
echo
[ ! -f "${CADIR}/certs/${cn}.crt" ] && echo "Unknow CN : ${cn}" >&2 && exit 1 # get CN from param
cn="${1}"
# check if CRT exists
[ ! -f "${CADIR}/certs/${cn}.crt" ] && error "Unknow CN : ${cn}"
# check if CRT is a valid
"${OPENSSL}" x509 -noout -subject -in "${CADIR}/certs/${cn}.crt" >/dev/null 2>&1 || error "${CADIR}/certs/${cn}.crt is not a valid CRT, you msust delete it !"
# ask for CA passphrase
ask_ca_password 0
echo "Revoke certificate ${CADIR}/certs/${cn}.crt :" echo "Revoke certificate ${CADIR}/certs/${cn}.crt :"
"$OPENSSL" ca \ CA_PASSWORD="${CA_PASSWORD}" "$OPENSSL" ca \
-config "${CONFFILE}" \ -config "${CONFFILE}" \
-revoke "${CADIR}/certs/${cn}.crt" \ -passin env:CA_PASSWORD \
-revoke "${CADIR}/certs/${cn}.crt" \
&& rm "${CADIR}/certs/${cn}.crt" && rm "${CADIR}/certs/${cn}.crt"
echo "Update CRL :" CA_PASSWORD="${CA_PASSWORD}" "$OPENSSL" ca \
"$OPENSSL" ca \ -config "${CONFFILE}" \
-config "${CONFFILE}" \ -passin env:CA_PASSWORD \
-gencrl -out "${CADIR}/crl.pem" -gencrl -out "${CADIR}/crl.pem"
} }
list() { list() {
echo "* List of allowed CN :" [ -f /etc/shellpki/ca/index.txt ] && grep -Eo "CN\s*=[^,/]*" "${CADIR}/index.txt" | cut -d'=' -f2 | xargs -n1
ls -1 "${CADIR}/certs"
} }
main() { main() {
@ -207,29 +319,31 @@ main() {
# fix right # fix right
find "${PREFIX}" ! -path "${CADIR}" -exec chown "${PKIUSER}":"${PKIUSER}" {} \; -exec chmod u=rwX,g=rX,o= {} \; find "${PREFIX}" ! -path "${CADIR}" -exec chown "${PKIUSER}":"${PKIUSER}" {} \; -exec chmod u=rwX,g=rX,o= {} \;
case "$1" in command=${1:-help}
case "${command}" in
init) init)
shift
init init
;; ;;
create) create)
create shift
;; create "$@"
fromcsr)
fromcsr
;; ;;
revoke) revoke)
revoke shift
revoke "$@"
;; ;;
list) list)
list shift
list "$@"
;; ;;
*) *)
echo "Usage: ${0} {init|create|fromcsr|revoke|list}" >&2 usage >&2
exit 1 exit 1
;; ;;
esac esac