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
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 create
~~~
./shellpki.sh init
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 fromcsr
~~~
./shellpki.sh create [-p] <commonName>
Revoke a certificate :
Create a client cert from a CSR (doesn't need key) :
~~~
shellpki revoke
./shellpki.sh create -f <path>
Revoke a client cert with is commonName (CN) :
./shellpki.sh revoke <commonName>
List all actually valid commonName (CN) :
./shellpki.sh list
~~~
## License

View File

@ -3,6 +3,8 @@
# shellpki is a wrapper around openssl to manage a small PKI
#
set -eu
init() {
umask 0177
@ -27,64 +29,200 @@ init() {
-out "${CADIR}/cacert.pem"
}
check_cn() {
cn="${1}"
if [ -f "${CADIR}/certs/${cn}.crt" ]; then
echo "Please revoke actual ${cn} cert before creating one"
echo
echo "Press return to continue..."
read -r REPLY
exit 1
fi
usage() {
cat <<EOF
Usage: ${0} <subcommand> [options] [CommonName]
Initialize PKI (create CA key and self-signed cert) :
${0} init
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() {
umask 0137
echo "Please enter your CN (Common Name)"
read -r cn
echo
echo "Your CN is '${cn}'"
echo "Press return to continue..."
read -r REPLY
echo
from_csr=1
with_pass=1
# check if CN already exist
check_cn "${cn}"
while getopts ":f:p" opt; do
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
echo "Should private key be protected by a passphrase? [y/N] "
read -r REPLY
if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then
"$OPENSSL" genrsa -aes256 -out "${KEYDIR}/${cn}-${TIMESTAMP}.key" 2048
cn="${1:-}"
[ "${cn}" = "--" ] && shift
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
"$OPENSSL" genrsa -out "${KEYDIR}/${cn}-${TIMESTAMP}.key" 2048
fi
[ -z "${cn}" ] && usage >&2 && exit 1
# generate csr req
"$OPENSSL" req -batch \
-new \
-key "${KEYDIR}/${cn}-${TIMESTAMP}.key" \
-out "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
-config /dev/stdin <<EOF
# check if CN already exist
[ -f "${CADIR}/certs/${cn}.crt" ] && error "${cn} already used !"
# ask for client key passphrase
if [ "${with_pass}" -eq 0 ]; then
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}")
commonName_default = ${cn}
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
"${OPENSSL}" ca \
-config "${CONFFILE}" \
-in "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
-out "${CADIR}/certs/${cn}.crt"
# ca sign and generate cert
CA_PASSWORD="${CA_PASSWORD}" "${OPENSSL}" ca \
-config "${CONFFILE}" \
-passin env:CA_PASSWORD \
-in "${CSRDIR}/${cn}-${TIMESTAMP}.csr" \
-out "${CADIR}/certs/${cn}.crt"
# generate pem format
cat "${CADIR}/certs/${cn}.crt" "${CADIR}/cacert.pem" "${KEYDIR}/${cn}-${TIMESTAMP}.key" >> "${PEMDIR}/${cn}-${TIMESTAMP}.pem"
# check if CRT is a valid
"${OPENSSL}" x509 \
-noout -subject \
-in "${CADIR}/certs/${cn}.crt" \
>/dev/null 2>&1 \
|| rm -f "${CADIR}/certs/${cn}.crt"
# generate pkcs12 format
openssl pkcs12 -export -nodes -passout pass: -inkey "${KEYDIR}/${cn}-${TIMESTAMP}.key" -in "${CADIR}/certs/${cn}.crt" -out "${PKCS12DIR}/${cn}-${TIMESTAMP}.p12"
[ -f "${CADIR}/certs/${cn}.crt" ] || error "Error in CSR creation"
# generate openvpn format
if [ -e "${PREFIX}/ovpn.conf" ]; then
cat "${PREFIX}/ovpn.conf" > "${OVPNDIR}/${cn}-${TIMESTAMP}.ovpn" <<EOF
# generate pem format
cat "${CADIR}/certs/${cn}.crt" "${CADIR}/cacert.pem" "${KEYDIR}/${cn}-${TIMESTAMP}.key" >> "${PEMDIR}/${cn}-${TIMESTAMP}.pem"
# 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>
$(cat "${CADIR}/cacert.pem")
</ca>
@ -97,67 +235,41 @@ $(cat "${CADIR}/certs/${cn}.crt")
$(cat "${KEYDIR}/${cn}-${TIMESTAMP}.key")
</key>
EOF
echo "The configuration file is available in ${OVPNDIR}/${cn}.ovpn"
echo "The configuration file is available in ${OVPNDIR}/${cn}.ovpn"
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() {
echo "Please enter CN (Common Name) to revoke"
read -r cn
echo
echo "CN '${cn}' will be revoked"
echo "Press return to continue..."
read -r REPLY
echo
[ "${1}" = "" ] && usage >&2 && exit 1
[ ! -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 :"
"$OPENSSL" ca \
-config "${CONFFILE}" \
-revoke "${CADIR}/certs/${cn}.crt" \
CA_PASSWORD="${CA_PASSWORD}" "$OPENSSL" ca \
-config "${CONFFILE}" \
-passin env:CA_PASSWORD \
-revoke "${CADIR}/certs/${cn}.crt" \
&& rm "${CADIR}/certs/${cn}.crt"
echo "Update CRL :"
"$OPENSSL" ca \
-config "${CONFFILE}" \
CA_PASSWORD="${CA_PASSWORD}" "$OPENSSL" ca \
-config "${CONFFILE}" \
-passin env:CA_PASSWORD \
-gencrl -out "${CADIR}/crl.pem"
}
list() {
echo "* List of allowed CN :"
ls -1 "${CADIR}/certs"
[ -f /etc/shellpki/ca/index.txt ] && grep -Eo "CN\s*=[^,/]*" "${CADIR}/index.txt" | cut -d'=' -f2 | xargs -n1
}
main() {
@ -207,29 +319,31 @@ main() {
# fix right
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)
shift
init
;;
create)
create
;;
fromcsr)
fromcsr
shift
create "$@"
;;
revoke)
revoke
shift
revoke "$@"
;;
list)
list
shift
list "$@"
;;
*)
echo "Usage: ${0} {init|create|fromcsr|revoke|list}" >&2
usage >&2
exit 1
;;
esac