Can "TLS-ALPN-01" be done with .NET?

Hello

I’m having a problem with using “TLS-ALPN-01”.

Our client doesn’t want to open port 80 and we don’t have access to the DNS, so the only way there is using “TLS-ALPN-01” I think.

I have read on my support sites that this is difficult or can’t be done.

Can you please tell me if this is doable?

My scenario is that I have a .Net Core 7 Windows application that requests certificates from “Let’s Encrypt” and add it to the relevant IIS website.

Currently, this is what I’m doing, but I’m getting the result status “Invalid” all the time.

Here is my code:

        IOrderContext order = await acmeContextService.NewOrder(new[] { certificateInfo.Domain });

        IAuthorizationContext auth = (await order.Authorizations()).First();

        IChallengeContext challengeContext = null;
        string challengeKey = null;
        string challengeToken = null;

        if (certificateInfo.UseHttpsForCertificateReplacement)
        {
            // HTTPS: Compute key authorization for tls-alpn-01
            challengeContext = await auth.TlsAlpn();
            challengeKey = challengeContext.KeyAuthz;
            challengeToken = challengeContext.Token;
        }
        else
        {
            // Comptue key authorization for http-01 
            challengeContext = await auth.Http();
            challengeKey = challengeContext.KeyAuthz;
            challengeToken = challengeContext.Token;
        }

        // Create a file and put it inside the website directory structure.
        CreateCertificateChallengeToken(challengeToken, challengeKey);

        // Remove URL Rewrite Rules.
        RemoveURLRewriteRules();

        DisableIISRedirection();

        if (certificateInfo.UseHttpsForCertificateReplacement)
        {
            // Checks if the token can be loaded in the URL. 
            // If it doesn't it will throw an exception.
            await CheckIfTokenCanBeReached(challengeToken, challengeKey, 0, true);
        }
        else
        {
            await CheckIfTokenCanBeReached(challengeToken, challengeKey, 0, false);
        }

        var result = await challengeContext.Validate();

        int attempts = 6;
        while (attempts > 0 && result.Status == ChallengeStatus.Pending || result.Status == ChallengeStatus.Processing)
        {
            result = await challengeContext.Resource();

            if (result.Status == ChallengeStatus.Pending || result.Status == ChallengeStatus.Processing)
            {
                System.Threading.Thread.Sleep(10000);
            }
        }

result.Status is “INVALID”.

Thanks in advance for your help.

1 Like

Skimmed the code I can't see the part to change certificate given to client by alpn negotiation.

2 Likes

Yes dotnet can (but the original .net framework cannot). The only dotnet client I know of that does offer tls-alpn-01 is win-acme: win-acme/src/main.lib/Plugins/ValidationPlugins/Tls/SelfHosting/SelfHosting.cs at master · win-acme/win-acme · GitHub

Usually the bigger problem is getting exclusive access to port 443, which is one of the reasons we don't implement it in Certify The Web.

Personally I would suggest that you should either use a certificate provided to you by the client (pulling it from a keyvault etc) or use DNS validation via a CNAME to a domain you control, and make your DNS challenge updates there. There are other options like acme-dns.

Alternatively, simply convince the client to open port 80 (you can be the only listener, and only during validation) - there is a sense that port 80 itself is a security problem but it's just a number after all, the real security is more around what's listening on the port.

4 Likes

Hi, I'm not sure what you mean by your comment.

This code works like this when I use "auth.Http()" (when certificateInfo.UseHttpsForCertificateReplacement is FALSE).

What other code do I need for "auth.TlsAlpn()"?
I can't find much on how to do this.

Yes, I totally agree that is unnecessary. And that port 80 is totally fine. However, this is a government client which doesn't want port 80 open. And I can't do anything about it. I also don't have access to their DNS, so I cannot use that option either.

Pro-tip: the dns-01 challenge is also possible using DNS "redirection" using e.g. CNAME or NS resource records, which would only require a one-time zone modification.

3 Likes

Doesn't this require access to the owner of whom own the domain? And that is our client and not us, so that won't work either.

This (in the Certes library you are using) retrieves the TlsAlpn challenge information, it does not perform the challenge response.

What's supposed to happen with an acme challenge is that you decide which challenge you want to complete for each identifier (domain), then you setup whatever is necessary on your side to complete that challenge, then you tell the acme CA that you're ready for them to check your answer. If you complete a challenge for the domains on your order you can then proceed to finalizing your certificate request and download your certificate.

You are currently doing nothing that would actually answer the challenge (e.g. set up something to listen on port 443 and perform the necessary TLS coversation to satisfy tls-alpn-01, which the example code I linked to does.

I would suggest you perhaps use win-acme directly to acquire your certificate.

3 Likes

Yes, but that could be a one-time thing only, redirecting the _acme-challenge label to a DNS server under your own control. Once the CNAME or NS redirection is in place, all the recurring adding/removing of the TXT RRs for the challenge could be done on that DNS server under your control. For example, acme-dns is a popular (?) solution for this problem.

5 Likes

Ok, I will ask the client if they mind doing this. Thank you for your help.

2 Likes

Usually the bigger problem is getting exclusive access to port 443,
which is one of the reasons we don't implement it in Certify The Web.

I don't think you need exclusive access. You could use nginx to
redirect to different services depending on the ALPN protocol
identifier.

For example I'm using the following nginx config to allow SSH to also
work via port 443, next to a regular nginx for serving HTTPS:

stream {
    upstream web {
        server 127.0.0.1:81;
    }

    upstream ssh {
        server 127.0.0.1:22;
    }

    map $ssl_preread_protocol $upstream {
        default web;
        "" ssh;
    }

    server {
        listen *:443;
        listen [::]:443;

        proxy_pass $upstream;
        ssl_preread on;
    }
}

(I need this since I'm sometimes in hotel networks that do not allow
connections to port 22, but are fine with connections to port 443...)

I'm sure this can be adapted for use with TLS-ALPN-01 so that you never
need to disconnect the web server from port 443, but I haven't tried it
yet...

This is on windows and I assume IIS is present, if you were going to switch web server then caddy would make more sense because it can do tls-alpn-01 automatically.

4 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.