Files
GmSSL/apps/gmca/gmca
Zhi Guan 187d2d6c2f Add gmca
gmca is a command line tool to make CA maintaining easier.
2018-03-13 14:20:39 +08:00

499 lines
11 KiB
Bash
Executable File

#!/bin/bash
#
# Copyright (c) 2018 The GmSSL Project. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. All advertising materials mentioning features or use of this
# software must display the following acknowledgment:
# "This product includes software developed by the GmSSL Project.
# (http://gmssl.org/)"
#
# 4. The name "GmSSL Project" must not be used to endorse or promote
# products derived from this software without prior written
# permission. For written permission, please contact
# guanzhi1980@gmail.com.
#
# 5. Products derived from this software may not be called "GmSSL"
# nor may "GmSSL" appear in their names without prior written
# permission of the GmSSL Project.
#
# 6. Redistributions of any form whatsoever must retain the following
# acknowledgment:
# "This product includes software developed by the GmSSL Project
# (http://gmssl.org/)"
#
# THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE GmSSL PROJECT OR
# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# OF THE POSSIBILITY OF SUCH DAMAGE.
curve=sm2p256v1
md=sm3
function set_std {
curve=secp256k1
md=sha256
ca_engine=""
user_engine=""
}
# CA ajustable parameters
ca_dn_prefix="/C=CN/ST=BJ/L=BJ/O=PKU"
ca_rootcert_days=3650
ca_usercert_days=365
#ca_engine="skf"
#ca_engine="sdf"
#user_engine="skf"
#user_engine="sdf"
user_dn_prefix="$ca_dn_prefix/OU=Sign"
user_dn_enc_prefix="$ca_dn_prefix/OU=Enc"
# The following values should not be changed.
ca_dn="$ca_dn_prefix/OU=CA/CN=PKUCA"
ca_dir=".ca"
ca_rootcert_file="$ca_dir/cacert.pem"
ca_private_dir="$ca_dir/private"
ca_rootcertkey_file="$ca_private_dir/cakey.pem"
ca_rand_file="$ca_private_dir/.rand"
ca_serial_file="$ca_dir/serial"
ca_csr_dir="$ca_dir/csr"
ca_signed_csr_dir="$ca_csr_dir/signed"
ca_rejected_csr_dir="$ca_csr_dir/rejected"
ca_cert_dir="$ca_dir/certs"
ca_newcert_dir="$ca_dir/newcerts"
ca_index_file="$ca_dir/index.txt"
ca_crl_dir="$ca_dir/crl"
ca_crl_file="$ca_crl_dir/crl.pem"
ca_crl_number_file="$ca_dir/crlnumber"
ca_crl_days=60
user_key_dir="$ca_dir/keys"
function setup {
if [[ -n $1 ]]; then
ca_dn=$1
fi
rm -fr $ca_dir
mkdir $ca_dir
mkdir $ca_private_dir
mkdir $ca_crl_dir
mkdir $ca_csr_dir
mkdir $ca_signed_csr_dir
mkdir $ca_rejected_csr_dir
mkdir $ca_cert_dir
mkdir $ca_newcert_dir
mkdir $user_key_dir
touch $ca_index_file
touch $ca_rand_file
echo 01 > $ca_serial_file
echo 01 > $ca_crl_number_file
case $ca_engine in
"")
gmssl ecparam -genkey -name $curve -noout -out $ca_rootcertkey_file
key="-key $ca_rootcertkey_file"
;;
-skf)
key="-engine skf -keyform engine -key sm2.sign"
;;
-sdf)
# admin has to use sdf management tools to manually init device
key="-engine sdf -keyform engine -key ecc_1.sign"
;;
*)
echo "usage: gmca -setup <DN>"
exit
;;
esac
gmssl req -new -x509 -subj=$ca_dn -days $ca_rootcert_days -$md $key -out $ca_rootcert_file
gmssl x509 -text -noout -in $ca_rootcert_file
}
function showstatus {
echo "Valid certificates : "
echo "Valid Distinct Subjects : "
echo "In Validity but Revoked Certificaes : "
echo "Certificates near Expiration : "
echo "Certificates recently expired : "
echo "Overall Certificate Count : "
echo "Overall Distinct Subjects : "
echo "Overall Revoked Certificates : "
echo "Expired Certificates : "
}
function showcacert {
gmssl x509 -text -noout -in $ca_rootcert_file
}
function getcacert {
if [[ -n "$1" ]]; then
out="-out $1"
fi
gmssl x509 -in $ca_rootcert_file $out
}
#FIXME: if common_name already exist in csr_dir or in certs_dir?
function gencsr {
local common_name=$1
if [[ -z $common_name ]]; then
echo "No Common Name given"
exit
fi
local subject="$user_dn_prefix/CN=$common_name"
local csrfile="$ca_csr_dir/$common_name.csr"
case $user_engine in
"")
local keyfile="$user_key_dir/$common_name.key"
gmssl ecparam -genkey -name $curve -out "$keyfile"
local key="-key $keyfile"
;;
skf)
;;
sdf)
;;
*)
;;
esac
gmssl req -new -$md -subj="$subject" $key -out "$csrfile"
gmssl req -text -in "$csrfile"
}
function listcsrs {
for csrfile in $ca_csr_dir/*.csr; do
if [[ -f $csrfile ]]; then
basename $csrfile .csr
fi
done
}
function showcsr {
if [[ -z "$1" ]]; then
echo "usage: gmca -showcsr <common-name>"
exit
fi
local common_name=$1
local csrfile="$ca_csr_dir/$common_name.csr"
#FIXME: check if $csrfile exist
gmssl req -text -noout -in "$csrfile"
}
function getcsr {
if [[ -z "$1" ]]; then
echo "usage: gmca -getcsr <common-name>"
exit
fi
local common_name=$1
local csrfile="$ca_csr_dir/$common_name.csr"
#FIXME: check if $csrfile exist
gmssl req -in "$csrfile"
}
function signcsr {
if [[ -z "$1" ]]; then
echo "usage: gmca -signcsr <common-name>"
exit
fi
common_name=$1
csrfile="$ca_csr_dir/$common_name.csr"
#FIXME: check if $csrfile exist
#FIXME: use -subj to change csrname
subject="$user_dn_prefix/CN=$common_name"
gmssl ca -config ./signcsr.cnf -batch -subj=$subject -md $md -days $ca_usercert_days -outdir $ca_cert_dir -infiles "$csrfile"
}
function signenccsr {
common_name=$1
csrfile="$ca_csr_dir/$common_name.csr"
subject="$user_dn_enc_prefix/CN=$common_name"
gmssl ca -config ./signenccsr.cnf -batch -subj=$subject -md $md -days 365 -outdir $ca_usercert_dir -infiles "$csrfile"
}
function gencert {
common_name=$1
gencsr $common_name
signcsr $common_name
}
#FIXME: how to handle double certs?
function genenccert {
common_name=$1
gencsr $common_name
signenccsr $common_name
}
function rejectcsr {
#FIXME: check argument exist
common_name=$1
csrfile="$ca_csr_dir/$common_name.csr"
#FIXME: check if $csrfile exist
mv $csrfile $ca_rejected_csr_dir/
}
function listcerts {
# the ca txt_db is a tab seperated text file
# 1. Certificate validity, "V" for valid, "R" for revoked or E for expired
# 2. Expiration datetime,
# 3. Revokation datetime, will be null if not revoked
# 4. Serial number
# 5. File name of the certificate, always be "unknown"
# 6. Certificate subject name, in oneline format
awk -F'\t' '{print $2,$4,$6}' $ca_index_file
}
function listcertsbyname {
#FIXME: check argument exist
awk -F'\t' '{print $2,$4,$6}' $ca_index_file | grep $1
}
function getcertbyserial {
#FIXME: check argument exist
local serial=$1
local cerfile=$ca_cert_dir/$serial.pem
gmssl x509 -in $certfile
}
function getcertbyname {
local common_name=$1
#FIXME: need better method than just grep
serial=`awk -F'\t' '{print $2,$4,$6}' $ca_index_file | grep $common_name | awk '{print $2}'`
getcertbyserial $serial
}
function listrevokereasons {
echo " unspecified"
echo " keyCompromise"
echo " CACompromise"
echo " affiliationChanged"
echo " superseded"
echo " cessationOfOperation"
echo " certificateHold"
echo " removeFromCRL (should not be used)"
}
function _revokecertfile {
certfile=$1
if [[ -z "$certfile" ]]; then
echo "Usage: $0 <certfile>"
echo ""
exit -1
fi
time=`date +"%Y%m%d%H%M%S"`Z
#reason=unspecified
reason="keyCompromise -crl_compromise $time"
#reason="CACompromise -crl_CA_compromise $time"
#reason=affiliationChanged
#reason=superseded,
#reason=cessationOfOperation
#reason=certificateHold
#reason=removeFromCRL.
#gmssl ca -config ./ca.cnf -valid $certfile
gmssl ca -config ./ca.cnf -revoke $certfile -crl_reason $reason
#gmssl ca -config ./ca.cnf -valid $certfile
}
function revokecertbyserial {
serial=$1
_revokecertfile "$ca_cert_dir/$serial.pem"
}
function gencrl {
time=`date +"%Y%m%d%H%M%S"`
crlfile="$ca_crl_dir/$time.crl"
gmssl ca -config ./ca.cnf -gencrl -md $md -crldays $ca_crl_days -out $crlfile
cp $crlfile $ca_crl_file
gmssl crl -text -noout -in $ca_crl_file -CAfile $ca_rootcert_file
}
function showcrl {
# when use `-CAfile` option, verification result will be printed
gmssl crl -text -noout -in $ca_crl_file #-CAfile $ca_rootcert_file
}
function getcrl {
gmssl crl -in $ca_crl_file #-CAfile $ca_rootcert_file
}
function backup {
time=`date +"%Y%m%d%H%M%S"`
tar cvzf ca-$time.tar.gz $ca_dir
}
POSITIONAL=()
while [[ $# -gt 0 ]]
do
opt="$1"
#FIXME: show help info, get more options
case $opt in
-verbose)
verbose=yes
shift
shift
;;
-setup)
dn="$2"
setup $dn
shift
shift
;;
-showcacert)
showcacert
shift
;;
-getcacert)
file="$2"
getcacert $file
shift
shift
;;
-showstatus)
showstatus
shift
;;
-gencsr)
common_name="$2"
gencsr "$common_name"
shift
shift
;;
-listcsrs)
listcsrs
shift
;;
-showcsr)
common_name="$2"
showcsr "$common_name"
shift
shift
;;
-getcsr)
common_name="$2"
getcsr "$common_name"
shift
shift
;;
-signcsr)
common_name="$2"
signcsr "$common_name"
shift
shift
;;
-rejectcsr)
common_name="$2"
rejectcsr "$common_name"
shift
shift
;;
-gencert)
common_name="$2"
gencert $common_name
shift
shift
;;
-listcerts)
listcerts
shift
;;
-listcertsbyname)
name="$2"
listcertsbyname $name
shift
shift
;;
-getcertbyserial)
serial="$2"
getcertbyserial $serial
shift
shift
;;
-getcertbyname)
name="$2"
getcertbyname $name
shift
shift
;;
-showcert)
cert="$2"
showcert "$cert"
shift
shift
;;
-revokereasons)
revokereasons
shift
;;
-revokecert)
certfile="$2"
revokebycert "$certfile"
shift
shift
;;
-revokecertbyserial)
serial="$2"
revokecertbyserial $serial
shift
shift
;;
-gencrl)
gencrl
shift
;;
-crl)
getcrl
shift
;;
-showcrl)
showcrl
shift
;;
-getcrl)
getcrl
shift
;;
-backup)
backup
shift
;;
*)
echo "usage: ...."
POSITIONAL+=("$1")
shift
;;
esac
done
set -- "${POSITIONAL[@]}"
if [[ -n $1 ]]; then
echo "Last line of file specified as non-opt/last argument:"
fi