A script that will use multiple, publicly available RFC 3161-compatible services for timestamping files.
Requires:
openssl
openssl
curl
curl
Optional: Offline verification
For the optional offline timestamp verification, a directory structure containing the CA root certificates is required. Example:
~/certificates/
βββ digicert
β βββ DigiCertAssuredIDRootCA_comb.crt.pem
βββ freeTSA
β βββ cacert.pem
βββ globalsign
βββ root-r6.pem
For this example, the certificates can be downloaded from:
These timestamping services are configured near the top of the script β please modify where needed. If your chosen service does not offer PEM-format certificates, use this to convert:
openssl x509 -inform DER -outform PEM -in certificate.crt -out certificate.pem
Calling the script
What the script will do:
- Create timestamping queries (
.tsq
), one for each file to be time stamped (skipped if the respective query already exists) - Request and retrieve time stamp responses (
.tsr
) from all configured time stamping services, (skipped if the respective response has already been retrieved) - Optional: quick or extended offline verification of the time stamps of each time-stamped file. If needed, tries to extract the certificate chain from the time stamping response (
.tsr
). The quick verification is performed against the file hash value stored in the query file (.tsq
); the extended verification is performed against the hash value re-calculated by reading the complete file, again (the.tsq
query file will not be overwritten).
Typical calls:
timestamp.sh -e -c ~/certificates file.pdf
timestamp.sh -e -c ~/certificates -l filepaths
#!/bin/bash certificates_basedir=~/certificates file_list= verify_timestamps= extended_verify_timestamps= error=0 function usage() { echo "Usage:" echo "$(basename $0) [-e] [-v] [-c certificatesBasedir] [-l listOfFiles] [fileToBeTimestamped]" echo echo "Options:" echo " -c certificatesBasedir, base directory for CA certificates, one subdir per TSA" echo " -e, extended verification of timestamps, recalculates hash of source file (implies -v)" echo " -l listOfFiles, list of files to be timestamped, one file path per line" echo " -v, quick verification of timestamps, using only hash values from tsr" echo " [fileToBeTimestamped], single file to be timestamped (ignored if -l is given)" echo echo "Examples:" echo "$(basename $0) data.bin" echo "$(basename $0) -l ./tmp/filelist.txt" echo "$(basename $0) -e -l ./tmp/filelist.txt -c ~/certificates" } while getopts ":evc:l:" opt; do case $opt in c) certificates_basedir="$OPTARG" ;; e) extended_verify_timestamps=1 verify_timestamps=1 ;; l) file_list="$OPTARG" ;; v) verify_timestamps=1 ;; \?) echo "Invalid option -$OPTARG" >&2 usage exit 1 ;; esac done shift $((OPTIND-1)) # see https://github.com/Manouchehri/rfc3161-servers/blob/master/server_list.txt declare -A TSA declare -A CAFILE # Commercial, provides full certificate chain file including root certificate TSA[digicert]="http://timestamp.digicert.com" CAFILE[digicert]="DigiCertAssuredIDRootCA_comb.crt.pem" # Non-commercial, uses self-signed root certificate TSA[freeTSA]="https://freetsa.org/tsr" CAFILE[freeTSA]="cacert.pem" # Commercial, provides root-only certificate, chain must be retrieved from timestamp response TSA[globalsign]="http://aatl-timestamp.globalsign.com/tsa/aohfewat2389535fnasgnlg5m23" CAFILE[globalsign]="root-r6.pem" function timestamp_file() { if [ -f "$1" ]; then verify=$2 extended_verify=$3 current_file_path="$1" echo echo Processing "$current_file_path" ... generic_query_file="$current_file_path.tsq" if [ ! -f "$generic_query_file" ]; then echo Creating generic time stamp query file ... openssl ts -query -data "$current_file_path" -cert -no_nonce -sha512 -out "$generic_query_file" else echo Request file exists, skipping query generation. fi echo Checking time stamps ... echo for current_tsa in "${!TSA[@]}"; do current_tsa_response_file="$current_file_path.$current_tsa.tsr" CAFile="$certificates_basedir/$current_tsa/${CAFILE[$current_tsa]}" if [ ! -f "$current_tsa_response_file" ]; then echo echo "TSA: $current_tsa" url="${TSA[$current_tsa]}" echo "URL: $url" echo "CAfile: $CAFile" curl --http2 -s -H "Content-Type: application/timestamp-query" --data-binary @"$generic_query_file" "$url" > "$current_tsa_response_file" echo Time stamp issued: openssl ts -reply -in "$current_tsa_response_file" -text else echo $current_tsa response file exists, skipping request. fi if [ $verify ]; then try_chain=0 openssl ts -verify -x509_strict -in "$current_tsa_response_file" -queryfile "$generic_query_file" -CAfile "$CAFile" >/dev/null 2>&1 EC=$? if [ $EC -ne 0 ]; then try_chain=1 openssl ts -reply -in "$current_tsa_response_file" -token_out | openssl pkcs7 -inform der -print_certs | sed -n '/-----BEGIN/,/-----END/p' > "$certificates_basedir/$current_tsa/chain.pem" openssl ts -verify -x509_strict -in "$current_tsa_response_file" -queryfile "$generic_query_file" -CAfile "$CAFile" -untrusted "$certificates_basedir/$current_tsa/chain.pem" >/dev/null 2>&1 EC=$? if [ $EC -ne 0 ]; then error=1 echo ERROR: Quick verification against request failed, exit code $EC for file "$current_file_path" else echo OK: Quick verification against request fi else echo OK: Quick verification against request fi if [ $extended_verify ]; then if [ $try_chain -ne 0 ]; then openssl ts -verify -x509_strict -data "$current_file_path" -in "$current_tsa_response_file" -CAfile "$CAFile" -untrusted "$certificates_basedir/$current_tsa/chain.pem" >/dev/null 2>&1 else openssl ts -verify -x509_strict -data "$current_file_path" -in "$current_tsa_response_file" -CAfile "$CAFile" >/dev/null 2>&1 fi EC=$? if [ $EC -ne 0 ]; then error= echo ERROR: Full verification against recalculated file hash failed, exit code $EC for file "$current_file_path" else echo OK: Full verification against recalculated file hash fi fi echo fi done else echo Cannot find $1 - skipping. fi } if [ -f "$file_list" ] then while read current_file_path ; do timestamp_file "$current_file_path" $verify_timestamps $extended_verify_timestamps done < "$file_list" else if [ -f "$1" ]; then timestamp_file "$1" $verify_timestamps $extended_verify_timestamps else error= echo No file given. fi fi exit $error
Image Credits:
Papirus icon for Terminal (modified) | GNU General Public License, version 3
Licensing:
This content is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
For your attributions to us please use the word Β»tuxwiseΒ«, and the link https://tuxwise.net.