{"record":{"id":"0cef248f62f80793","repo":"TechnitiumSoftware/DnsServer","slug":"the-primary-zone-must-be-signed","errorCode":null,"errorMessage":"The primary zone must be signed.","messagePattern":"The primary zone must be signed\\.","errorType":"exception","errorClass":"DnsServerException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Zones/PrimaryZone.cs","lineNumber":1045,"sourceCode":"            {\n                deletedRecords.AddRange(zone.RemoveNSec3RecordsWithRRSig());\n\n                if (zone is SubDomainZone subDomainZone)\n                {\n                    if (zone.IsEmpty)\n                        _dnsServer.AuthZoneManager.RemoveSubDomainZone(zone.Name); //remove empty sub zone\n                    else\n                        subDomainZone.AutoUpdateState();\n                }\n            }\n\n            CommitAndIncrementSerial(deletedRecords);\n        }\n\n        public DnssecPrivateKey GenerateAndAddPrivateKey(DnssecPrivateKeyType keyType, DnssecAlgorithm algorithm, ushort rolloverDays, int keySize = -1)\n        {\n            if (_dnssecStatus == AuthZoneDnssecStatus.Unsigned)\n                throw new DnsServerException(\"The primary zone must be signed.\");\n\n            int i = 0;\n            while (i++ < 5)\n            {\n                DnssecPrivateKey privateKey = DnssecPrivateKey.Create(algorithm, keyType, keySize);\n                privateKey.RolloverDays = rolloverDays;\n\n                lock (_dnssecPrivateKeys)\n                {\n                    if (_dnssecPrivateKeys.TryAdd(privateKey.KeyTag, privateKey))\n                        return privateKey;\n                }\n            }\n\n            throw new DnsServerException(\"Failed to add private key: key tag collision. Please try again.\");\n        }\n\n        public void AddPrivateKey(DnssecPrivateKey privateKey)","sourceCodeStart":1027,"sourceCodeEnd":1063,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Zones/PrimaryZone.cs#L1027-L1063","documentation":"Thrown by GenerateAndAddPrivateKey when the zone's DNSSEC status is Unsigned. Private keys can only live on a signed zone because their DNSKEY material has nowhere to be published otherwise; the guard blocks creating keys against an unsigned zone.","triggerScenarios":"Calling GenerateAndAddPrivateKey(keyType, algorithm, rolloverDays) on a zone whose DnssecStatus is Unsigned.","commonSituations":"Provisioning keys before signing; an orchestration script that adds keys then signs, in the wrong order.","solutions":["Sign the zone first with SignZone, then call GenerateAndAddPrivateKey.","If you have keys you want to import, sign the zone with those keys in one SignZone call instead.","Guard the call with a DnssecStatus != Unsigned check."],"exampleFix":"// before\nvar key = zone.GenerateAndAddPrivateKey(DnssecPrivateKeyType.KeySigningKey, DnssecAlgorithm.ECDSAP256SHA256, 365);\n\n// after\nif (zone.DnssecStatus == AuthZoneDnssecStatus.Unsigned)\n    throw new InvalidOperationException(\"Sign the zone before generating keys.\");\nvar key = zone.GenerateAndAddPrivateKey(DnssecPrivateKeyType.KeySigningKey, DnssecAlgorithm.ECDSAP256SHA256, 365);","handlingStrategy":"validation","validationCode":"// Only generate keys on a signed zone\nif (zone.DnssecStatus == AuthZoneDnssecStatus.Unsigned)\n    zone.SignZone(initialKeys, dnsKeyTtl, useNSec3: false);\nvar key = zone.GenerateAndAddPrivateKey(keyType, algorithm, rolloverDays);","typeGuard":"static bool IsZoneSignedForKeys(ApexZone zone) =>\n    zone.DnssecStatus != AuthZoneDnssecStatus.Unsigned;","tryCatchPattern":"try\n{\n    key = zone.GenerateAndAddPrivateKey(keyType, algorithm, rolloverDays);\n}\ncatch (DnsServerException ex) when (ex.Message == \"The primary zone must be signed.\")\n{\n    zone.SignZone(initialKeys, dnsKeyTtl, useNSec3: false);\n    key = zone.GenerateAndAddPrivateKey(keyType, algorithm, rolloverDays);\n}","preventionTips":["Order provisioning as: sign -> generate/import keys -> publish -> activate.","Gate every key-management call behind a DnssecStatus != Unsigned check."],"tags":["dnssec","private-key","state-precondition","validation","csharp"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}