{"record":{"id":"d72ef06a4c6e02eb","repo":"TechnitiumSoftware/DnsServer","slug":"dns-server-tls-certificate-file-must-contain-a-cer","errorCode":null,"errorMessage":"DNS Server TLS certificate file must contain a certificate with private key.","messagePattern":"DNS Server TLS certificate file must contain a certificate with private key\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/DnsServer.cs","lineNumber":1589,"sourceCode":"\n                default:\n                    throw new ArgumentException(\"DNS Server TLS certificate file must be PKCS #12 formatted with .pfx or .p12 extension: \" + tlsCertificatePath);\n            }\n\n            X509Certificate2Collection certificateCollection = X509CertificateLoader.LoadPkcs12CollectionFromFile(tlsCertificatePath, tlsCertificatePassword, X509KeyStorageFlags.PersistKeySet);\n            X509Certificate2 serverCertificate = null;\n\n            foreach (X509Certificate2 certificate in certificateCollection)\n            {\n                if (certificate.HasPrivateKey)\n                {\n                    serverCertificate = certificate;\n                    break;\n                }\n            }\n\n            if (serverCertificate is null)\n                throw new ArgumentException(\"DNS Server TLS certificate file must contain a certificate with private key.\");\n\n            SslStreamCertificateContext certificateContext = SslStreamCertificateContext.Create(serverCertificate, certificateCollection, false);\n\n            _dotSslServerAuthenticationOptions = new SslServerAuthenticationOptions()\n            {\n                ServerCertificateContext = certificateContext\n            };\n\n            _doqSslServerAuthenticationOptions = new SslServerAuthenticationOptions()\n            {\n                ApplicationProtocols = _doqApplicationProtocols,\n                ServerCertificateContext = certificateContext\n            };\n\n            List<SslApplicationProtocol> applicationProtocols = new List<SslApplicationProtocol>();\n\n            if (_enableDnsOverHttp3)\n                applicationProtocols.Add(new SslApplicationProtocol(\"h3\"));","sourceCodeStart":1571,"sourceCodeEnd":1607,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/DnsServer.cs#L1571-L1607","documentation":"Thrown by LoadDnsTlsCertificate after loading the PKCS #12 collection when no certificate in the bundle has a private key. The server iterates the collection looking for HasPrivateKey; TLS servers must present a cert with its private key, so a keyless bundle is unusable.","triggerScenarios":"The .pfx contains only the leaf certificate or a CA chain but not the end-entity private key (common when exporting without 'include private key'), or the key was stripped for security during distribution.","commonSituations":"Exporting from Windows cert store without checking 'Yes, export the private key'; generating a CA bundle instead of an end-entity bundle; using a public cert downloaded from the CA website rather than the one created with the CSR.","solutions":["Re-export the .pfx and explicitly include the private key (Windows: 'Yes, export the private key').","Rebuild with openssl including -inkey: openssl pkcs12 -export -in cert.pem -inkey privkey.pem -out cert.pfx.","Confirm privkey.pem corresponds to the certificate's public key (modulus match) before bundling.","If the key lives in an HSM/KMS, use a tool that exports a key-bearing pfx, or supply a different bundle."],"exampleFix":"# before: openssl pkcs12 -export -in cert.pem -out cert.pfx   (no -inkey)\n\n# after\nopenssl pkcs12 -export -in cert.pem -inkey privkey.pem -out cert.pfx -password pass:secret","handlingStrategy":"validation","validationCode":"bool PfxHasPrivateKey(string path, string pass)\n{\n    using var col = new X509Certificate2Collection();\n    col.Import(path, pass, X509KeyStorageFlags.DefaultKeySet);\n    return col.OfType<X509Certificate2>().Any(c => c.HasPrivateKey);\n}","typeGuard":"static bool CertBundleContainsPrivateKey(string pfxPath, string password)\n{\n    var col = new X509Certificate2Collection();\n    col.Import(pfxPath, password ?? string.Empty, X509KeyStorageFlags.DefaultKeySet);\n    try { return col.Cast<X509Certificate2>().Any(c => c.HasPrivateKey); }\n    finally { foreach (var c in col) c.Dispose(); }\n}","tryCatchPattern":"try { server.SetDnsTlsCertificate(path, pass, throwException: true); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"private key\")) { log.Error(\"Re-export the .pfx including the private key\"); }","preventionTips":["Build .pfx with -inkey in openssl.","When exporting from Windows, select 'Yes, export the private key'.","Verify HasPrivateKey before configuring the server."],"tags":["tls","certificate","private-key","pkcs12"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}