DNS-01 validation getting "Correct value not found for DNS challenge"

My domain is:

I used ACMESharp to calculate the challenge and have now published the required TXT record. A dig query returns:

_acme-challenge.test-acme-dns-verification.suav.ec. 299 IN TXT "nSJOheM3ZP1F9UXtImAWjHdNKK5-Z9jc51bo1wpCzVA"
(see this dig output live).

Submitting the challenge to letsencrypt, it changes to invalid with an error:
Correct value not found for DNS challenge
(see challenge details, which is part of this authorization)

I do believe my TXT record to be correct. Using a web SHA256 tool I can see that
a SHA256 base64 hash of
is indeed
(the tool just shows + instead of -, as it’s not a Base64 URL)

Caveat - my DNS server is .NET library ARSoft.Tools.Net. Could it be anything with the formatting of the TXT entry or some other entries missing in the DNS query response? I understand that this code is responsible for the compare.

The operating system my web server runs on is (include version):
Windows 2016 Server

I can login to a root shell on my machine: yes.

I’m using a control panel to manage my site: no.

@ebekker Could you take a look? Does ACMESharp support DNS-01 challenges?

Thanks for help. Just to give what I know on the subject:

ACMESharp supports DNS-01 through its ‘manual’ provider, where upon running
Complete-ACMEChallenge -ChallengeType dns-01 -Handler manual ...
it just displays the the value to put in the TXT record.

So I really just used ACMESharp for that part and submission of these challenge details once I confirmed dig sees the TXT entry. I never suspected ACMESharp, since I assumed that the keyAuthorization in the challenge details is a valid one, already validated by Boulder (based on ‘If the client’s response is invalid for any reason … then the server MUST return an HTTP error.’ from The Spec). Am I wrong about that? If so, could Boulder return the expected one in the error details?

Alternatively, if keyAuthorization is always the validated expected one, it would be really great if Boulder included in the error details the values that were found and were not equal to the SHA256 of that one.

Yes, ACMESharp has supported DNS-01 and HTTP-01 from the start. @zlamma, I queried your DNS record that you quoted above and it appears to be resolving correctly.

If you’re using the “manual” challenge handler then it does not matter what your local web server or operating system are, as long as the DNS service is Internet-routable and publicly reachable, which it appears to be fine I was able to query it.

Are you running this against the PRODUCTION ACME servers, or STAGING?

@ebekker I believe I’m running this against PRODUCTION - I’m following the ACMESharp Quick start guide without specifying the environment and I already successfully completed a few HTTP-01 authorizations with it. I guess the URL of the challenge details also proves this.

I’m a bit at loss here. I should add that I’ve tried around 5 times before on other domain names with the same result.

It could be my DNS response implementation misbehaving only at the time of verification, or the matching code picks up some extra bytes for comparison, because the error message looks like the one from here, which means the txts must contain some response. It would really helpful if the error message included both sides of the comparison.

Is there a chance for adding this or is it considered a security concern?

Also, I’d like to establish my previous question clearly. When Boulders shows the keyAuthorization here, after I submitted it in the challenge response (using the ACMESharp’s Complete-ACMEChallenge), is it guaranteed to be correct, or is it merely the value that was received by Boulder?

@zlamma, can you try against the STAGING system for LE (https://letsencrypt.org/docs/staging-environment/). The ACMESharp PS module includes the staging endpoint as a pre-defined option for the service endpoint when you initialize the Vault (so you’ll need to setup an alternate Vault (see Vault Profiles)).

I completely expect you’ll get the same results, but “just in case”…

@jsha – I don’t suppose there is any way to get any more detail on the issue – I suppose it’s just getting back with a simple mismatch of the served up value from the TXT record, but I’m curious if there’s a way to see what LE sees in the response. Don’t suppose that’s accessible? Even in STAGE?

Also, just as a test, I wonder if you would go to the trouble of trying to setup a DNS subdomain with an alternate provider, like one of these free ones and see if you get different results. I’m curious if the problem is some subtle difference in the way it serves up TXT records, like spacing or quoting, though it doesn’t appear to be…

How long did you wait before updating your TXT record and completing the challenge? Are you sure that each of your nameservers (including anycast servers) was updated with that TXT record? A common problem with DNS challenges is submitting the challenge too soon, before all nameservers have the updated information.

@jsha . For 2 tests I have waited around 10hrs, while the TTL on the record is just 300s.

I only have that one DNS server on a single machine. Like said originally, it’s a bespoke DNS server using a .NET DNS library (this is a ‘work-in-progress’ website in a prototype stage).

@ebekker - yes I will try with some free hosting provider, but the whole point of my project is to host the DNS server, so I would be really keen to fix what is not working with my exact setup.

Ah, this wasn’t clear from your original message; I assumed ARSoft.Tools.Net was an established tool. I’m afraid Let’s Encrypt staff aren’t able to help you debug your DNS server, though you’re welcome to keep talking it through with other forum members if you like. I will mention that I noticed different results at different times. For instance, sometimes dig will show me a single NS record, and sometimes dig will show me several:

$ dig TXT _acme-challenge.test-acme-dns-verification.suav.ec

; <<>> DiG 9.10.3-P4-Ubuntu <<>> TXT _acme-challenge.test-acme-dns-verification.suav.ec
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17711
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 5

; EDNS: version: 0, flags:; udp: 4096
;_acme-challenge.test-acme-dns-verification.suav.ec. IN TXT

_acme-challenge.test-acme-dns-verification.suav.ec. 157 IN TXT "nSJOheM3ZP1F9UXtImAWjHdNKK5-Z9jc51bo1wpCzVA"

suav.ec.                129453  IN      NS      suav.ec.
suav.ec.                129453  IN      NS      ns4.suav.ec.
suav.ec.                129453  IN      NS      ns2.suav.ec.
suav.ec.                129453  IN      NS      ns3.suav.ec.

suav.ec.                129453  IN      A
ns3.suav.ec.            129453  IN      A
ns2.suav.ec.            129453  IN      A
ns4.suav.ec.            129453  IN      A

;; Query time: 8 msec
;; WHEN: Thu Oct 05 09:16:01 PDT 2017
;; MSG SIZE  rcvd: 267

Also, intentionally misspelling a record returns a seemingly related SPF record, which seems like an indicator of brokenness.

$ dig TXT _acme-challenge.xxxtest-acme-dns-verification.suav.ec

; <<>> DiG 9.10.3-P4-Ubuntu <<>> TXT _acme-challenge.xxxtest-acme-dns-verification.suav.ec
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28354
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 5

; EDNS: version: 0, flags:; udp: 4096
;_acme-challenge.xxxtest-acme-dns-verification.suav.ec. IN TXT

_acme-challenge.xxxtest-acme-dns-verification.suav.ec. 1800 IN TXT "v=spf1 a include:spf.efwd.registrar-servers.com ~all"

suav.ec.                129382  IN      NS      ns4.suav.ec.
suav.ec.                129382  IN      NS      ns3.suav.ec.
suav.ec.                129382  IN      NS      ns2.suav.ec.
suav.ec.                129382  IN      NS      suav.ec.

suav.ec.                129382  IN      A
ns3.suav.ec.            129382  IN      A
ns2.suav.ec.            129382  IN      A
ns4.suav.ec.            129382  IN      A

;; Query time: 164 msec
;; WHEN: Thu Oct 05 09:17:12 PDT 2017
;; MSG SIZE  rcvd: 279

Lastly, I’d recommend taking the config from https://unboundtest.com/ and running your own local Unbound server so you can easily test the behavior of your bespoke authoritative DNS server against a popular recursive resolver.


@jsha. OK! thanks for the staff’s time. I have finally found the cause.

Just to share lessons learned for resolving any similar problems, the issue turned out to be caused by text casing. The test using your suggested unboundtest.com did show that LetsEncrypt wasn’t seeing the right TXT record, because it seems to be using RaNDOm cASInG during the query (security measure for cache poisoning - intended, I presume), while, coincidentally, I fell into the trap of the library I am using, where the == operator I used happens to be passing false to the ignoreCase: parameter, unlike the object.Equals overload.

Apologies for the whole bother, but I did think that adding some more information to the error response is a sensible thing to do and it could save some time for the staff also. Anyways, over and out!

Thanks for all the Let’s Encrypt work!


I was not suggesting changing your whole setup permanently, just wanted to try a different variation to help narrow down the problem.

Glad it worked out! Good luck.

Thanks for the link to unboundtest.com! That’s a great troubleshooting resource!


6 posts were split to a new topic: SPF record wildcards and spam detection