#!/bin/bash
#
# Copyright: Patrick Koppen
# License:   GPLv3
# Version:   1.2
# Date:      29.12.2012

set -e

etc=/etc/burp
dir=${etc}/CA
conf=${etc}/CA.cnf

name=$(hostname -f)
ca_days=7300
size=2048

def_umask=022
sec_umask=077

function help() {
  cat <<EOF
$0: Help:
    -h|--help               show help
    -i|--init               inititalize CA
    -k|--key                generate new key
    -K|--keypath <path>     path to new key
    -r|--request            generate certificate sign request
    -R|--requestpath <path> path to certificate sign request
    -s|--sign               sign csr (use --ca <ca> and --name <name>)
       --batch              do not prompt for anything
       --revoke <number>    revoke certificate with serial number
       --crl                generate certificate revoke list
    -d|--dir <dir>          ca output dir (default: $dir)
    -c|--config             config file (default: $conf)
    -n|--name               name (default: $name)
    -D|--days               valid days for certificate (default in config file)
       --ca_days            valid days for CA certificate (default: $ca_days)
    -S|--size               key size (default: $size)
    -a|--ca                 ca name if different from name
    -f|--dhfile <path>      generate Diffie-Hellman file
    -A|--altname            subjectAltName
EOF
}

check_second_arg()
{
	if [ "$1" -eq 0 ] ; then
		help
		exit 1
	fi
}

while [ $# -gt 0 ]
do
    case $1 in
    -h|--help) help; exit 0 ;;
    -i|--init) init=yes ;;
    -k|--key) key=yes ;;
    -K|--keypath) check_second_arg $#; keypath=$2; shift ;;
    -r|--request) request=yes ;;
    -R|--requestpath) check_second_arg $#; requestpath=$2; shift ;;
    -s|--sign) sign=yes ;;
       --batch) batch="-batch" ;;
       --revoke) check_second_arg $#; revoke=$2; shift ;;
       --crl) crl=yes ;;
    -d|--dir) check_second_arg $#; dir=$2; shift ;;
    -c|--config) check_second_arg $#; conf=$2; shift ;;
    -n|--name) check_second_arg $#; name=$2; shift ;;
    -D|--days) check_second_arg $#; days="-days $2"; shift ;;
       --ca_days) check_second_arg $#; ca_days=$2; shift ;;
    -S|--size) check_second_arg $#; size=$2; shift ;;
    -a|--ca) check_second_arg $#; ca=$2; shift ;;
    -f|--dhfile) check_second_arg $#; dhfile=$2; shift ;;
    -A|--altname) check_second_arg $#; altname=$2; shift ;;
    --) shift; break;;
    -*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    *) break;;
    esac
    shift
done

if [ -n "$dhfile" ] ; then
	openssl dhparam -out "$dhfile" 1024
	r=$?
	chmod 600 "$dhfile"
	exit $r
fi

if [ -z "$ca" ]; then
  ca=${name}
fi

if [ -n "$altname" ]; then
  altname="subjectAltName=$altname"
fi

# init CA
if [ "$init" = "yes" ]; then
  echo "Init... ${ca}"
  if [ ! -f ${conf} ]; then
     echo "$0: error - config ${conf} missing" 1>&2; exit 1
  fi
  if [ -d ${dir} ]; then
     echo "$0: error - ${dir} exists, ca initialized" 1>&2; exit 1
  fi
  
  mkdir ${dir}
  mkdir ${dir}/certs
  mkdir ${dir}/newcerts

  umask ${sec_umask}
  openssl genrsa -out ${dir}/CA_${ca}.key ${size}
  umask ${def_umask}
  TEMP=$(mktemp /tmp/burp_ca.tmp.XXXXXXXX || echo /tmp/burp_ca.tmp.$$)
  cat <<-EOF > ${TEMP}
	RANDFILE                = /dev/urandom

	[ req ]
	distinguished_name      = req_distinguished_name
	prompt                  = no

	[ v3_ca ]
	basicConstraints=CA:true
	subjectKeyIdentifier=hash
	authorityKeyIdentifier=keyid,issuer:always

	[ req_distinguished_name ]
	commonName                      = ${ca}
EOF
  CA_DIR=${dir} openssl req -config ${TEMP} -new -x509 -days $ca_days \
    -key ${dir}/CA_${ca}.key -out ${dir}/CA_${ca}.crt -extensions v3_ca
  rm -f $TEMP

  : > ${dir}/index.txt
  echo "00" > ${dir}/serial.txt
  echo "00" > ${dir}/crlnumber.txt

fi

[ -z "$keypath" ] && keypath=${dir}/${name}.key

# generate key
if [ "$key" = "yes" ]; then
  echo "generating key ${name}: ${keypath}"
  umask ${sec_umask}
  openssl genrsa -out "${keypath}" ${size}
  umask ${def_umask}
fi

# generate signing request 
[ -z "$requestpath" ] && requestpath=${dir}/${name}.csr
if [ "$request" = "yes" ]; then
  echo "generating request ${name}"
  TEMP=$(mktemp /tmp/burp_ca.tmp.XXXXXXXX || echo /tmp/burp_ca.tmp.$$)
  cat <<-EOF > ${TEMP}
	RANDFILE                = /dev/urandom
        req_extensions          = v3_req

	[ req ]
	distinguished_name      = req_distinguished_name
	prompt                  = no

	[ v3_req ]
	basicConstraints=CA:false
	$altname

	[ req_distinguished_name ]
	commonName                      = ${name}

EOF
  openssl req -config ${TEMP} -new -key "${keypath}" \
    -out "${requestpath}" -extensions v3_req
  rm -f $TEMP
fi


# sign 
if [ "$sign" = "yes" ]; then
  serial=$(cat ${dir}/serial.txt)
  CA_DIR=${dir} openssl ca -config ${conf} -name ca \
    -in ${dir}/${name}.csr -out $dir/${name}.crt ${days} \
    -keyfile ${dir}/CA_${ca}.key -cert ${dir}/CA_${ca}.crt \
    ${batch}
  if [ ! -f ${dir}/newcerts/${serial}.pem ]; then
    exit 0
  fi
  mv ${dir}/newcerts/${serial}.pem ${dir}/certs/${serial}.pem
  c_rehash ${dir}/certs
fi

#revoke
if [ -n "$revoke" ]; then
  CA_DIR=${dir} openssl ca -config ${conf} -name ca \
    -revoke ${dir}/certs/${revoke}.pem \
    -keyfile ${dir}/CA_${ca}.key -cert ${dir}/CA_${ca}.crt \
    ${batch}
fi

#crl
if [ -n "$crl" ]; then
  CA_DIR=${dir} openssl ca -config ${conf} -name ca \
   -gencrl -out ${dir}/CA_${ca}.crl \
   -keyfile ${dir}/CA_${ca}.key -cert ${dir}/CA_${ca}.crt 
fi

exit 0
