#! /bin/sh # # pkcs7 sign, verify (and optionally extract), or extract only # # cms.sh [config_options] [-q] -s|-v|-e|-i|-I infile [outfile|-] # # -q quiet, no verbose output # -s sign, signed data is {infile_basename}.cms in current directory unless otherwise specified by outfile # -v verify, no data extracted unless otherwise specified by outfile # -e extract data to stdout unless otherwise specified by outfile # -i info, display signature date and signer certificate issuer and subject # -I display the entire pkcs7 structure # # config_options: # -CAfile cafile # used for signing and verifying, default = demoCA/cacert.pem # -signer signer_cert # used for signing, default = testcert.pem # -inkey signer_key # used for signing, default = testkey.pem # # temporary $sig file is created in current directory and removed at end # # signed data is signature concatenated with data: cat "$sig" "$data" # signature is created using: openssl cms -sign -binary # signature is verified using: openssl cms -verify -binary # signature is extracted using: openssl cms -cmsout # data is extracted using: tail -c "+$((siglen+1))" # # R. Perry, 23 May 2025 # default config CAfile="demoCA/cacert.pem" signer="testcert.pem" inkey="testkey.pem" # config and -q options quiet="" while [ "$#" -gt 0 ]; do case "$1" in -CAfile) CAfile="$2"; shift 2;; -signer) signer="$2"; shift 2;; -inkey) inkey="$2"; shift 2;; -q) quiet="1"; shift 1;; *) break;; esac done # help usage() { echo "usage: $0 [config_options] [-q] -s|-v|-e|-i|-I infile [outfile|-] [-CAfile cafile] [-signer signer_cert] [-inkey signer_key]" 1>&2; exit 1 } # infile arg required infile="$2"; outfile="$3"; if [ -z "$infile" ]; then usage; fi # check command status, exit on error check() { status="$1"; msg="$2" if [ 0 != "$status" ]; then echo "$0: error $msg" 1>&2; exit "$status"; fi } # pkcs7 command pkcs7="openssl cms -cmsout -inform DER -outform DER" # get signature and length getsig() { [ -z "$quiet" ] && echo "Extracting $sig" 1>&2 $pkcs7 < "$cms" > "$sig" check "$?" "extracting $sig" siglen=$(wc -c < "$sig") check "$?" "getting $sig length" } # set file variables case "$1" in -s) data="$infile"; base=$(basename "$infile"); sig="$base.sig"; cms="$base.cms";; -v|-e) cms="$infile"; base=$(basename "$infile" .cms); sig="$base.sig"; getsig;; -i) $pkcs7 -noout -print < "$infile" | egrep 'subject:|issuer:|UTCTIME:' | sed 's/^ *//' | sort -u; exit;; -I) $pkcs7 -noout -print < "$infile"; exit;; *) usage;; esac # commands to verify signature and extract data verify="openssl cms -verify -binary -in '$sig' -inform DER -CAfile '$CAfile'" extract="tail -c '+$((siglen+1))' '$cms'" # sign, verify, or extract case "$1" in -s) [ -z "$quiet" ] && echo "Signing $data -> $sig" 1>&2 openssl cms -sign -binary -nosmimecap -in "$data" -out "$sig" -inkey "$inkey" -signer "$signer" -outform DER check "$?" "signing" # [ -z "$quiet" ] && echo "Checking $sig" 1>&2 $pkcs7 < "$sig" | cmp - "$sig" check "$?" "checking $sig" # cmd="$verify -content '$data' > /dev/null" [ -n "$quiet" ] && cmd="$cmd 2>/dev/null" [ -z "$quiet" ] && echo "Verifying signature" 1>&2 eval "$cmd" check "$?" "verifying" # cmd="cat '$sig' '$data'" if [ "X$outfile" = "X-" ]; then cms="stdout"; else if [ -n "$outfile" ]; then cms="$outfile"; fi cmd="$cmd > '$cms'" fi [ -z "$quiet" ] && echo "Writing cms to $cms" 1>&2 eval "$cmd" check "$?" "writing $cms" ;; -v) cmd="$extract | $verify -content /dev/stdin"; outmsg="" [ -n "$quiet" ] && cmd="$cmd 2>/dev/null" if [ -z "$outfile" ]; then cmd="$cmd > /dev/null"; else if [ "X$outfile" = "X-" ]; then outfile="stdout"; else cmd="$cmd > '$outfile'"; fi outmsg=" and writing data to $outfile" fi [ -z "$quiet" ] && echo "Verifying signature$outmsg" 1>&2 eval "$cmd" check "$?" "verifying" ;; -e) cmd="$extract" if [ -n "$outfile" -a "X$outfile" != "X-" ]; then cmd="$cmd > '$outfile'"; else outfile="stdout"; fi [ -z "$quiet" ] && echo "Writing data to $outfile" 1>&2 [ -z "$quiet" ] && echo "WARNING: not checking signature" 1>&2 eval "$cmd" check "$?" "extracting data" ;; esac # cleanup [ -z "$quiet" ] && echo "Removing $sig" 1>&2 rm -f "$sig"