Validation

Every token embeds the full signing chain (signer → TSA intermediate → root CA). Verification is purely cryptographic — it never requires contacting CNX.

Trust anchor

FieldValue
Root CA subjectCN=CNX Internal CA, O=Cambodia Network Exchange, C=KH
Root CA SPKI sha256 (base64)m5vzPxboPEMCcS3RZh2ka74DfE33BRdqEjL5Dv7YfKE=
Root CA SPKI sha256 (hex)9b9bf33f16e83c4302712dd1661da46bbe037c4df705176a1232f90efed87ca1
DNS recorddig TXT _ca.cnx.net.kh
DNSSECcnx.net.kh is signed; .kh does not delegate a DS record, so this is not yet chain-validatable from an unconfigured resolver
TSA intermediate subject/C=KH/O=Cambodia Network Exchange/CN=CNX TSA Intermediate CA

Obtaining and verifying the CA certificate

CNX publishes an SPKI pin — a hash of the root CA's public key alone, not a full certificate fingerprint — as a DNS TXT record. The root CA certificate itself travels inside every timestamp token, so there is nothing separate to download: extract it from a token you already hold, then check it against the published pin.

# Step 1 — get the published SPKI pin
dig +dnssec TXT _ca.cnx.net.kh
# "v=spki1; alg=sha256; spki=m5vzPxboPEMCcS3RZh2ka74DfE33BRdqEjL5Dv7YfKE="

# Step 2 — extract the root CA certificate bundled inside a token you hold
openssl ts -reply -in validation-sample.txt.tsr -token_out \
  | openssl pkcs7 -inform DER -print_certs \
  | awk '/-----BEGIN/{c++} c==3' > cnx-tsa-ca.crt

# Step 3 — compute the SPKI hash of the extracted certificate
openssl x509 -in cnx-tsa-ca.crt -pubkey -noout \
  | openssl pkey -pubin -outform DER \
  | openssl dgst -sha256 -binary | base64
# Output must match the spki= value from step 1

cnx.net.kh is DNSSEC-signed, but .kh does not delegate a DS record down to it, so a standard resolver will show the RRSIG but will not set the AD (authenticated) bit. Treat the value on this page as authoritative for now.

Treat the published pin as the anchor, and verify it out-of-band on first use — standard practice for any pinned root of trust.

What verification proves

Verification answers three questions simultaneously:

  1. Authenticity — did this token come from CNX? (signature check against CNX TSA certificate)
  2. Integrity — does this token match this specific data? (hash comparison)
  3. Time — when did CNX witness this data? (read genTime from the token)

All three must pass. A token that fails any one of them is either forged, mismatched, or the data has been modified since timestamping.

Worked example

Download both files and reproduce every step below yourself:

This pair is permanent — the sample file's content will not change, so its hash and this token stay valid as a fixed reference for testing your own tooling before trusting it against real records.

1. Decode the token contents (openssl ts -reply -in validation-sample.txt.tsr -text):

Status: Granted.

Policy OID:    1.3.6.1.4.1.66148.1.2.1
Hash Algorithm: sha256
Message data:  5236001e31573abd350972d570ca85cea7691674d0f8839f91f7bfd44ebefaf9
Serial number: 0xC10424E46192CA52363648EAB7D28C2A
Time stamp:    Jul 10 10:29:46.439 2026 GMT
Accuracy:      unspecified seconds, 0x01 millis, unspecified micros
Ordering:      yes
TSA:           DirName:/CN=cnx.tsa.cnx.net.kh/O=CNX TSA
FieldWhat to check
StatusMust read Granted. Anything else means CNX refused to issue — the token is not valid evidence.
Policy OIDIdentifies which Timestamp Policy terms govern this token — retention, SLA, dispute support. The same OID appears in the signer certificate itself (see step 2).
Message dataThe SHA-256 hash CNX witnessed. Compute sha256sum validation-sample.txt on the file you downloaded and compare — it must match exactly, or the token isn't attesting to the file you think it is.
Serial numberA fingerprint tied to this specific request, not a sequential counter. CNX, or an authorized logs custodian per your subscription terms, can look up c10424e46192ca52363648eab7d28c2a in access.log and evidence.log to confirm exactly when and against which hash this token was issued.
Time stampWhen CNX witnessed the hash, to millisecond precision — the .439 is a real recorded digit, not display rounding, sourced from the atomic-clock chain described in Policy §4.
AccuracyCNX's claimed error bound on the time stamp field: 1 millisecond. Backed by continuous PTP-disciplined clock synchronization to an in-country atomic reference.
TSAThe exact domain that signed this token. This is how you tell tiers apart without a separate lookup: free.tsa.cnx.net.kh vs. a subscriber's own subdomain determines which retention/SLA clause in the policy document applies to this specific token.

