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
- 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.
- Rename only if the file is already PKCS #12 but mislabeled; otherwise convert.
- Re-export from your CA tooling choosing the .pfx option.
- 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
- Always convert PEM to PKCS #12 before configuring.
- Standardize on .pfx in your provisioning scripts.
- Reject non-pfx uploads in the UI before submission.
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Web server '{_name}' TLS certificate file must be PKCS #12 f
- Web server '{_name}' TLS certificate file must contain a cer
- DNS Server TLS certificate file must contain a certificate w
- Web Service TLS certificate file must be PKCS #12 formatted
- Web Service TLS certificate file must contain a certificate
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/a4170a3a61fc0ad1.
Report an issue: GitHub.