Skip to content

Instantly share code, notes, and snippets.

@Manouchehri
Last active December 9, 2024 13:18
Show Gist options
  • Save Manouchehri/fd754e402d98430243455713efada710 to your computer and use it in GitHub Desktop.
Save Manouchehri/fd754e402d98430243455713efada710 to your computer and use it in GitHub Desktop.
List of free rfc3161 servers.
https://rfc3161.ai.moda
https://rfc3161.ai.moda/adobe
https://rfc3161.ai.moda/microsoft
https://rfc3161.ai.moda/apple
https://rfc3161.ai.moda/any
http://rfc3161.ai.moda
http://timestamp.digicert.com
http://timestamp.globalsign.com/tsa/r6advanced1
http://rfc3161timestamp.globalsign.com/advanced
http://timestamp.sectigo.com
http://timestamp.apple.com/ts01
http://tsa.mesign.com
http://time.certum.pl
https://freetsa.org
http://tsa.startssl.com/rfc3161
http://dse200.ncipher.com/TSS/HttpTspServer
http://zeitstempel.dfn.de
https://ca.signfiles.com/tsa/get.aspx
http://services.globaltrustfinder.com/adss/tsa
https://tsp.iaik.tugraz.at/tsp/TspRequest
http://timestamp.entrust.net/TSS/RFC3161sha2TS
http://timestamp.acs.microsoft.com
@danvy
Copy link

danvy commented May 30, 2024

My http://rfc3161.ai.moda load balancer should work fine over HTTP. I didn’t add it to the list because I want to encourage everyone to use HTTPS, but it works fine if you must use HTTP.

@Manouchehri Could you explain what's behind this service ?

@Manouchehri
Copy link
Author

Could you explain what's behind this service ?

