Complete DNSSEC support including signing, validation, key generation, and all standard algorithms.
const (
RSAMD5 uint8 = 1 // RSA/MD5 (deprecated)
DH uint8 = 2 // Diffie-Hellman
DSA uint8 = 3 // DSA/SHA1
RSASHA1 uint8 = 5 // RSA/SHA-1
DSANSEC3SHA1 uint8 = 6 // DSA-NSEC3-SHA1
RSASHA1NSEC3SHA1 uint8 = 7 // RSASHA1-NSEC3-SHA1
RSASHA256 uint8 = 8 // RSA/SHA-256
RSASHA512 uint8 = 10 // RSA/SHA-512
ECCGOST uint8 = 12 // GOST R 34.10-2001
ECDSAP256SHA256 uint8 = 13 // ECDSA Curve P-256 with SHA-256
ECDSAP384SHA384 uint8 = 14 // ECDSA Curve P-384 with SHA-384
ED25519 uint8 = 15 // Ed25519
ED448 uint8 = 16 // Ed448
INDIRECT uint8 = 252 // Reserved for Indirect Keys
PRIVATEDNS uint8 = 253 // Private algorithm
PRIVATEOID uint8 = 254 // Private algorithm OID
)const (
SHA1 uint8 = 1 // SHA-1 (RFC 4034)
SHA256 uint8 = 2 // SHA-256 (RFC 4509)
GOST94 uint8 = 3 // GOST R 34.11-94 (RFC 5933)
SHA384 uint8 = 4 // SHA-384 (Experimental)
SHA512 uint8 = 5 // SHA-512 (Experimental)
)const (
SEP = 1 // Secure Entry Point
REVOKE = 1 << 7 // Key has been revoked
ZONE = 1 << 8 // Zone Key flag
)// AlgorithmToString maps algorithm IDs to names
var AlgorithmToString = map[uint8]string{
RSAMD5: "RSAMD5",
DH: "DH",
DSA: "DSA",
RSASHA1: "RSASHA1",
DSANSEC3SHA1: "DSA-NSEC3-SHA1",
RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1",
RSASHA256: "RSASHA256",
RSASHA512: "RSASHA512",
ECCGOST: "ECC-GOST",
ECDSAP256SHA256: "ECDSAP256SHA256",
ECDSAP384SHA384: "ECDSAP384SHA384",
ED25519: "ED25519",
ED448: "ED448",
INDIRECT: "INDIRECT",
PRIVATEDNS: "PRIVATEDNS",
PRIVATEOID: "PRIVATEOID",
}
// AlgorithmToHash maps algorithms to crypto.Hash
var AlgorithmToHash = map[uint8]crypto.Hash{
RSAMD5: crypto.MD5,
DSA: crypto.SHA1,
RSASHA1: crypto.SHA1,
RSASHA1NSEC3SHA1: crypto.SHA1,
RSASHA256: crypto.SHA256,
ECDSAP256SHA256: crypto.SHA256,
ECDSAP384SHA384: crypto.SHA384,
RSASHA512: crypto.SHA512,
ED25519: 0, // ED25519 does its own hashing
}
// HashToString maps hash IDs to names
var HashToString = map[uint8]string{
SHA1: "SHA1",
SHA256: "SHA256",
GOST94: "GOST94",
SHA384: "SHA384",
SHA512: "SHA512",
}
// StringToAlgorithm maps names to algorithm IDs
var StringToAlgorithm = map[string]uint8{
"RSAMD5": RSAMD5,
"DH": DH,
"DSA": DSA,
"RSASHA1": RSASHA1,
"DSA-NSEC3-SHA1": DSANSEC3SHA1,
"RSASHA1-NSEC3-SHA1": RSASHA1NSEC3SHA1,
"RSASHA256": RSASHA256,
"RSASHA512": RSASHA512,
"ECC-GOST": ECCGOST,
"ECDSAP256SHA256": ECDSAP256SHA256,
"ECDSAP384SHA384": ECDSAP384SHA384,
"ED25519": ED25519,
"ED448": ED448,
"INDIRECT": INDIRECT,
"PRIVATEDNS": PRIVATEDNS,
"PRIVATEOID": PRIVATEOID,
}
// StringToHash maps names to hash IDs
var StringToHash = map[string]uint8{
"SHA1": SHA1,
"SHA256": SHA256,
"GOST94": GOST94,
"SHA384": SHA384,
"SHA512": SHA512,
}// KeyTag calculates the keytag (key ID) of DNSKEY
func (k *DNSKEY) KeyTag() uint16
// ToDS converts DNSKEY to DS record with specified hash
func (k *DNSKEY) ToDS(h uint8) *DS
// ToCDNSKEY converts DNSKEY to CDNSKEY record
func (k *DNSKEY) ToCDNSKEY() *CDNSKEY
// Generate generates a new private key for the DNSKEY
// bits: key size in bits (e.g., 2048 for RSA, 256 for ECDSA P-256)
// Returns private key suitable for signing
func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error)
// NewPrivateKey parses private key from BIND-style string format
// s: private key string
// Returns parsed private key
func (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error)
// ReadPrivateKey reads BIND-style private key from reader
// q: reader containing private key
// file: filename for error reporting
// Returns parsed private key
func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error)
// PrivateKeyString converts private key to BIND-style string format
// p: private key to convert
// Returns BIND-style private key string
func (k *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string// ToCDS converts DS to CDS record
func (ds *DS) ToCDS() *CDS// GeneratePrivateKey generates a DNSSEC private key
// algorithm: encryption algorithm (RSASHA256, ECDSAP256SHA256, ED25519, etc.)
// bits: key size in bits (e.g., 2048 for RSA, 256 for ECDSA P-256)
func GeneratePrivateKey(algorithm uint8, bits int) (crypto.PrivateKey, error)// Sign signs an RRSet with the provided private key
// The RRSIG must have Inception, Expiration, KeyTag, SignerName, and Algorithm set
// If OrigTtl is zero, it uses the TTL from the RRset
func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error
// Verify verifies the signature on an RRSet
func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error
// ValidityPeriod checks if signature is valid at time t
func (rr *RRSIG) ValidityPeriod(t time.Time) bool// Sign creates SIG(0) signature for message
func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error)
// Verify verifies SIG(0) signature on message
func (rr *SIG) Verify(k *KEY, msg []byte) error// Cover checks if NSEC3 record covers the name
func (rr *NSEC3) Cover(name string) bool
// Match checks if NSEC3 record matches the name
func (rr *NSEC3) Match(name string) bool
// HashName hashes a domain name for NSEC3
// name: domain name to hash
// hash: hash algorithm
// iterations: number of iterations
// salt: hex encoded salt
func HashName(name string, hash uint8, iterations uint16, salt string) string// Generate RSA key
rsaKey, err := dns.GeneratePrivateKey(dns.RSASHA256, 2048)
if err != nil {
log.Fatal(err)
}
// Generate ECDSA P-256 key
ecdsaKey, err := dns.GeneratePrivateKey(dns.ECDSAP256SHA256, 256)
if err != nil {
log.Fatal(err)
}
// Generate Ed25519 key
ed25519Key, err := dns.GeneratePrivateKey(dns.ED25519, 256)
if err != nil {
log.Fatal(err)
}import (
"crypto/rsa"
"encoding/base64"
)
// Assuming you have a private key
privKey := rsaKey.(*rsa.PrivateKey)
pubKeyBytes, _ := pubKeyToBytes(privKey.PublicKey)
dnskey := &dns.DNSKEY{
Hdr: dns.RR_Header{
Name: "example.com.",
Rrtype: dns.TypeDNSKEY,
Class: dns.ClassINET,
Ttl: 3600,
},
Flags: dns.ZONE | dns.SEP, // Zone key with SEP flag (KSK)
Protocol: 3,
Algorithm: dns.RSASHA256,
PublicKey: base64.StdEncoding.EncodeToString(pubKeyBytes),
}
// Calculate key tag
keyTag := dnskey.KeyTag()
fmt.Printf("Key tag: %d\n", keyTag)// Create DS record with SHA-256
ds := dnskey.ToDS(dns.SHA256)
fmt.Printf("DS record: %s\n", ds.String())
// Output: example.com. 3600 IN DS 12345 8 2 ABC123...// Create RRset to sign
rrset := []dns.RR{
&dns.A{
Hdr: dns.RR_Header{
Name: "example.com.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 3600,
},
A: net.ParseIP("192.0.2.1"),
},
&dns.A{
Hdr: dns.RR_Header{
Name: "example.com.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 3600,
},
A: net.ParseIP("192.0.2.2"),
},
}
// Create RRSIG
now := time.Now()
rrsig := &dns.RRSIG{
Hdr: dns.RR_Header{
Name: "example.com.",
Rrtype: dns.TypeRRSIG,
Class: dns.ClassINET,
Ttl: 3600,
},
TypeCovered: dns.TypeA,
Algorithm: dns.RSASHA256,
Labels: 2,
OrigTtl: 3600,
Expiration: uint32(now.Add(30 * 24 * time.Hour).Unix()),
Inception: uint32(now.Unix()),
KeyTag: dnskey.KeyTag(),
SignerName: "example.com.",
}
// Sign the RRset
err := rrsig.Sign(privKey.(crypto.Signer), rrset)
if err != nil {
log.Fatal(err)
}
fmt.Printf("RRSIG: %s\n", rrsig.String())// Verify RRSIG
err := rrsig.Verify(dnskey, rrset)
if err != nil {
log.Printf("Signature verification failed: %v", err)
} else {
log.Println("Signature valid")
}
// Check validity period
if rrsig.ValidityPeriod(time.Now()) {
log.Println("Signature is currently valid")
} else {
log.Println("Signature expired or not yet valid")
}nsec3 := &dns.NSEC3{
Hdr: dns.RR_Header{
Name: "abc123.example.com.",
Rrtype: dns.TypeNSEC3,
Class: dns.ClassINET,
Ttl: 3600,
},
Hash: dns.SHA1,
Flags: 0,
Iterations: 10,
Salt: "AABBCCDD",
NextDomain: "def456",
TypeBitMap: []uint16{dns.TypeA, dns.TypeRRSIG},
}
// Check if name is covered
if nsec3.Cover("www.example.com.") {
log.Println("Name is covered by NSEC3")
}
// Check if name matches
if nsec3.Match("www.example.com.") {
log.Println("Name matches NSEC3")
}// Hash a name for NSEC3
salt := "AABBCCDD"
iterations := uint16(10)
hash := dns.HashName("www.example.com.", dns.SHA1, iterations, salt)
fmt.Printf("Hashed name: %s\n", hash)nsec := &dns.NSEC{
Hdr: dns.RR_Header{
Name: "example.com.",
Rrtype: dns.TypeNSEC,
Class: dns.ClassINET,
Ttl: 3600,
},
NextDomain: "www.example.com.",
TypeBitMap: []uint16{
dns.TypeA,
dns.TypeNS,
dns.TypeSOA,
dns.TypeRRSIG,
dns.TypeNSEC,
dns.TypeDNSKEY,
},
}// Create SIG(0) for message authentication
m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("example.com"), dns.TypeA)
sig := &dns.SIG{
RRSIG: dns.RRSIG{
Hdr: dns.RR_Header{
Name: ".",
Rrtype: dns.TypeSIG,
Class: dns.ClassANY,
Ttl: 0,
},
TypeCovered: 0,
Algorithm: dns.RSASHA256,
Labels: 0,
OrigTtl: 0,
Expiration: uint32(time.Now().Add(5 * time.Minute).Unix()),
Inception: uint32(time.Now().Unix()),
KeyTag: key.KeyTag(),
SignerName: "example.com.",
},
}
// Sign message
signedMsg, err := sig.Sign(privKey.(crypto.Signer), m)
if err != nil {
log.Fatal(err)
}// Zone signing workflow
zone := "example.com."
zsk, _ := dns.GeneratePrivateKey(dns.RSASHA256, 2048) // Zone Signing Key
ksk, _ := dns.GeneratePrivateKey(dns.RSASHA256, 4096) // Key Signing Key
// Create DNSKEY records
zskDNSKEY := createDNSKEY(zone, zsk, dns.ZONE)
kskDNSKEY := createDNSKEY(zone, ksk, dns.ZONE|dns.SEP)
// Create DS from KSK
ds := kskDNSKEY.ToDS(dns.SHA256)
// Sign DNSKEY RRset with KSK
dnskeyRRset := []dns.RR{zskDNSKEY, kskDNSKEY}
dnskeyRRSIG := createRRSIG(zone, dns.TypeDNSKEY, kskDNSKEY.KeyTag())
err := dnskeyRRSIG.Sign(ksk.(crypto.Signer), dnskeyRRset)
// Sign other RRsets with ZSK
aRRset := []dns.RR{/* ... */}
aRRSIG := createRRSIG(zone, dns.TypeA, zskDNSKEY.KeyTag())
err = aRRSIG.Sign(zsk.(crypto.Signer), aRRset)// KSK rollover: double signature method
oldKSK, _ := loadKey("Kexample.com.+008+12345")
newKSK, _ := dns.GeneratePrivateKey(dns.RSASHA256, 4096)
oldKSKDNSKEY := createDNSKEY(zone, oldKSK, dns.ZONE|dns.SEP)
newKSKDNSKEY := createDNSKEY(zone, newKSK, dns.ZONE|dns.SEP)
// Publish both keys
dnskeyRRset := []dns.RR{zskDNSKEY, oldKSKDNSKEY, newKSKDNSKEY}
// Sign with both KSKs
oldRRSIG := createRRSIG(zone, dns.TypeDNSKEY, oldKSKDNSKEY.KeyTag())
oldRRSIG.Sign(oldKSK.(crypto.Signer), dnskeyRRset)
newRRSIG := createRRSIG(zone, dns.TypeDNSKEY, newKSKDNSKEY.KeyTag())
newRRSIG.Sign(newKSK.(crypto.Signer), dnskeyRRset)
// Publish both DS records at parent
oldDS := oldKSKDNSKEY.ToDS(dns.SHA256)
newDS := newKSKDNSKEY.ToDS(dns.SHA256)// TimeToString converts Unix timestamp to DNSSEC time format (YYYYMMDDHHmmSS)
func TimeToString(t uint32) string
// StringToTime converts DNSSEC time format to Unix timestamp
func StringToTime(s string) (uint32, error)// Convert current time to DNSSEC format
now := uint32(time.Now().Unix())
timeStr := dns.TimeToString(now)
fmt.Printf("DNSSEC time: %s\n", timeStr) // e.g., "20240115120000"
// Parse DNSSEC time
timestamp, err := dns.StringToTime("20240115120000")
if err != nil {
log.Fatal(err)
}var (
ErrAlg error // Bad algorithm
ErrKey error // Bad key
ErrKeyAlg error // Bad key algorithm
ErrKeySize error // Bad key size
ErrNoSig error // No signature found
ErrPrivKey error // Bad private key
ErrSig error // Bad signature
)