<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xml" href="http://www.uli-fahrer.de/feed.xslt.xml"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://www.uli-fahrer.de/feed.xml" rel="self" type="application/atom+xml" /><link href="http://www.uli-fahrer.de/" rel="alternate" type="text/html" /><updated>2026-02-01T20:14:36+00:00</updated><id>http://www.uli-fahrer.de/feed.xml</id><title type="html">Uli Fahrer</title><subtitle>Uli Fahrer: DevSecOps Engineer</subtitle><author><name>Uli Fahrer</name></author><entry><title type="html">WKD for OpenPGP key discovery on Github Pages</title><link href="http://www.uli-fahrer.de/blog/2026/02/01/gpg-wdk-key-discovery" rel="alternate" type="text/html" title="WKD for OpenPGP key discovery on Github Pages" /><published>2026-02-01T00:00:00+00:00</published><updated>2026-02-01T00:00:00+00:00</updated><id>http://www.uli-fahrer.de/blog/2026/02/01/gpg-wdk-key-discovery</id><content type="html" xml:base="http://www.uli-fahrer.de/blog/2026/02/01/gpg-wdk-key-discovery"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>Last year, I created an OpenPGP <a href="https://wiki.gnupg.org/WKDHosting">Web Key Directory (WKD)</a> hosted on Github Pages to enable automatic lookup of my OpenPGP public keys based on my email address.</p>

<p>Back then, I decided to use the <strong>Direct Method</strong>, which is a fallback method for discovering keys through WKD and does not require setting up a custom DNS record.</p>

<p>Most WKD clients though prefer the <strong>Advanced Method</strong> by looking up the <code class="language-plaintext highlighter-rouge">openpgpkey.domain.tld</code> domain for advanced key discovery. If this DNS request fails, they will lookup <code class="language-plaintext highlighter-rouge">domain.tld</code> and then use this host for the direct method.</p>

<blockquote class="callout callout-tip">
  <p>The directory structure differs between the direct and the advanced method. Use <a href="https://www.webkeydirectory.com">webkeydirectory.com</a> to verify your setup after setting up the WKD.</p>
</blockquote>

<h1 id="problem-statement--troubleshooting">Problem Statement &amp; Troubleshooting</h1>

<p>While setting up a fresh installation of <a href="https://cachyos.org/">CachyOS</a> this weekend, the key import via WKD failed:</p>

<blockquote class="callout callout-aside">
  <p>I use Arch BTW!</p>
</blockquote>

<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>gpg-wks-client <span class="nt">--check</span> <span class="nt">-v</span> <span class="nt">--debug</span><span class="o">=</span>ipc mail@domain.tld
<span class="go">
</span><span class="gp">gpg-wks-client: DBG: chan_3 &lt;- #</span><span class="w"> </span>Home: /home/&lt;user&gt;/.gnupg
<span class="gp">gpg-wks-client: DBG: chan_3 &lt;- #</span><span class="w"> </span>Config: /home/&lt;user&gt;/.gnupg/dirmngr.conf
<span class="go">gpg-wks-client: DBG: chan_3 &lt;- OK Dirmngr 2.4.9 at your service, process 5107
gpg-wks-client: DBG: connection to the dirmngr established
</span><span class="gp">gpg-wks-client: DBG: chan_3 -&gt;</span><span class="w"> </span>WKD_GET <span class="nt">--</span> mail@domain.tld
<span class="go">gpg-wks-client: DBG: chan_3 &lt;- S SOURCE https://openpgpkey.domain.tld
</span><span class="gp">gpg-wks-client: DBG: chan_3 &lt;- ERR 1 General error &lt;Unspecified source&gt;</span><span class="w">
</span><span class="gp">gpg-wks-client: DBG: chan_3 -&gt;</span><span class="w"> </span>BYE
<span class="go">gpg-wks-client: error looking up 'mail@domain.tld' via WKD: General error
</span></code></pre></div></div>

<p>The error message revealed nothing useful. The thing called dirmngr (spoken: dirmanager) is a tool that comes with GnuPG. It is responsible for sending or receiving information about public keys, like getting them from a keyserver or via WKD.</p>

<p>If you want to analyze what is going wrong with dirmngr, you can create <a href="https://wiki.gnupg.org/TroubleShooting/DebugWithDirmngr">log files</a>.</p>

<p><code class="language-plaintext highlighter-rouge">~/.gnupg/dirmngr.conf</code>:</p>

<div class="language-config highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">log</span>-<span class="n">file</span> /<span class="n">home</span>/&lt;<span class="n">user</span>&gt;/<span class="n">dirmngr</span>.<span class="n">log</span>
<span class="n">verbose</span>
<span class="n">debug</span> <span class="n">dns</span>,<span class="n">network</span>,<span class="n">lookup</span>
</code></pre></div></div>