@danvy It's a load balancer that:

  1. Response validation of the timestamp reply before returning it to you.
  2. Automatic retrying. e.g. if one of the upstream servers returns an invalid timestamp reply, we automatically return the next valid response from the next server.
  3. Fans out to multiple trusted timestamping servers in parallel. The two steps above happen in multiple threads, so you will always get the fastest response possible, even if the first upstream CA returns us an error (you won't see the error, we handle that).
  4. Allow CORS requests.
  5. We update the upstream CAs in our list server-side. i.e. You should never need to update your RFC3161 URL in your application if you use any of the https://rfc3161.ai.moda/[*] URLs. e.g. today I noticed that IDnomic/Keynectis took their server down, but we already had 7 fallbacks for https://rfc3161.ai.moda/adobe and 8 fallbacks for https://rfc3161.ai.moda/windows, so it resulted in zero downtime for anyone.

Out of 1.33 million requests this month, we've had 60 errors. So roughly a 99.995% success rate.

@itdevwu
Copy link

itdevwu commented Jul 7, 2024

Hi, @Manouchehri. I wonder if there is any document discribing diffrences between each suffix of https://rfc3161.ai.moda/[*]?

@rschultheis
Copy link

Hello users of Trusted Timestamps. I would like to add my own site to the list: https://timestampit.com/. TimeStampIt! is a TSA and it is only a TSA. We do not however offer RFC3161 timestamps, but a new design based on plain text encoded trusted timestamps. I built it because a) RFC3161 timestamps are not easy to work with, and b) there really was not a good dedicated TSA that felt like I could build a commercial application on top of.

It is brand new and experimental. I would love any feedback you might have on it! info at timestampit dot com 💚. If there is enough demand I could even add support for RFC3161 timestamps. But honestly I would rather keep innovating in other directions.

@Manouchehri
Copy link
Author

@rschultheis RFC3161 is super easy to work with, lots of programs support it out of the box. I've been using it for years on all of my important PDFs, even those non-technical can see the timestamps easily.

@JohnPlanetary
Copy link

JohnPlanetary commented Aug 12, 2024

timestampit without RFC 3161 and RFC 5816 support doesn't make a lot of sense here in these topic.
In fact I would argue that since RFC 3161 and RFC 5816 are the standards and are supported by so many programs that is still relevant, the magic part should be the part to be able to verify in the future that the file was really timestamped 23 years ago... I've been testing TSA's for a long time and basically is almost impossible once the TSA expires to proof anything since it will start to give errors... since CRL and or OCSP usually stop working and stops being possible to verify anything.

timestampit could have say a different key for every year using RSA 8192 bit SHA512 key to timestamp using RFC 3161 and RFC 5816, and say 150 years of year key lifetime, the main Root with say 500 years lifetime, RSA 16384 bit SHA512. and every year a new key with 150 years of key lifetime.

TimeStampit Root Key (RSA 16384 bit SHA512) [From: 2024-08-12 - Until: 2524-08-12]

TimeStampit 2024 Key (RSA 8192 bit SHA512) [From: 2024-08-13 -> Until: 2174-12-31] [signs between 2024-08-13 00:00:00 until 2024-12-31 23:59:59]
TimeStampit 2025 Key (RSA 8192 bit SHA512) [From: 2025-01-01 -> Until: 2175-12-31] [signs between 2025-01-01 00:00:00 until 2025-12-31 23:59:59]
TimeStampit 2026 Key (RSA 8192 bit SHA512) [From: 2026-01-01 -> Until: 2176-12-31] [signs between 2026-01-01 00:00:00 until 2026-12-31 23:59:59]
TimeStampit 2026 Key (RSA 8192 bit SHA512) [From: 2026-01-01 -> Until: 2176-12-31] [signs between 2026-01-01 00:00:00 until 2026-12-31 23:59:59]

If the CRL('s) can be included within the signature (still doesn't happen in most programs) then theoretically the timestamp could be believed until its expiration date... as long the company itself doesn't have any troubles coming up like the private key being stolen or cracked.

RSA 16834 bit key with SHA512 should be fine for the Root, but usually can't be used for the final signing key because it will give too many errors at least in my tests it would fail on most signing programs because would take too many memory/ space reserved for the signature.

NIST P-521 could theoretically be used but the problem is that in reality the security level of it should be lower than even RSA 3072 bit key at least if quantum computing is used to try to find the private key.

Ok... but what if TimeStampit doesn't want to support RFC 3161 and RFC 5816?
In what it would be different from other services online like: https://truetimestamp.org , https://opentimestamps.org , https://tzstamp.io , https://timestamp.decred.org and https://notbot.me ? What happens if TimeStampit closes in 6 years time, how would people be able to prove to others that timestamp is real? Some of the services above include some sort of bitcoin or other coin integration to make it more easy to proof in the future (as long those continue to exist)... in some cases judges may not accept them since it wouldn't be easy to verify by themselves and they may or may not believe experts, if it seems magic mumbojumbo... and to my knowledge there is no standard log database for timestamp similar to Certificate Transparency (where people can search for the certificate from multiple sources using for example: https://crt.sh ) where I think the certificate companies need to publish to more than one log database (at least one being from others not related to that company) in order for others to trust it and be able to verify things after the fact.

@rschultheis
Copy link

Hi @JohnPlanetary , thank you for this feedback.

I don't want to take over this excellent gist about RFC3161 TSAs (thank you @Manouchehri ) with a discussion of TimestampIt!, therefore I've started a new gist to respond: https://gist.github.com/rschultheis/ea3b17017f520b4b3dcca270fc8dd1b6.

I'd love to keep the discussion going over there, but to quickly respond to one point:

What happens if TimeStampit closes in 6 years time, how would people be able to prove to others that timestamp is real?

This is what the TimestampIt! verification key replica repos are for: https://github.com/timestampit/keychain/. More info in my new gist 💚

@vasekkral
Copy link

Hi, @Manouchehri. I wonder if there is any document discribing diffrences between each suffix of https://rfc3161.ai.moda/[*]?

That would be really great as I am wondering what are the differences too!

@vasekkral
Copy link

The https://rfc3161.ai.moda/[*] load balancer sounds really great. Unfortunately we are not able to use it as our custom (time)stamping service needs to have list of all used TSA CAs root certificates to consider them trusted.

Would it be possible to have a list with links to root certificates of all active CAs used for https://rfc3161.ai.moda/[*] so we could download them?

@zablockil
Copy link

zablockil commented Aug 16, 2024

maybe it will be useful to someone:

#!/bin/bash
#
# tests TSA servers
#
# copyright: public domain / MIT
#
# 1. creates a random hash and nonce
# 2. creates a .tsq file and sends it to the server
# 3. collects certificates from response and saves to .p7b file
# 4. saves validity of certificates from the chain
# 5. saves http address from which any missing certificates can be downloaded
#
# RUN:
# ./tsa_batch.sh
#

hashedMessage_rand () {
	echo "$(openssl rand -hex 64)" | cut -c1-64
}
# md5     cut -c1-32
# sha1    cut -c1-40
# sha224  cut -c1-56
# sha256  cut -c1-64
# sha384  cut -c1-96
# sha512  cut -c1-128

#hashedMessage="00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
# change:
# parameter.1 = FORMAT:HEX,OCTETSTRING:$(hashedMessage_rand)
# to:
# parameter.1 = FORMAT:HEX,OCTETSTRING:${hashedMessage}


# sha1/sha256/sha384/sha512
algorithmIdentifier="sha256"

# ~128 bits entropy
nonce_32 () {
	echo "$(shuf -i 1-7 -n 1)$(openssl rand -hex 20)" | cut -c1-32
}

asn1parse_timestamp_request () {
cat <<-EOF

asn1 = SEQUENCE:TimeStampReq

[ TimeStampReq ]

parameter.0 = INTEGER:1
parameter.1 = SEQUENCE:messageImprint
#parameter.2 = OID:1.2.3.4
parameter.3 = INTEGER:0x$(nonce_32)
parameter.4 = BOOLEAN:TRUE

	[ messageImprint ]

	parameter.0 = SEQUENCE:hashAlgorithm
	parameter.1 = FORMAT:HEX,OCTETSTRING:$(hashedMessage_rand)

		[ hashAlgorithm ]

		parameter.0 = OID:${algorithmIdentifier}
		parameter.1 = NULL

EOF
}

generate_tsr () {
  cat <(echo "$(asn1parse_timestamp_request)") | openssl asn1parse -genconf /dev/stdin -noout -out "${ts_name}_timestamp_query.tsq" && \
  openssl ts -query -config /dev/null -text -in "${ts_name}_timestamp_query.tsq" > "${ts_name}_timestamp_query.tsq.txt" && \
  curl ${tsr_server} -H 'Content-Type: application/timestamp-query' -s -S --data-binary "@${ts_name}_timestamp_query.tsq" -o "${ts_name}_timestamp_response.tsr" && \
  openssl ts -reply -config /dev/null -text -in "${ts_name}_timestamp_response.tsr" > "${ts_name}_timestamp_response.tsr.txt" && \
  openssl ts -reply -config /dev/null -token_out -in "${ts_name}_timestamp_response.tsr" | openssl pkcs7 -inform DER -print_certs -text | grep -C1 "Not After" > "${ts_name}_chain_validity.txt" && \
  openssl ts -reply -config /dev/null -token_out -in "${ts_name}_timestamp_response.tsr" | openssl pkcs7 -inform DER -print_certs | awk '/^-----BEGIN CERTIFICATE-----/{n++;s=1}s{print}/^-----END CERTIFICATE-----/{s=0}' | openssl crl2pkcs7 -inform PEM -outform DER -nocrl -certfile /dev/stdin -out "${ts_name}_chain.p7b" && \
  echo "====" >> "${ts_name}_chain_validity.txt" && \
  openssl ts -reply -config /dev/null -token_out -in "${ts_name}_timestamp_response.tsr" | openssl pkcs7 -inform DER -print_certs -text | grep -C0 "CA Issuers" >> "${ts_name}_chain_validity.txt"
  echo "DONE :: ${tsr_server}"
  echo "-------"
  #dumpasn1 -apz "${ts_name}_timestamp_response.tsr" | awk '{ sub(/[ \t]+$/, ""); print }' > "${ts_name}_timestamp_response.tsr.dumpasn1.txt"
}

# https://gist.github.com/Manouchehri/fd754e402d98430243455713efada710

ts_name="digicert"
tsr_server="http://timestamp.digicert.com"
generate_tsr

ts_name="globalsign"
tsr_server="http://aatl-timestamp.globalsign.com/tsa/aohfewat2389535fnasgnlg5m23"
generate_tsr

ts_name="sectigo"
tsr_server="https://timestamp.sectigo.com"
generate_tsr

ts_name="sectigo_2"
tsr_server="https://timestamp.sectigo.com/qualified"
generate_tsr

ts_name="entrust"
tsr_server="http://timestamp.entrust.net/TSS/RFC3161sha2TS"
generate_tsr

ts_name="swisssign"
tsr_server="http://tsa.swisssign.net"
generate_tsr

ts_name="quovadisglobal"
tsr_server="http://ts.quovadisglobal.com/ch"
generate_tsr

ts_name="quovadisglobal_2"
tsr_server="http://ts.quovadisglobal.com/eu"
generate_tsr

ts_name="ssl_com"
tsr_server="http://ts.ssl.com"
generate_tsr

ts_name="identrust"
tsr_server="http://timestamp.identrust.com"
generate_tsr

ts_name="belgium"
tsr_server="http://tsa.belgium.be/connect"
generate_tsr

ts_name="cartaodecidadao"
tsr_server="http://ts.cartaodecidadao.pt/tsa/server"
generate_tsr

ts_name="accv_es"
tsr_server="http://tss.accv.es:8318/tsa"
generate_tsr

ts_name="baltstamp"
tsr_server="http://tsa.baltstamp.lt"
generate_tsr

ts_name="aped_gr"
tsr_server="https://timestamp.aped.gov.gr/qtss"
generate_tsr

ts_name="sep_bg"
tsr_server="http://tsa.sep.bg"
generate_tsr

ts_name="izenpe"
tsr_server="http://tsa.izenpe.com"
generate_tsr

ts_name="certum"
tsr_server="http://time.certum.pl"
generate_tsr

ts_name="symantec"
tsr_server="http://sha256timestamp.ws.symantec.com/sha256/timestamp"
generate_tsr

ts_name="globalsign"
tsr_server="http://rfc3161timestamp.globalsign.com/advanced"
generate_tsr

ts_name="globalsign_2"
tsr_server="http://timestamp.globalsign.com/tsa/r6advanced1"
generate_tsr

ts_name="apple"
tsr_server="http://timestamp.apple.com/ts01"
generate_tsr

ts_name="trustwave"
tsr_server="http://timestamp.ssl.trustwave.com"
generate_tsr

ts_name="freetsa"
tsr_server="https://freetsa.org/tsr"
generate_tsr

ts_name="zeitstempel"
tsr_server="http://zeitstempel.dfn.de"
generate_tsr

ts_name="catcert_cat"
tsr_server="http://psis.catcert.cat/psis/catcert/tsp"
generate_tsr

ts_name="codegic"
tsr_server="http://pki.codegic.com/codegic-service/timestamp"
generate_tsr

#ts_name="mesign"
#tsr_server="https://tsa.mesign.com"
#generate_tsr

ts_name="wotrus"
tsr_server="https://tsa.wotrus.com"
generate_tsr

ts_name="lex_persona"
tsr_server="http://tsa.lex-persona.com/tsa"
generate_tsr

ts_name="cesnet"
tsr_server="https://tsa.cesnet.cz:5817/tsa"
generate_tsr

ts_name="cesnet_2"
tsr_server="https://tsa.cesnet.cz:3162/tsa"
generate_tsr

ts_name="signfiles"
tsr_server="http://ca.signfiles.com/TSAServer.aspx"
generate_tsr

#ts_name="signfiles_2"
#tsr_server="https://ca.signfiles.com/tsa/get.aspx"
#generate_tsr

ts_name="aloahacoin"
tsr_server="http://aloahacoin.chain-provider.com/tsa.aspx"
generate_tsr

ts_name="sinpe_cr"
tsr_server="http://tsa.sinpe.fi.cr/tsaHttp/"
generate_tsr

ts_name="mahidol_th"
tsr_server="https://tsa.mahidol.ac.th/tsa/get.aspx"
generate_tsr

ts_name="cra_ge"
tsr_server="http://tsa.cra.ge/signserver/tsa?workerName=qtsa"
generate_tsr

ts_name="gob_hn"
tsr_server="http://tss.cnbs.gob.hn/TSS/HttpTspServer"
generate_tsr

ts_name="mconnect"
tsr_server="https://time.mconnect.mc"
generate_tsr

ts_name="tugraz_at"
tsr_server="https://tsp.iaik.tugraz.at/tsp/TspRequest"
generate_tsr

#ts_name="safecreative"
#tsr_server="http://tsa.safecreative.org"
#generate_tsr

#ts_name="comodoca"
#tsr_server="http://timestamp.comodoca.com/rfc3161"
#generate_tsr

ts_name="nowina_lu"
tsr_server="http://dss.nowina.lu/pki-factory/tsa/good-tsa"
generate_tsr


#
# other
#


# Digidoc4_Client
#ts_name="sk_ee"
#tsr_server="http://tsa.sk.ee/"
#generate_tsr


# EOF

@hillac
Copy link

hillac commented Aug 20, 2024

If you choose one between the Adobe: European Union Trusted Lists, and Adobe Approved Trust List, which is more widely accepted? I'm assuming the EU has a higher level of trust and works everywhere?

@ErikKnowles
Copy link

Symantec's timestamp server appears to be defunct. See this.

@dallmair
Copy link

We switched to the Azure Code Signing timestamp server: http://timestamp.acs.microsoft.com

@Manouchehri
Copy link
Author

Would it be possible to have a list with links to root certificates of all active CAs used for https://rfc3161.ai.moda/[*] so we could download them?

I've exposed https://rfc3161.ai.moda/servers.json for now, does that work or still not quite?

@Manouchehri
Copy link
Author

We switched to the Azure Code Signing timestamp server: http://timestamp.acs.microsoft.com

Thanks, I've added that one for Windows signing on https://rfc3161.ai.moda!

@vasekkral
Copy link

vasekkral commented Sep 3, 2024

Would it be possible to have a list with links to root certificates of all active CAs used for https://rfc3161.ai.moda/[*] so we could download them?

I've exposed https://rfc3161.ai.moda/servers.json for now, does that work or still not quite?

I mean list of URLs to all CAs root certificates so we can download them and put to trusted list.
I can find one for ssl.com for example: https://www.ssl.com/how-to/install-ssl-com-ca-root-certificates/#ftoc-heading-4

But where can I find CA root certificate for TSA http://timestamp.acs.microsoft.com

The thing is we cannot make proper TSA request without having it's CA root certificate in local trusted list.
When the TSA can be done with any server from the list https://rfc3161.ai.moda/servers.json (thanks, that's quite nice), we need to have all root certificates in one place.

@Pique7
Copy link

Pique7 commented Sep 8, 2024

Hello! What can these servers be used for? Are they suitable for productive systems?
I am currently looking for a solution to sign log entries with some kind of trusted timestamp.

@Manouchehri
Copy link
Author

@Pique7 You can use them for anything, many folks are using https://rfc3161.ai.moda in production. We serve a few million requests per month now I think, with higher uptime than the majority of any single RFC3161 server (since we have automatic failovers).

@chimmmpie
Copy link

How can i verify the timestamp? I get a response from a random server. But i also would like to verify this response locally. But for that i need CA and intermediate files i think. Could u also expose those/add them to the server list? I assume your backend has them in order to verify the response. Bonus for a example command :)

