#!/bin/sh
##
## sign_cert.sh - Create/Sign/Revoke a SSL certificate
## by eightn
##
## /original idea from sign.sh script by Ralf S. Engelschall/
##


#   argument line handling
MODE=$1
CSR=$2

ROOTCA="tmp_org"
#root CA name

O="TempOrg"
C="RU"
ST="Euro"
L="Moscow"
OU="network IT"
EMAIL="email@tmp_org.ru"

BITS=2048
CLIENT_DAYS=730
SERVER_DAYS=1461
CRL_DAYS=30

ROOTCA=${ROOTCA}-ca
SIGN_CA=$ROOTCA

CA_CONFIG="ca.config"

CLIENT_PURPOSES="digitalSignature, keyEncipherment"
CLIENT_CERTTYPE="client, email, objsign"


function usage()
{
	echo "Usage: sign_cert.sh <mode> [<common_name>[.csr]]"; 
	echo "mode is <client_cert>, <server_cert>, <revoke>, <regen_crl>";
}


if [ $# -eq 1 ] && [ "$MODE" != "regen_crl" ] ; then
    usage
	exit 1
fi

if [ $# -lt 1 ] || [ $# -gt 2 ] ; then
    usage 
    exit 1
fi

if [ $# -eq 2 ] && [ "$MODE" != "client_cert" ] && [ "$MODE" != "server_cert" ] && [ "$MODE" != "revoke_cert" ] ; then
	echo "invalid mode string";
	echo "mode is <client_cert>, <server_cert>, <revoke>"; 
	exit 1
fi
    
    
case $CSR in
	   *.csr )	CERT="`echo $CSR | sed -e 's/\.csr/.crt/'`" ;
	   			NAME="`echo $CSR | sed -e 's/\.csr//'`" ;;
       * ) 		CERT="$CSR.crt" ;
    	   		NAME="$CSR";    
    	   		CSR="${CSR}.csr" ;;
esac


#   make sure environment exists
if [ ! -d ca.db.certs ]; then
    mkdir ca.db.certs
fi
if [ ! -d certificates ]; then
    mkdir certificates
    chmod 700 certificates
fi
if [ ! -f ca.db.serial ]; then
    echo '01' >ca.db.serial
fi
if [ ! -f ca.db.index ]; then
    cp /dev/null ca.db.index
fi

cd certificates

#------------------------------------------------------------------------
# create CA config
#
function create_ca_config()
{

	cat >$CA_CONFIG <<EOT
[ req ]
default_bits = $BITS
distinguished_name = req_DN

[ req_DN ]
countryName = "1. Country Name (2 letter code)"
countryName_default = "$C"
countryName_min = 2
countryName_max = 2
stateOrProvinceName = "2. State or Province Name (full name) "
stateOrProvinceName_default = "$ST"
localityName = "3. Locality Name (eg, city) "
localityName_default = "$L"
0.organizationName = "4. Organization Name (eg, company) "
0.organizationName_default = "$O"
organizationalUnitName = "5. Organizational Unit Name (eg, section) "
organizationalUnitName_default = "$OU"
commonName = "6. Common Name (eg, CA name) "
commonName_max = 64
commonName_default = "$NAME"
emailAddress = "7. Email Address (eg, name@FQDN)"
emailAddress_max = 40
emailAddress_default = "$EMAIL"

[ ca ]
default_ca              = CA_own

[ CA_own ]
dir                     = ./..
#certs                   = \$dir
certs                   = .
new_certs_dir           = \$dir/ca.db.certs
database                = \$dir/ca.db.index
serial                  = \$dir/ca.db.serial
RANDFILE                = \$dir/ca.db.rand
certificate             = \$dir/${SIGN_CA}.crt
private_key             = \$dir/${SIGN_CA}.key
default_days            = $DAYS
default_crl_days        = $CRL_DAYS
default_md              = md5
preserve                = no
policy                  = policy_anything
unique_subject          = no

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ xpclient_ext ]
extendedKeyUsage        = 1.3.6.1.5.5.7.3.2
# for EAP/TLS (radius) - winxp client

[ xpserver_ext ]
extendedKeyUsage        = 1.3.6.1.5.5.7.3.1
# for EAP/TLS (radius) - radius server

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = $CLIENT_PURPOSES
nsCertType = $CLIENT_CERTTYPE
#do not allow client use own certificate for own CA centre
EOT

}


#------------------------------------------------------------------------
# generate server or user vertificate
#

function generate_cert()
{ 

	echo
	echo create certificate key: ${NAME}.key
	echo

	openssl genrsa -des3 -out ${NAME}.key $BITS

	if [ "$MODE" = "server_cert" ] ; then

		echo
		echo decrypt certificate key: ${CERT}
		echo

		openssl rsa -in ${NAME}.key -out ${NAME}.key.unsecure
	fi

	case $MODE in
	   client_cert )
		DAYS=$CLIENT_DAYS ;
		;;
	   server_cert )
		DAYS=$CLIENT_DAYS ;
		;;
	
	   * )
		DAYS=$SERVER_DAYS ;
		;;
	esac
				 
	#  create an client SSLeay config
	cat > $CA_CONFIG <<EOT
