Every token embeds the full signing chain (signer → TSA intermediate → root CA). Verification is purely cryptographic — it never requires contacting CNX.
| Field | Value |
|---|---|
| Root CA subject | CN=CNX Internal CA, O=Cambodia Network Exchange, C=KH |
| Root CA SPKI sha256 (base64) | m5vzPxboPEMCcS3RZh2ka74DfE33BRdqEjL5Dv7YfKE= |
| Root CA SPKI sha256 (hex) | 9b9bf33f16e83c4302712dd1661da46bbe037c4df705176a1232f90efed87ca1 |
| DNS record | dig TXT _ca.cnx.net.kh |
| DNSSEC | cnx.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 |
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.
Verification answers three questions simultaneously:
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.
Download both files and reproduce every step below yourself:
cnx.tsa.cnx.net.khThis 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
| Field | What to check |
|---|---|
Status | Must read Granted. Anything else means CNX refused to issue — the token is not valid evidence. |
Policy OID | Identifies which Timestamp Policy terms govern this token — retention, SLA, dispute support. The same OID appears in the signer certificate itself (see step 2). |
Message data | The 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 number | A 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 stamp | When 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. |
Accuracy | CNX's claimed error bound on the time stamp field: 1 millisecond. Backed by continuous PTP-disciplined clock synchronization to an in-country atomic reference. |
TSA | The 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:
| # | Subject | Issued by | Role |
|---|---|---|---|
| 1 | CN=cnx.tsa.cnx.net.kh, O=CNX TSA | CNX TSA Intermediate CA | Signer — the key that actually produced this token, sealed in this node's vTPM |
| 2 | CN=CNX TSA Intermediate CA | CNX Internal CA | Intermediate — HSM-held, issues every node's signer cert |
| 3 | CN=CNX Internal CA | self-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.
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
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
For a regulatory examination or external audit, provide:
.tsr token file for that recordcnx-tsa-ca.crt)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.