<p>The debug log below reveals the issue. This is what you can observe in the log:</p>

<ul>
  <li>dirmngr first attempts to locate the public key via the advanced method</li>
  <li>dirmngr successfully resolves <code class="language-plaintext highlighter-rouge">openpgpkey.domain.tld</code>, but fails to verify the certificate</li>
  <li>dirmngr skips the direct method as fallback</li>
</ul>

<p>The Problem: Despite <strong>no</strong> CNAME configuration, dirmngr successfully resolves <code class="language-plaintext highlighter-rouge">openpgpkey.domain.tld</code>. In consequence, dirmngr now skips the direct method as fallback.</p>

<p><code class="language-plaintext highlighter-rouge">~/dirmngr.log</code>:</p>

<div class="language-config highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">enabled</span> <span class="n">debug</span> <span class="n">flags</span>: <span class="n">dns</span> <span class="n">network</span> <span class="n">lookup</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">permanently</span> <span class="n">loaded</span> <span class="n">certificates</span>: <span class="m">145</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]:     <span class="n">runtime</span> <span class="n">cached</span> <span class="n">certificates</span>: <span class="m">0</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]:            <span class="n">trusted</span> <span class="n">certificates</span>: <span class="m">145</span> (<span class="m">145</span>,<span class="m">0</span>,<span class="m">0</span>,<span class="m">0</span>)
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">handler</span> <span class="n">for</span> <span class="n">fd</span> <span class="m">5</span> <span class="n">terminated</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">handler</span> <span class="n">for</span> <span class="n">fd</span> <span class="m">5</span> <span class="n">started</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">connection</span> <span class="n">from</span> <span class="n">process</span> <span class="m">152242</span> (<span class="m">1000</span>:<span class="m">1000</span>)
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>: <span class="n">dns</span>: <span class="n">libdns</span> <span class="n">initialized</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>: <span class="n">dns</span>: <span class="n">resolve_dns_name</span>(<span class="n">openpgpkey</span>.<span class="n">domain</span>.<span class="n">tld</span>): <span class="n">Success</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>: <span class="n">Using</span> <span class="n">TLS</span> <span class="n">library</span>: <span class="n">GNUTLS</span> <span class="m">3</span>.<span class="m">8</span>.<span class="m">11</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">detected</span> <span class="n">interfaces</span>: <span class="n">IPv4</span> <span class="n">IPv6</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>: <span class="n">http</span>.<span class="n">c</span>:<span class="n">connect_server</span>: <span class="n">trying</span> <span class="n">name</span>=<span class="s1">'openpgpkey.domain'</span> <span class="n">port</span>=<span class="m">443</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>: <span class="n">dns</span>: <span class="n">resolve_dns_name</span>(<span class="n">openpgpkey</span>.<span class="n">domain</span>.<span class="n">tld</span>): <span class="n">Success</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>: <span class="n">http</span>.<span class="n">c</span>:<span class="m">2893</span>:<span class="n">socket_new</span>: <span class="n">object</span> <span class="m">0</span><span class="n">x00007fc370fb3190</span> <span class="n">for</span> <span class="n">fd</span> <span class="m">6</span> <span class="n">created</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">TLS</span> <span class="n">verification</span> <span class="n">of</span> <span class="n">peer</span> <span class="n">failed</span>: <span class="n">status</span>=<span class="m">0</span><span class="n">x0042</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">TLS</span> <span class="n">verification</span> <span class="n">of</span> <span class="n">peer</span> <span class="n">failed</span>: <span class="n">The</span> <span class="n">certificate</span> <span class="n">is</span> <span class="n">NOT</span> <span class="n">trusted</span>. <span class="n">The</span> <span class="n">certificate</span> <span class="n">issuer</span> <span class="n">is</span> <span class="n">unknown</span>.-
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">TLS</span> <span class="n">verification</span> <span class="n">of</span> <span class="n">peer</span> <span class="n">failed</span>: <span class="n">hostname</span> <span class="n">does</span> <span class="n">not</span> <span class="n">match</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>: <span class="n">expected</span> <span class="n">hostname</span>: <span class="n">openpgpkey</span>.<span class="n">domain</span>.<span class="n">tld</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>: <span class="n">BEGIN</span> <span class="n">Certificate</span> <span class="s1">'server[0]'</span>:
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>:      <span class="n">serial</span>: <span class="m">058715</span><span class="n">B14819765A73941B66A6DE1364</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>:   <span class="n">notBefore</span>: <span class="m">2026</span>-<span class="m">01</span>-<span class="m">31</span> <span class="m">08</span>:<span class="m">07</span>:<span class="m">11</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>:    <span class="n">notAfter</span>: <span class="m">2027</span>-<span class="m">01</span>-<span class="m">31</span> <span class="m">08</span>:<span class="m">07</span>:<span class="m">11</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>:      <span class="n">issuer</span>: <span class="n">CN</span>=<span class="n">TRAEFIK</span> <span class="n">DEFAULT</span> <span class="n">CERT</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>:     <span class="n">subject</span>: <span class="n">CN</span>=<span class="n">TRAEFIK</span> <span class="n">DEFAULT</span> <span class="n">CERT</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>:         <span class="n">aka</span>: (<span class="m">8</span>:<span class="n">dns</span>-<span class="n">name81</span>:<span class="m">86</span><span class="n">be942d4d7104eebd8a6783e066e9e8</span>.<span class="m">24</span><span class="n">bed133f6aabc1d2182b35f2fa76289</span>.<span class="n">traefik</span>.<span class="n">default</span>)
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>:   <span class="n">hash</span> <span class="n">algo</span>: <span class="m">1</span>.<span class="m">2</span>.<span class="m">840</span>.<span class="m">113549</span>.<span class="m">1</span>.<span class="m">1</span>.<span class="m">11</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>:   <span class="n">SHA1</span> <span class="n">fingerprint</span>: <span class="n">D118CC76203D39481C4C38639A32887234105343</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">DBG</span>: <span class="n">END</span> <span class="n">Certificate</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">TLS</span> <span class="n">connection</span> <span class="n">authentication</span> <span class="n">failed</span>: <span class="n">General</span> <span class="n">error</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">error</span> <span class="n">connecting</span> <span class="n">to</span> <span class="s1">'https://openpgpkey.domain.tld/.well-known/openpgpkey/domain.tld/hu/dizb37aqa5h4skgu7jf1xjr4q71w4paq?l=mail'</span>: <span class="n">General</span> <span class="n">error</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">command</span> <span class="s1">'WKD_GET'</span> <span class="n">failed</span>: <span class="n">General</span> <span class="n">error</span> &lt;<span class="n">Unspecified</span> <span class="n">source</span>&gt;
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">5</span>]: <span class="n">handler</span> <span class="n">for</span> <span class="n">fd</span> <span class="m">5</span> <span class="n">terminated</span>
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">0</span>]: <span class="n">SIGTERM</span> <span class="n">received</span> - <span class="n">shutting</span> <span class="n">down</span> ...
<span class="n">dirmngr</span>[<span class="m">142016</span>.<span class="m">0</span>]: <span class="n">dirmngr</span> (<span class="n">GnuPG</span>) <span class="m">2</span>.<span class="m">4</span>.<span class="m">9</span> <span class="n">stopped</span>
</code></pre></div></div>