@paris-ci
Copy link

paris-ci commented Oct 4, 2024

@chimmmpie I have made a script that extracts the .cer / .crt from a timestamping service

#!/usr/bin/env bash

set -o errexit
set -o nounset
set -o pipefail

# Check that we have the name of the TSA service as a first arg and the URL as a second arg
if [ "$#" -ne 2 ]; then
    echo "Illegal number of parameters"
    echo "Usage: $0 <TSA_URL> <TSA_NAME>"
    exit 1
fi

TSA_URL=$1
TSA_NAME=$2

echo "==> We are trying to get the TSA certificate from the following service : $TSA_NAME ($TSA_URL)"

echo "==> Sending a signature request..."
openssl rand 256 | openssl ts -query -data - -cert -sha256 | curl -s -S --data-binary @- "$TSA_URL" --header "Content-Type: application/timestamp-query" -o - -v > "$TSA_NAME.reply.tsr"

echo "==> Verifying the response..."
openssl ts -reply -text -in "$TSA_NAME.reply.tsr" || (echo "==> Verification failed :" && cat "$TSA_NAME.reply.tsr" && rm "$TSA_NAME.reply.tsr" && exit 1)

echo "==> Extracting the token..."
openssl ts -reply -in "$TSA_NAME.reply.tsr" -token_out -out "$TSA_NAME.token.tk"