2. Check the signer certificate's own policy pointer:

$ openssl ts -reply -in validation-sample.txt.tsr -token_out \
    | openssl pkcs7 -inform DER -print_certs \
    | awk '/-----BEGIN/{c++} c==1' > signer.crt

$ openssl x509 -in signer.crt -noout -text | grep -A3 "Certificate Policies"
X509v3 Certificate Policies:
    Policy: 1.3.6.1.4.1.66148.1.2.1
      CPS: https://tsa.cnx.net.kh/policy.html

The policy document's URL is embedded directly in the certificate.

3. Confirm the chain to the root certificate:

#SubjectIssued byRole
1CN=cnx.tsa.cnx.net.kh, O=CNX TSACNX TSA Intermediate CASigner — the key that actually produced this token, sealed in this node's vTPM
2CN=CNX TSA Intermediate CACNX Internal CAIntermediate — HSM-held, issues every node's signer cert
3CN=CNX Internal CAself-signed (root)Root — HSM-held, the trust anchor. Its SPKI is what you pin against DNS.

All three travel inside the token — see step 4. A verifier never needs to fetch anything from CNX to check this chain.

4. Extract the chain and check the root against the published pin:

$ openssl ts -reply -in validation-sample.txt.tsr -token_out \
    | openssl pkcs7 -inform DER -print_certs > chain.pem

$ awk '/-----BEGIN/{c++} c==3' chain.pem > cnx-tsa-ca.crt

$ openssl x509 -in cnx-tsa-ca.crt -pubkey -noout \
    | openssl pkey -pubin -outform DER \
    | openssl dgst -sha256 -binary | base64
m5vzPxboPEMCcS3RZh2ka74DfE33BRdqEjL5Dv7YfKE=

Matches the Trust anchor value on this page exactly. That match is the whole verification — everything else in the chain follows cryptographically from it.

5. Run the actual verification:

$ openssl ts -verify -data validation-sample.txt -in validation-sample.txt.tsr \
    -untrusted chain.pem -CAfile cnx-tsa-ca.crt
Verification: OK

Run this command and get Verification: OK — that alone proves everything above: authenticity, integrity, and time, offline, without contacting CNX.

Verify a token

openssl ts -verify -data validation-sample.txt -in validation-sample.txt.tsr -CAfile cnx-tsa-ca.crt
# Verification: OK

Read the attested timestamp:

openssl ts -reply -in validation-sample.txt.tsr -text | grep "Time stamp"
# Time stamp: Jul 10 10:29:46.439 2026 GMT

Batch verification

Verify all records in a directory against their tokens. Run on a schedule to confirm no records have been modified since timestamping:

for f in records/*.json; do
  openssl ts -verify -data "$f" -in "${f%.json}.tsr" \
    -CAfile cnx-tsa-ca.crt 2>/dev/null || echo "FAILED: $f"
done

Presenting tokens to auditors

For a regulatory examination or external audit, provide:

  1. The original record — in exactly the form used to generate the hash
  2. The .tsr token file for that record
  3. The CNX TSA CA certificate (cnx-tsa-ca.crt)
  4. The verification command above

An auditor with OpenSSL installed can independently verify the token without any CNX involvement — the verification is purely cryptographic. CNX does not need to be contacted, online, or cooperative. The signed token is self-contained evidence.

When to involve CNX directly: for a subscriber-tier token (issued under a CNX Precision Time subscription), an auditor or your own team can ask CNX to independently confirm a specific signing event — that request goes through your CNX account contact under the subscription agreement. Subscriber-tier logs (serial number, timestamp, hash algorithm, requester address) are sealed and retained for a minimum of 7 years — see Policy §7 and §9. This path does not exist for the free tier: those logs are operational-only and purged within 30 days, so past that window the token's own cryptographic proof above is the only evidence that will ever exist for it — there is no one left to ask.