<h1 id="the-solution">The Solution</h1>

<p>I never found out why dirmngr successfully resolves the <code class="language-plaintext highlighter-rouge">openpgpkey.domain.tld</code> DNS without a CNAME configuration. Investigating would require more time than I had available. So, I switched to the advanced method instead.</p>

<p>I already have a <a href="https://github.com/Tooa/tooa.github.io">personal user repository</a> hosting my website with a custom CNAME <code class="language-plaintext highlighter-rouge">domain.tld</code>. Github Pages do not support multiple CNAME records (<code class="language-plaintext highlighter-rouge">domain.tld</code> and <code class="language-plaintext highlighter-rouge">openpgpkey.domain.tld</code>) for this personal user site repository (<code class="language-plaintext highlighter-rouge">&lt;username&gt;.github.io</code>). So I created a dedicated <a href="https://github.com/Tooa/openpgpkey">openpgpkey</a> Github Pages repository hosting the WKD for advanced method discovery.</p>

<p>I then configured an additional CNAME record via my domain service provider that redirects <code class="language-plaintext highlighter-rouge">openpgpkey.domain.tld</code> to <code class="language-plaintext highlighter-rouge">&lt;username&gt;.github.io</code>. That way, <code class="language-plaintext highlighter-rouge">domain.tld</code> still resolves to my website and <code class="language-plaintext highlighter-rouge">openpgpkey.domain.tld</code> to the WKD.</p>

<p>In conclusion, switching to the advanced method solved (worked around) the problem:</p>

<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>gpg-wks-client <span class="nt">--check</span> <span class="nt">-v</span> mail@domain.tld
<span class="go">
gpg-wks-client: public key for 'mail@domain.tld' found via WKD
</span></code></pre></div></div>]]></content><author><name>Uli Fahrer</name></author><category term="pgp" /><summary type="html"><![CDATA[How I debugged and fixed OpenPGP WKD key discovery failures. The solution involved switching from the WDK direct method to the WDK advanced method using a dedicated GitHub Pages repository.]]></summary></entry></feed>