echo "==> Extracting the TSA certificate..."
openssl pkcs7 -inform DER -in "$TSA_NAME.token.tk" -print_certs -outform PEM -out "$TSA_NAME.cer"

echo "==> Extracting the TSA certificate as a .crt..."
openssl x509 -inform PEM -in "$TSA_NAME.cer" -out "$TSA_NAME.crt"

rm "$TSA_NAME.reply.tsr" "$TSA_NAME.token.tk"

Call it like ./request_crt.sh http://timestamp.acs.microsoft.com/ microsoft to get everything in microsoft.crt

@vasekkral
Copy link

I have made a script that extracts the .cer / .crt from a timestamping service

Thanks, that is great!

Would it be possible to make version of the script that downloads all certs for servers provided by https://rfc3161.ai.moda/servers.json?

@chimmmpie
Copy link

chimmmpie commented Oct 7, 2024

@chimmmpie I have made a script that extracts the .cer / .crt from a timestamping service

That looks interesting. But it would suggest to me that the cert is already in the response? Or does anyone think that some of the openssl commands will fetch it in the background?

@paris-ci
Copy link

paris-ci commented Oct 7, 2024

The -certpart in openssl ts -query -data - -cert -sha256 asks the TSA to return its cert as well

@Pique7
Copy link

Pique7 commented Nov 10, 2024