[ req ]
default_bits = $BITS
distinguished_name = req_DN

[ req_DN ]
countryName = "1. Country Name (2 letter code)"
countryName_default = "$C"
countryName_min = 2
countryName_max = 2
stateOrProvinceName = "2. State or Province Name (full name) "
stateOrProvinceName_default = "$ST"
localityName = "3. Locality Name (eg, city) "
localityName_default = "$L"
0.organizationName = "4. Organization Name (eg, company) "
0.organizationName_default = "$O"
organizationalUnitName = "5. Organizational Unit Name (eg, section) "
organizationalUnitName_default = "$OU"
commonName = "6. Common Name (eg, CA name) "
commonName_max = 64
commonName_default = "$NAME"
emailAddress = "7. Email Address (eg, name@FQDN)"
emailAddress_max = 40
emailAddress_default = "$EMAIL"

[ ca ]
default_ca              = CA_own

[ CA_own ]
dir                     = ./..
#certs                   = \$dir
certs                   = .
new_certs_dir           = \$dir/ca.db.certs
database                = \$dir/ca.db.index
serial                  = \$dir/ca.db.serial
RANDFILE                = \$dir/ca.db.rand
certificate             = \$dir/${ROOTCA}.crt
private_key             = \$dir/${ROOTCA}.key
default_days            = $DAYS
default_crl_days        = $CRL_DAYS
default_md              = md5
preserve                = no
policy                  = policy_anything
unique_subject          = no

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ xpclient_ext ]
extendedKeyUsage        = 1.3.6.1.5.5.7.3.2
# for EAP/TLS (radius) - winxp client

[ xpserver_ext ]
extendedKeyUsage        = 1.3.6.1.5.5.7.3.1
# for EAP/TLS (radius) - radius server

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = $CLIENT_PURPOSES
nsCertType = $CLIENT_CERTTYPE
#do not allow client use own certificate for own CA centre
EOT


	echo
	echo create certificate request: ${NAME}.csr
	echo

	case $MODE in
	   client_cert )
		openssl req -new -key ${NAME}.key -config $CA_CONFIG -extensions v3_req -out ${NAME}.csr ;
		;;
	   server_cert )
		openssl req -new -key ${NAME}.key -config $CA_CONFIG -out ${NAME}.csr ;
		;;
	esac


	if [ $? -ne 0 ]; then
	    echo "Failed to make a certificate req due to error: $?" ;
	    exit $? ;
	fi


	echo	
	echo sign certificate by CA: ${CERT}
	echo \(sign ca is: $SIGN_CA\)
	echo 

	#   create an own SSLeay config
	create_ca_config

	#  sign the certificate
	echo "CA signing: $NAME.csr -> $CERT:"

	case $MODE in
	   client_cert ) 
		openssl ca -config $CA_CONFIG -batch -out $CERT -extensions xpclient_ext -infiles ${NAME}.csr
		;;
	   server_cert ) 
		openssl ca -config $CA_CONFIG -batch -out $CERT -extensions xpserver_ext -infiles ${NAME}.csr 
		#openssl ca -config $CA_CONFIG -batch -out $CERT -infiles ${NAME}.csr 
		;;
	esac

	if [ $? -ne 0 ]; then
	    echo "Failed to sign a certificate due to error: $?" ;
	    exit $? ;
	fi

	if [ "$MODE" == "client_cert" ] ; then
	
	    echo	
		echo convertign ${NAME} certificate to pkcs12 format
		echo
    
    	openssl pkcs12 -export -in ${NAME}.crt -inkey ${NAME}.key -certfile ../${ROOTCA}.crt -out ${NAME}.p12 
	    #create p12 format of certificate
	fi

	#openssl ca -config ca.config -out $CERT -infiles ${NAME}.csr
	echo "CA verifying: $CERT <-> CA cert"
	#echo openssl verify -CAfile ${ROOTCA}.crt $CERT

	openssl verify -CAfile ../${ROOTCA}.crt $CERT
}
#------------------------------------------------------------------------
# end of cert_gen()

function regen_crl()
{
    echo
    echo regen crl
    echo
    
	openssl ca -config $CA_CONFIG -gencrl -out ../${ROOTCA}.crl
}


function revoke_cert()
{
    echo
    echo revoke certificate $CERT
    echo

	openssl ca -config $CA_CONFIG -revoke $CERT

	regen_crl
}

if [ "$MODE" == "client_cert" ] || [ "$MODE" == "server_cert" ] ; then
	generate_cert
fi

if [ "$MODE" == "revoke_cert" ] ; then
    create_ca_config
    revoke_cert
fi

if [ "$MODE" == "regen_crl" ] ; then
    create_ca_config
    regen_crl
fi


#  cleanup after SSLeay 
rm -f ca.config
rm -f ${NAME}.csr
rm -f ../ca.db.serial.old
rm -f ../ca.db.index.old
cd ..

#  die gracefully
exit 0
