česky | cymraeg | deutsch | english | español | français | nederlands | português --- more step-by-step guides
Verify fingerprints via DNSSEC
To verify if the fingerprints of the downloaded root certificates are correct you can use DNSSEC-protected TXT records under the cacert.org domain.
The information about the fingerprints is stored in the DNS zone _fp.cacert.org - the underscore indicates non-host information. For each generation of root certificates a new sub-directory will be created. The current one is "g1". To list all available certificates of a specific generation you can query the label _certs for that sub-directory given a DNS query for _certs.g1._fp.cacert.org yielding the two names "root_X0F class3_X0E" as the certificates. Each of those references in turn provides both an URL ("_url") and a set of fingerprints (_md5, _sha1, _sha256) needed for the verified download of that certificate. To download the current (g1) root certificate you'd thus look for the download URL at _url.root_X0F.g1._fp.cacert.org and verify the SHA2-256 fingerprint given at _sha256.root_X0F.g1._fp.cacert.org. Fingerprints are always uppercase and without any delimiters.
When using commands and scripts, always start with the heading record _certs.g1._fp.cacert.org, currently yielding "root_X0F class3_X0E". Where material uses "root class3" instead it is outdated and should be implementing the first query.
For short test on Linux the below code can do the check. For production use MAKE sure that the DNS responses are validated. This can be achieved by installation of a validating stub resolver [1, 2]. Please refer to your distributions advice on how to properly setup DNSSEC if necessary.
$ host -t TXT _url.root_X0F.g1._fp.cacert.org. _url.root_X0F.g1._fp.cacert.org descriptive text "http://www.cacert.org/certs/root_X0F.crt" $ host -t TXT _sha256.root_X0F.g1._fp.cacert.org. _sha256.root_X0F.g1._fp.cacert.org descriptive text "07EDBD824A4988CFEF4215DA20D48C2B41D71529D7C900F570926F277CC230C5" $ wget -O root_X0F.crt 'http://www.cacert.org/certs/root_X0F.crt' $ openssl x509 -in root_X0F.crt -noout -fingerprint -sha256 | tr -d : SHA256 Fingerprint=07EDBD824A4988CFEF4215DA20D48C2B41D71529D7C900F570926F277CC230C5
In Windows, the same information can be obtained using nslookup:
>nslookup Default Server: my.dnssrv.eu Address: 2001:470:6f:458:c56d:5931:992:4708 > set type=TXT > _url.root_X0F.g1._fp.cacert.org. Server: my.dnssrv.eu Address: 2001:470:6f:458:c56d:5931:992:4708 Non-authoritative answer: _url.root_X0F.g1._fp.cacert.org text = "http://www.cacert.org/certs/root_X0F.crt" > _sha256.root_X0F.g1._fp.cacert.org. Server: my.dnssrv.eu Address: 2001:470:6f:458:c56d:5931:992:4708 Non-authoritative answer: _sha256.root_X0F.g1._fp.cacert.org text = "07EDBD824A4988CFEF4215DA20D48C2B41D71529D7C900F570926F277CC230C5" > _url.class3_X0E.g1._fp.cacert.org. Server: my.dnssrv.eu Address: 2001:470:6f:458:c56d:5931:992:4708 Non-authoritative answer: _url.class3_X0E.g1._fp.cacert.org text = "http://www.cacert.org/certs/class3_X0E.crt" > _sha256.class3_X0E.g1._fp.cacert.org. Server: my.dnssrv.eu Address: 2001:470:6f:458:c56d:5931:992:4708 Non-authoritative answer: _sha256.class3_X0E.g1._fp.cacert.org text = "F6873D70D67596C2ACBA34401E69738B52701DD6AB06B49749BC55150936D544" > exit
A small script doing the validation (if the system resolver is properly configured) can be seen below. The exit status is 0 on success, or non-zero otherwise (error 3, 4 and 5 may indicate a system resolver dropping DNSSEC records).
The script does not work, at least on Ubuntu 15.10 platform. It ends with the result code of 3 on the line 8.
The updated script with the original lines commented just before the changed ones: CAcert-roots-fingerprint-test.sh. These are changed lines, according to the original script listed below: 8, 10, 24, 26, 33, 35.
- Reasons:
- There is no flag "ad" in the "dig" output; it is replaced by "rd" in the script.
- There are no lines containing " RRSIG" in the "dig" output.
1 #!/bin/bash
2
3 # Fetch list of available root certificates (names)
4 TMP_RESPONSE=___root___$$.tmp
5 dig +nocmd +comments +nostats +norrcomments +dnssec IN TXT _certs.g1._fp.cacert.org. > ${TMP_RESPONSE} || exit 1
6
7 grep -q '^;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ' ${TMP_RESPONSE} || exit 2
8 grep '^;; flags:' ${TMP_RESPONSE} | cut '-d;' -f3 | cut -d: -f2 | grep -q ad || exit 3
9 grep '^; EDNS:' ${TMP_RESPONSE} | tr ";," "\n\n" | grep flags | cut -d: -f2 | grep -q do || exit 4
10 grep -v '^;' ${TMP_RESPONSE} | grep -q RRSIG || exit 5
11
12 ROOTS=$(grep -v '^;' "${TMP_RESPONSE}" | grep -v " RRSIG " | grep TXT | cut '-d"' -f2 | head -1)
13 printf "Found roots: %s\n\n" "$ROOTS"
14
15 rm -f -- "${TMP_RESPONSE}"
16
17 for a in $ROOTS; do
18 printf "Downloading %s ...\n" "$a"
19
20 # Fetch the download URL of the current certificate in the list
21 TMP_ROOT=___root___$$.tmp
22 dig +nocmd +comments +nostats +norrcomments +dnssec IN TXT "_url.$a.g1._fp.cacert.org." > ${TMP_ROOT} || exit 11
23 grep -q '^;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ' ${TMP_ROOT} || exit 12
24 grep '^;; flags:' ${TMP_ROOT} | cut '-d;' -f3 | cut -d: -f2 | grep -q ad || exit 13
25 grep '^; EDNS:' ${TMP_ROOT} | tr ";," "\n\n" | grep flags | cut -d: -f2 | grep -q do || exit 14
26 grep -v '^;' ${TMP_ROOT} | grep -q RRSIG || exit 15
27 CRT_URL=$(grep -v '^;' ${TMP_ROOT} | grep -v " RRSIG " | grep TXT | cut '-d"' -f2 | head -1)
28
29 # Fetch the SHA2-256 fingerprint of the current certificate in the list
30 TMP_ROOT_FP=___root___$$.tmp
31 dig +nocmd +comments +nostats +norrcomments +dnssec IN TXT "_sha256.$a.g1._fp.cacert.org." > ${TMP_ROOT_FP} || exit 21
32 grep -q '^;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ' ${TMP_ROOT_FP} || exit 22
33 grep '^;; flags:' ${TMP_ROOT_FP} | cut '-d;' -f3 | cut -d: -f2 | grep -q ad || exit 23
34 grep '^; EDNS:' ${TMP_ROOT_FP} | tr ";," "\n\n" | grep flags | cut -d: -f2 | grep -q do || exit 24
35 grep -v '^;' ${TMP_ROOT_FP} | grep -q RRSIG || exit 25
36 CRT_FP=$(grep -v '^;' ${TMP_ROOT_FP} | grep -v " RRSIG " | grep TXT | cut '-d"' -f2 | head -1)
37
38 # Download the current certificate in the list from the obtained URL
39 wget --quiet -O "___CRT___$a.crt" "$CRT_URL" || exit 31
40
41 # Calculate fingerprint of the downloaded certificate file
42 DL_FP=$(openssl x509 -in "___CRT___$a.crt" -noout -fingerprint -sha256 | cut -d= -f2 | tr -d :)
43
44 printf "Found Certificate for Root \"%s\":\n- CRT URL:\t%s\n- CRT FP:\t%s\n- DL FP:\t%s\n\n" "$a" "$CRT_URL" "$CRT_FP" "$DL_FP"
45
46 # Compare fingerprints of the downloaded certificate with the fetched value from DNS
47 if [ ! "$CRT_FP" == "$DL_FP" ]; then
48 printf 'Verification Failed!'
49 exit 32
50 fi
51
52 rm -f -- "$TMP_ROOT" "$TMP_ROOT_FP"
53 done
The above script should produce the following output:
_certs.g1._fp.cacert.org. 21599 IN TXT "root_X0F class3_X0E" Found roots: root_X0F class3_X0E Downloading root_X0F ... _url.root_X0F.g1._fp.cacert.org. 20558 IN TXT "http://www.cacert.org/certs/root_X0F.crt" _sha256.root_X0F.g1._fp.cacert.org. 20632 IN TXT "07EDBD824A4988CFEF4215DA20D48C2B41D71529D7C900F570926F277CC230C5" Found Certificate for Root "root_X0F": - CRT URL: http://www.cacert.org/certs/root_X0F.crt - CRT FP: 07EDBD824A4988CFEF4215DA20D48C2B41D71529D7C900F570926F277CC230C5 - DL FP: 07EDBD824A4988CFEF4215DA20D48C2B41D71529D7C900F570926F277CC230C5 Downloading class3_X0E ... _url.class3_X0E.g1._fp.cacert.org. 20651 IN TXT "http://www.cacert.org/certs/class3_X0E.crt" _sha256.class3_X0E.g1._fp.cacert.org. 20680 IN TXT "F6873D70D67596C2ACBA34401E69738B52701DD6AB06B49749BC55150936D544" Found Certificate for Root "class3_X0E": - CRT URL: http://www.cacert.org/certs/class3_X0E.crt - CRT FP: F6873D70D67596C2ACBA34401E69738B52701DD6AB06B49749BC55150936D544 - DL FP: F6873D70D67596C2ACBA34401E69738B52701DD6AB06B49749BC55150936D544
[1] [http://askubuntu.com/questions/51367/how-do-i-configure-my-caching-nameserver-to-validate-dnssec]
[2] [https://wiki.debian.org/DNSSEC]