@Pique7 You can use them for anything, many folks are using https://rfc3161.ai.moda in production. We serve a few million requests per month now I think, with higher uptime than the majority of any single RFC3161 server (since we have automatic failovers).

Thanks for your reply. Now I have another question:
The TSA certificate of my current test response has a validity of 10 years. I thinks that's a lot. But what can I do when the TSA certificate expires? Sorry if this question is too stupid or off-topic/misplaced.

@Manouchehri
Copy link
Author

The signature should still be considered valid (in my opinion), since it was signed within the original lifetime of the CA. It just can’t (or rather shouldn’t) be used for new signatures.

You shouldn’t have to do anything. All of the upstream servers for rfc3161.ai.moda should rollover to using a new certificates long before the current ones expire.

@Manouchehri
Copy link
Author

I you want to see the CA that was used in the response for your request, you can use asn1parse. Example:

openssl rand 512 | openssl ts -query -data - -cert -sha512 | curl -s -S --data-binary @- https://rfc3161.ai.moda -o - -v | openssl asn1parse -in /dev/stdin -inform DER -dump

@vasekkral
Copy link

Thanks for tip.

But once again: we are not able to generate a time stamp unless we have CA root certificate in the local "trusted" list.
Would it be possible to provide a list (URLs) of all available timestamping CA root certificates?

