TechnitiumSoftware/DnsServer · error · ArgumentException

DNS Server TLS certificate file must be PKCS #12 formatted w

Error message

DNS Server TLS certificate file must be PKCS #12 formatted with .pfx or .p12 extension: {tlsCertificatePath}

What it means

Thrown by LoadDnsTlsCertificate when the file extension is neither .pfx nor .p12. The server only loads PKCS #12 bundles via X509CertificateLoader.LoadPkcs12CollectionFromFile; PEM/DER/CER are not accepted for DoT/DoH/DoQ.

Source

Thrown at DnsServerCore/Dns/DnsServer.cs:1573

                _tlsCertificateUpdateTimer = null;
            }
        }

        private void LoadDnsTlsCertificate(string tlsCertificatePath, string tlsCertificatePassword)
        {
            FileInfo fileInfo = new FileInfo(tlsCertificatePath);

            if (!fileInfo.Exists)
                throw new ArgumentException("DNS Server TLS certificate file does not exists: " + tlsCertificatePath);

            switch (Path.GetExtension(tlsCertificatePath).ToLowerInvariant())
            {
                case ".pfx":
                case ".p12":
                    break;

                default:
                    throw new ArgumentException("DNS Server TLS certificate file must be PKCS #12 formatted with .pfx or .p12 extension: " + tlsCertificatePath);
            }

            X509Certificate2Collection certificateCollection = X509CertificateLoader.LoadPkcs12CollectionFromFile(tlsCertificatePath, tlsCertificatePassword, X509KeyStorageFlags.PersistKeySet);
            X509Certificate2 serverCertificate = null;

            foreach (X509Certificate2 certificate in certificateCollection)
            {
                if (certificate.HasPrivateKey)
                {
                    serverCertificate = certificate;
                    break;
                }
            }

            if (serverCertificate is null)
                throw new ArgumentException("DNS Server TLS certificate file must contain a certificate with private key.");

            SslStreamCertificateContext certificateContext = SslStreamCertificateContext.Create(serverCertificate, certificateCollection, false);

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Convert the PEM cert + key into a PKCS #12 bundle: openssl pkcs12 -export -in cert.pem -inkey key.pem -out cert.pfx -password pass:secret.
  2. Rename only if the file is already PKCS #12 but mislabeled; otherwise convert.
  3. Re-export from your CA tooling choosing the .pfx option.
  4. For Let's Encrypt, use certbot with --deploy-hook that builds the .pfx on each renewal.

Example fix

# before: server.pem passed to SetDnsTlsCertificate

# after
openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem \
  -out dns.pfx -password pass:secret
# then server.SetDnsTlsCertificate("dns.pfx", "secret")
Defensive patterns

Strategy: validation

Validate before calling

static readonly HashSet<string> Pkcs12 = new(StringComparer.OrdinalIgnoreCase) { ".pfx", ".p12" };
bool IsPkcs12Path(string path) => Pkcs12.Contains(Path.GetExtension(path));

Type guard

static bool IsValidTlsCertExtension(string path)
{
    var ext = Path.GetExtension(path).ToLowerInvariant();
    return ext is ".pfx" or ".p12";
}

Try / catch

try { server.SetDnsTlsCertificate(path, pass, throwException: true); }
catch (ArgumentException ex) when (ex.Message.Contains("PKCS #12")) { log.Error("Convert the cert to a .pfx/.p12 bundle"); }

Prevention

When it happens

Trigger: Setting a TLS cert path whose extension is .pem, .crt, .cer, .key, or anything other than .pfx/.p12. The extension switch fires before any parse attempt.

Common situations: Admin generates a PEM cert chain (common with Let's Encrypt / certbot --pem) and points the server at it; copying a CA's .crt instead of the bundled .pfx; case variations are handled (ToLowerInvariant) but a missing or wrong extension is not.

Understand the failure class

Related errors


AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13). Data as JSON: /api/errors/a4170a3a61fc0ad1. Report an issue: GitHub.