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
|
||||
|
||||
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
|
||||
|
|
316
shellpki.sh
316
shellpki.sh
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue