Generate dns-01 TXT challenge with ACME-python

I am new to SSL certificate installation. I am trying to do the dns-01 validation using acme-python library. Below is the code I am using. I have added the TXT record also in domain, but in answer challenge i am getting error as "acme.messages.Error: urn:ietf:params:acme:error:malformed :: The request message was malformed :: Unable to update challenge :: authorization must be pending"

Can anyone please help what I am missing.

My domain is:grid1234.fanvase.com

I ran below commands in same order:

Register User.

  1. user_key = josepy.JWKRSA(key=rsa.generate_private_key(public_exponent=65537,key_size=2048,backend=default_backend()))
  2. net = client.ClientNetwork(user_key, user_agent=USER_AGENT)
    3)directory = messages.Directory.from_json(net.get(DIRECTORY_URL).json())
    4)client_acme = client.ClientV2(directory, net=net)
    5)account_created = messages.NewRegistration.from_data(email=(EMAIL), terms_of_service_agreed=True)
    6)regr = client_acme.new_account(account_created)

Generate CSR

7)pkey = OpenSSL.crypto.PKey()
8)pkey.generate_key(OpenSSL.crypto.TYPE_RSA, CERT_PKEY_BITS)
9)pkey_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM,pkey)
10)csr_pem = crypto_util.make_csr(pkey_pem, [domain])

Create New Order

  1. orderr = client_acme.new_order(csr_pem)
  2. challenge = None
  3. authz_list = orderr.authorizations
    14)for authz in authz_list:
    for i in authz.body.challenges:
    if isinstance(i.chall, challenges.DNS01):
    challenge = i
  4. response, validation = challenge.response_and_validation(client_acme.net.key)
  5. After adding the generated TXT record in my DNS and waited till propagated. I ran below command
    client_acme.answer_challenge(challenge, response)

Then I got an error saying "acme.messages.Error: urn:ietf:params:acme:error:malformed :: The request message was malformed :: Unable to update challenge :: authorization must be pending"

My web server is (include version):

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

My hosting provider, if applicable, is:

I can login to a root shell on my machine (yes or no, or I don't know):

I'm using a control panel to manage my site (no, or provide the name and version of the control panel):

The version of my client is (e.g. output of certbot --version or certbot-auto --version if you're using Certbot):

Hello @Nagendra, welcome to the Let's Encrypt community. :slightly_smiling_face:

Yes using this online tool https://unboundtest.com/ I see your DNS TXT record
https://unboundtest.com/m/TXT/_acme-challenge.grid1234.fanvase.com/ZQKNWTIK

That DNS TXT record contents need to change base off of the challenge; it is not constant.

Can you show the "malformed URI"?

2 Likes

Your code does not appear to be checking the status of the authorizations returned. The error message is basically telling you that the authorization isn't in the right state to answer the challenge. If you had previously successfully validated a challenge, it is now probably cached and the status of those authorizations is valid when you create a new order for the same names.

4 Likes

@Bruce5051 & @rmbolger Thanks for the info. I am able to issue a certificate with your update.
I have a couple of doubts, it would be great if you will be able to give some heads up.

  1. Do I need to register the customer each time (with same Email) to form client_acme (in above code)
  2. When should I do the client_acme.answer_challenge(challenge, response). Apparently, I am able to do only one time and getting error if I execute the same command second time (if not successful in the first time).
  3. Is there a way if I can customise "_acme-challenge" to a custom value.
  4. I am generating SSL for my client domains. So, I want to implement this (Onboarding Your Customers with Let's Encrypt and ACME - Let's Encrypt). Can you point me in the right direction to achieve this. Below is what I am planning to do as per the documentation.
    1. Once customer gives his domain, generate a unique UUID for the domain and store it in my DB and ask the customer to add a CNAME record with name ".<client_domain>" and value "<my_domain>". (if possible to give custom key else will ask to add "_acme-challenge.<client_domain>")
    2. Once customer confirms he added the CNAME, check whether the CNAME is added or not using some python packages. (Not sure how to do this. would be great if you have any inputs)
    3. Request for challenge from ACME.
    4. Add the TXT record in my DNS as ".<client_domain>" and value generate by the challenge

As I am beginner with the process, you may find some of the questions are basic. But, would really appreciate your help in this regard.

2 Likes

It sounds like you need to review the actual ACME spec, RFC 8555, to get a better understanding of how the protocol works and how you should be using it in your code. It answers a lot of your questions in more detail than I'll try to give below.

No, you should be saving the account key you generated and subsequent account metadata so you can re-use it with future orders.

Whenever an authorization object associated with an order has status pending

No, that's a fundamental part of the spec. Though there is a draft spec that is still in development that could offer an account specific FQDN alternative.

Yes, having your customers create a CNAME to a DNS record you control will allow you to authorize certificates on their behalf.

The TXT record goes wherever they have their CNAME pointing to. You won't want to have all of your customers point their CNAME to the same place in your DNS zone.

5 Likes