@Manouchehri
Copy link
Author

@vasekkral Sure. Note, these certificates do change over time.

#!/usr/bin/env bash

# Available cryptographic hash algorithms for timestamp requests
# These algorithms are tried sequentially until a successful response is received
hash_algorithms=(
    "sha512" "blake2b512" "blake2s256" "md4" "md5" "md5-sha1" "mdc2" "ripemd"
    "ripemd160" "rmd160" "sha1" "sha224" "sha256" "sha3-224" "sha3-256"
    "sha3-384" "sha3-512" "sha384" "sha512-224" "sha512-256" "shake128"
    "shake256" "sm3" "ssl3-md5" "ssl3-sha1" "whirlpool"
)

# Attempts to obtain a timestamp token from a TSA server using specified parameters
# Returns 0 on success, 1 on failure
try_timestamp_request() {
    local url="$1"          # TSA server endpoint
    local hash_algo="$2"    # Cryptographic hash algorithm
    local tmp_query="$3"    # Path to store the timestamp request
    local tmp_reply="$4"    # Path to store the server's response
    local tmp_token="$5"    # Path to store the extracted timestamp token

    # Process flow:
    # 1. Generate random data as input
    # 2. Create a timestamp query using the specified hash algorithm
    # 3. Send the query to the TSA server
    # 4. Extract the timestamp token from the response
    if openssl rand 512 | \
       openssl ts -query -data - -cert -"$hash_algo" > "$tmp_query" 2>/dev/null && \
       curl -H "Content-Type: application/timestamp-query" \
            -H "Accept: application/timestamp-reply" \
            -s -S --data-binary @"$tmp_query" "$url" -o "$tmp_reply" && \
       openssl ts -reply -in "$tmp_reply" -token_out -out "$tmp_token" 2>/dev/null; then
        return 0    # All operations completed successfully
    else
        return 1    # One or more operations failed
    fi
}

# Main processing loop: Retrieve and process TSA server information
curl -s https://rfc3161.ai.moda/servers.json | \
jq -r '.[] | {name: .name, url: .url} | @json' | \
while read -r line; do
    # Extract server details from JSON response
    name=$(echo "$line" | jq -r '.name')   # Server's friendly name
    url=$(echo "$line" | jq -r '.url')     # Server's API endpoint

    # Create filesystem-safe server name by removing special characters
    safe_name=$(echo "$name" | tr -c '[:alnum:]' '_' | tr -s '_' | sed 's/^_//;s/_$//')

    # Create temporary storage for request/response data
    tmp_query=$(mktemp)
    tmp_reply=$(mktemp)
    tmp_token=$(mktemp)

    success=false           # Tracks if any attempt succeeded
    successful_hash=""      # Records which hash algorithm worked

    # Try each hash algorithm until successful
    for hash_algo in "${hash_algorithms[@]}"; do
        echo "Trying $hash_algo for $name..."
        if try_timestamp_request "$url" "$hash_algo" "$tmp_query" "$tmp_reply" "$tmp_token"; then
            success=true
            successful_hash="$hash_algo"
            break
        fi
    done

    if [ "$success" = true ]; then
        # Extract and save the CA certificate from the successful response
        if openssl pkcs7 -inform DER -in "$tmp_token" -print_certs -outform PEM -out "${safe_name}.pem" 2>/dev/null; then
            echo "Successfully extracted CA certificate for: $name (using $successful_hash)"
            echo "$name,$url,$successful_hash" >> successful_servers.log
        else
            echo "$url" >> failed_ca_certs.log
            echo "Failed to extract CA certificate for: $name"
        fi
    else
        echo "$url" >> failed_ca_certs.log
        echo "Failed to get timestamp response from: $name (tried all hash algorithms)"
    fi

    # Cleanup temporary files to prevent disk space issues
    rm -f "$tmp_query" "$tmp_reply" "$tmp_token"
done

This will dump the full certificate chain for all of the CAs. e.g. this is what my folder looks like after running the script:

APED.pem					Entrust.pem					QuoVadis_China.pem
Adacom.pem					FreeTSA.pem					QuoVadis_EU.pem
Aloaha.pem					GlobalSign.pem					SDA_GOV_GE.pem
Apple.pem					IdenTrust.pem					SEP_Bulgaria.pem
Azure.pem					Instituto_dos_Registos_e_do_Notariado_I_P.pem	SSL_com.pem
BalTstamp.pem					Izenpe.pem					Sectigo.pem
Belgium_Federal_Goverment.pem			Lex_Persona.pem					SwissSign.pem
CNBS.pem					Mahidol_University.pem				Swiss_Goverment.pem
CatCert.pem					MeSign.pem					TSA_SINPE.pem
Certum.pem					Netlock.pem					successful_servers.log
Digicert.pem					QuoVadis.pem

@vasekkral
Copy link

@Manouchehri great, thanks a lot, works perfectly. Now we can call our "time stamper" util with your load balancer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment