Root DNSSEC trust anchors
IANA's root DNSSEC KSK-2024, key tag 38696, began signing the root DNSKEY set on October 11, 2026.
The root DNSSEC Key Signing Key KSK-2024, key tag 38696, began signing the root DNSKEY RRset on 11 October 2026. The key was generated in April 2024 during Root KSK Ceremony 53 and later published in IANA's signed root-anchors.xml with algorithm 8 (RSASHA256). The post explains resolver trust anchors and how the new digest and DNSKEY relate to predecessor key tags 20326 and 19036.
- KSK-2024, key tag 38696, now signs the root DNSKEY set.
- The key was generated in April 2024 at Root KSK Ceremony 53.
- IANA publishes it in root-anchors.xml using algorithm 8, RSASHA256.
- Predecessor root key tags 20326 and 19036 remain documented.
Indicators of compromiseauto-extracted · verify before use · export allAll →
| Type | Indicator | Context |
|---|---|---|
| sha256 | 49aac11d7b6f6446702e54a1607371607a1a41855200fd2ce1cdde32f24e8fb5 | rithm> 8 </Algorithm> <DigestType> 2 </DigestType> <Digest> 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5 </Digest> </KeyDigest> <KeyDigest id= "Klajeyz" validFrom= |
| sha256 | 683d2d0acb8c9b712a1948b27f741219298d0a450d612c483af444a4c0fb2b16 | rithm> 8 </Algorithm> <DigestType> 2 </DigestType> <Digest> 683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16 </Digest> <PublicKey> AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3 |
| sha256 | e06d44b80b8f1d39a95c0b0d7c65d08458e880409bbc683457104237c7f8ec8d | rithm> 8 </Algorithm> <DigestType> 2 </DigestType> <Digest> E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D </Digest> <PublicKey> AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkM |
Full article2,304 words · extracted from jpmens.net · click to collapse
Root DNSSEC trust anchors
The root DNSSEC trust anchor, the Key Signing Key or KSK as it’s called, was rolled, i.e. renewed, on Sunday 2026-10-11. To be precise the key was created two years ago, but it is now used for signing the root DNSKEY resource record set (RRset). I’m not involved in the process, but I am an avid follower, and as such I tooted before the event and after:
The second screen shot shows an arrow pointing at 38696 which is the key tag (or key id) of this new key, and dig(1) is showing how it was used to sign the set of DNSKEY resource records.
During a discussion on Tuesday which began because of a quite disastrous and currently ongoing failure in the German medical IT infrastructure of Gematik, it turns out that some people I thought would know all about this don’t, so I’ll jot down some notes about how I think it ties in together.
Let’s begin with trust anchor; what is that? DNSSEC validation is performed by a recursive resolver which obtains cryptographically signed answers to queries. These signatures, created by the the authoritative DNS server responsible for the data, need to be verified, and the verification process needs a form of trust. Just like you trust the content your favorite Web site offers you because you trust your Web browser and it trusts that site, we need a similar chain of trust for the DNS.
So resolvers need to begin their chain of trust somewhere, but how should they do that? The need to trust the signed DNS root zone, and from thereon down to the name of your favorite Web site (e.g. www.example.com). (Many moons ago I created a diagram to depict the DNSSEC chain of trust.)
The current root signing key, the Key Signing Key code-named KSK-2024, was actually generated in April of 2024 during what is known as a key ceremony, and you can watch the video of how that happens at the Root KSK Ceremony 53. It’s quite interesting to watch, but let me say you won’t need popcorn: it’s a long and tedious thing, but it’s interesting nevertheless to scroll through it a bit. If you do sit for the whole séance you’ll be at it for 8 1/2 hours, so maybe strong coffee instead of popcorn. :-) Do also look at the associated documents, such as the scripts, a bit further down on that page.
So KSK-2024 was generated, and the attestation to it is signed by all participants present. If you look at that page, you might notice the key label assigned by the Hardware Security Module (HSM): it reads Kmyv6jo, and we’ll see that opaque string later again.
At some point after the generation of the key, the people at IANA add the public key to the root-anchors.xml file, and the file is signed. Here’s a current (as per the date of this writing) copy: (It is quite possible that when you read this the file will be different when old keys are removed and the next one is added.)
<?xml version="1.0" encoding="UTF-8"?>
<TrustAnchor id="0C05FDD6-422C-4910-8ED6-430ED15E11C2" source="http://data.iana.org/root-anchors/root-anchors.xml">
<Zone>.</Zone>
<KeyDigest id="Kjqmt7v" validFrom="2010-07-15T00:00:00+00:00" validUntil="2019-01-11T00:00:00+00:00">
<KeyTag>19036</KeyTag>
<Algorithm>8</Algorithm>
<DigestType>2</DigestType>
<Digest>49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5</Digest>
</KeyDigest>
<KeyDigest id="Klajeyz" validFrom="2017-02-02T00:00:00+00:00">
<KeyTag>20326</KeyTag>
<Algorithm>8</Algorithm>
<DigestType>2</DigestType>
<Digest>E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D</Digest>
<PublicKey>AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU=</PublicKey>
<Flags>257</Flags>
</KeyDigest>
<KeyDigest id="Kmyv6jo" validFrom="2024-07-18T00:00:00+00:00">
<KeyTag>38696</KeyTag>
<Algorithm>8</Algorithm>
<DigestType>2</DigestType>
<Digest>683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16</Digest>
<PublicKey>AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC6rXTsDc449/cidltpkyGwCJNnOAlFNKF2jBosZBU5eeHspaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vrhbu6irwCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAxm9qyJNg/wOZqqzL/dL/q8PkcRU5oUKEpUge71M3ej2/7CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+u1TQNatX2XBuzZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxPvYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc=</PublicKey>
<Flags>257</Flags>
</KeyDigest>
</TrustAnchor>
The XML contains the predecessor keys as well as the current one, i.e. KSK-2024. The XML has a root element (TrustAnchor), and each KeyDigest describes one key, the element names being rather self-explanatory. Note how the id attribute of KeyDigest contains the opaque string we saw earlier, i.e. the HSM key label with the value Kmyv6jo.
Digest and PublicKey respectively contain the hash of the key (what will later become known as a Delegation Signer (DS) record), and the public DNSKEY resource record we’ll later see in the DNS. Or rather, let’s look now (I abbreviate output to concentrate on KSK-2024):
$dig . DNSKEY +multi
;<<>> DiG 9.20.0 <<>> . DNSKEY +multi
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41293
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
. 26682 IN DNSKEY 257 3 8 (
AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC
6rXTsDc449/cidltpkyGwCJNnOAlFNKF2jBosZBU5eeH
spaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vr
hbu6irwCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAx
m9qyJNg/wOZqqzL/dL/q8PkcRU5oUKEpUge71M3ej2/7
CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+
u1TQNatX2XBuzZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxP
vYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc=
) ;KSK; alg = RSASHA256 ; key id = 38696
We’re informed it’s a KSK, and its key id (KeyTag in the XML – yes, I also find it tedious that there are two terms for the same thing) is 38696. Also note, how the base64-encoded blob corresponds to what we saw in the XML PublicKey element.
verification
This is all fine and dandy, but how can I trust what I’m seeing here?
Astute readers will have noticed the ad flag in above DNS response, because I’m using a validating resolver which has validated the DNS response, but what if I don’t have one of those? How do I verify the above?
If we want to do this manually, on the Root Zone Trust Anchors page, we see a link to a signature file with which we can verify the content of root-anchors.xml and certificates for doing so. Since I obtain these files over a TLS connection (and I tend to trust my Web browser and in doing so, the issuer of the certificate which is used by IANA on that site) I can trust that I am getting the correct files.
I download all three files and verify the validity of root-anchors.xml:
$openssl smime -verify -CAfile icannbundle.pem \
-in root-anchors.p7s \
-inform der \
-content root-anchors.xml \
-out /dev/null
Verification successful
We’re not actually verifying the content with a well-known CA certificate but with that signed and issued by ICANN. If we look at the two certificates contained in icannbundle.pem:
$openssl storeutl -noout -text -certs icannbundle.pem | grep Subject:
Subject: O=ICANN, OU=ICANN Certification Authority, CN=ICANN Root CA, C=US
Subject: C=US, O=ICANN, OU=ICANN Certification Authority, CN=ICANN Root CA v2
Good enough for me now.
trust anchors and DNS resolvers
What I could now do is to generate the required data I feed to my DNS resolver so that it can perform validation. For instance for a BIND named.conf I could do this: (many thanks to @astro and Jordan (@whack) for improving on my very rusty XSL “skills”):
<?xml version="1.0" encoding="UTF-8"?>
<!-- thanks to Jordan aka XSLT-god (@whack@hackyderm.io) for optimization of my primitive XSLT -->
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/TrustAnchor">
<xsl:text>trust-anchors {</xsl:text>
<xsl:apply-templates select="KeyDigest">
<!-- Pass the value of the Zone element as a param to the template -->
<xsl:with-param name="zone" select="Zone" />
</xsl:apply-templates>
<xsl:text>
};
</xsl:text>
</xsl:template>
<xsl:template match="KeyDigest">
<xsl:param name="zone" />
"<xsl:value-of select="$zone" />" initial-ds <xsl:value-of select="concat(
KeyTag, ' ', Algorithm, ' ', DigestType, ' ',
'"', Digest, '";')"/>
</xsl:template>
</xsl:transform>
Using the above XSL and the content of root-anchors.xml we can create the required configuration for BIND:
$xsltproc anchors.xsl root-anchors.xml > trust-anchors.keys
$cat trust-anchors.keys
trust-anchors {
"." initial-ds 19036 8 2 "49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5"; "." initial-ds 20326 8 2 "E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D"; "." initial-ds 38696 8 2 "683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16";};$cat named.conf
include "trust-anchors.keys";
options {
directory "/tmp"; pid-file "/tmp/named.pid"; listen-on port 5301 { 127.0.0.1;};
#the following setting is explicitly configured as 'yes' for this
#demonstration. You will please typically configure this as 'auto'
dnssec-validation yes; recursion yes; empty-zones-enable no; allow-query { any;};
};BIND will validate responses (we’ll see the ad flag on them). We can test the negative case by commenting out the trust anchor for key tag 38696 which will cause a query for, say, jpmens.net to SERVFAIL because the resolver can’t chase the chain of trust down from the root.
Two things to note, please:
- the above
trust-anchors{}contains predecessor KSKs which are no longer in use, and - don’t do this.
namedcan be configured to do all this electrically (automatically) by configuringdnssec-validation auto;Manual configuration as we have done is not necessary, but you might want to do so in private DNS root environments, say. (cough)
We’ve used the root-anchors.xml file and have verified its content to be legitimate, and from that we have generated trust-anchors{}, here for BIND.
Are there other methods I could employ?
Yes.
I’ve been talking about trust anchors being like Delegation Signer (DS) records, but there are programs which want the DNSKEY records. To be sure, if I have a DNSKEY I can create the DS, using dnssec-dsfromkey(1) or ldns-key2ds(1). The latter produces lower-case hex letters, but we can safely ignore that.
$cat root.key # as seen above
. 26682 IN DNSKEY 257 3 8 (
AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC
...
vYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc=
) ;KSK; alg = RSASHA256 ; key id = 38696
$ldns-key2ds -n root.key
. 26682 IN DS 38696 8 2 683d2d0acb8c9b712a1948b27f741219298d0a450d612c483af444a4c0fb2b16
The current crop of open source resolvers (and I use the word crop with utmost love and respect!) all come with the current trust anchor already compiled in.
- PowerDNS Recursor has DS records compiled in:
$strings /usr/sbin/pdns_recursor |grep -i 683D2D0A 38696 8 2 683d2d0acb8c9b712a1948b27f741219298d0a450d612c483af444a4c0fb2b16 [{name: ., dsrecords: ['20326 8 2 e06d44b80b8f1d39a95c0b0d7c65d08458e880409bbc683457104237c7f8ec8d', '38696 8 2 683d2d0acb8c9b712a1948b27f741219298d0a450d612c483af444a4c0fb2b16']}] - Unbound can use either DS or DNSKEY records. It uses the
unbound-anchorutility to prime its keys, verifying the download similar to how we did earlier:$unbound-anchor -a /tmp/root.keys $cat /tmp/root.keys ;autotrust trust anchor file ;;id: . 1 ;;last_queried: 1791976321 ;;Wed Oct 14 13:12:01 2026 ;;last_success: 1791976321 ;;Wed Oct 14 13:12:01 2026 ;;next_probe_time: 1792017637 ;;Thu Oct 15 00:40:37 2026 ;;query_failed: 0 ;;query_interval: 43200 ;;retry_time: 8640 . 86400 IN DNSKEY 257 3 8 AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC6rXTsDc449/cidltpkyGwCJNnOAlFNKF2jBosZBU5eeHspaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vrhbu6irwCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAxm9qyJNg/wOZqqzL/dL/q8PkcRU5oUKEpUge71M3ej2/7CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+u1TQNatX2XBuzZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxPvYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc= ;{id = 38696 (ksk), size = 2048b} ;;state=2 [ VALID ] ;;count=0 ;;lastchange=1791976321 ;;Wed Oct 14 13:12:01 2026 . 86400 IN DNSKEY 257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b} ;;state=2 [ VALID ] ;;count=0 ;;lastchange=1791976321 ;;Wed Oct 14 13:12:01 2026 - BIND’s named has DS compiled into it.
$strings /usr/sbin/named | grep 683D2D0A . initial-ds 38696 8 2 "683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A - Knot Resolver’s configuration comes preloaded with a correct DNSKEY RRset:
$kresd Interactive mode: >trust_anchors.summary() '. 3600 DNSKEY 257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;Valid: ; KeyTag:20326 . 3600 DNSKEY 257 3 8 AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC6rXTsDc449/cidltpkyGwCJNnOAlFNKF2jBosZBU5eeHspaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vrhbu6irwCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAxm9qyJNg/wOZqqzL/dL/q8PkcRU5oUKEpUge71M3ej2/7CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+u1TQNatX2XBuzZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxPvYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc= ;Valid: ; KeyTag:38696 ' > - I can use IANA’s standalone utility to download DS and DNSKEY records, verified as we did earlier. (I omit copious output.)
$python3 -mvenv venv $source venv/bin/activate (venv) $git clone https://github.com/iana-org/get-trust-anchor (venv) $pip install get-trust-anchor/. (venv) $get-trust-anchor --print-ds Validation of the signature over the file succeeded. There were 3 KeyDigest elements in the trust anchor file. Added the trust anchor 0 to the list: Trust anchor 0: the validUntil '2019-01-11 00:00:00' is before today, so not using this trust anchor. Trust anchor 1: there was no validUntil attribute, so the validity is OK. Trust anchor 2: there was no validUntil attribute, so the validity is OK. After the date validity checks, there are now 2 records. Fetching via Google Public DNS... Found KSK 257 3 8 'AwEAAa96jeuknZl...UUeayffKC73PYc='. Found KSK 257 3 8 'AwEAAaz/tAm8yTn...UwNR1AkUTV74bU='. Trust anchor 1 matched KSK 'AwEAAa96jeuknZl...UUeayffKC73PYc=' Trust anchor 0 matched KSK 'AwEAAaz/tAm8yTn...UwNR1AkUTV74bU=' There were 2 matched KSKs. The key tag for this KSK is 38696 The key tag for this KSK is 20326 . IN DS 38696 8 2 683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16 . IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D Deleting the temporary files.and there it is: a verified DS record for key tag
38696, but the program can also create DNSKEY records.
basic Xpath and XSL
While I was toying with the root-anchors.xml file I thought it might be useful to create a list of keys from root-anchors.xml which I could process in a shell script. It turned out not to be, but you might want it.
First, using Xpath:
$xmllint --xpath './/KeyTag | .//Algorithm | .//DigestType | .//Digest' root-anchors.xml |
sed -E -e 's|</?[A-Za-z]+>||g' |
xargs -n4
19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D
38696 8 2 683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16
and then I became braver and cranked up the old XSL:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" encoding="UTF-8" />
<xsl:strip-space elements="*"/>
<xsl:template match="Zone">
<xsl:text></xsl:text>
</xsl:template>
<xsl:template match="KeyDigest">
<xsl:value-of select="concat(KeyTag, ' ', Algorithm, ' ', DigestType, ' ', Digest)" />
<xsl:text>
</xsl:text><!-- newline -->
</xsl:template>
</xsl:transform>
$xsltproc jp.xsl files/root-anchors.xml
19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D
38696 8 2 683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16
fin
So, back to the beginning of this article, what’s the problem with the Gematik infrastructure? Well, the so-called connectors are configured with a root-anchors.xml as we’ve been discussing, but it’s an old version which was never updated with the new trust anchor!!!.
That’s the problem, and to add insult to injury, technicians are being sent out into the field to copy/paste from this miserably-formatted PDF into the Web interface of the connectors.

Kids, don’t do this at home!