Shellpki is no more interactive
This commit is contained in:
parent
1921f9e5e5
commit
ef12ea3bb9
31
README.md
31
README.md
|
@ -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
|
||||||
|
|
316
shellpki.sh
316
shellpki.sh
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue