验证 NSEC3 记录
Verifying NSEC3 records
我正在摆弄 DNSSEC,我想尝试验证 dnssec-signzone
从 bind9-utils
生成的 NSEC3 记录(我认为是有效的)。这是我的区域文件:
$ORIGIN dnssectest.mvolfik.tk.
$TTL 120
@ SOA dnssectestns.mvolfik.tk. email.example.com. 15 259200 3600 300000 3600
A 192.168.0.101
s3c A 192.168.0.101
$INCLUDE zsk.key
$INCLUDE ksk.key
ZSK和KSK用dnssec-keygen -a ECDSAP256SHA256 dnssectest.mvolfik.tk.
生成(分别加-f KSK
)
然后我使用命令 dnssec-signzone -3 deadbeef -H 5 -o dnssectest.mvolfik.tk -k ksk.key zonefile zsk.key
对其进行签名(使用 NSEC3 和 deadbeef
十六进制盐,5 次迭代)
我在 zonefile.signed
中得到了以下 NSEC3 记录:(省略了 RRSIG 和 DNSKEY 无关;A 和 SOA 没有改变)
0 NSEC3PARAM 1 0 5 DEADBEEF
F66KKS17FM851AVA4EARFHS55I3TOO85.dnssectest.mvolfik.tk. 3600 IN NSEC3 1 0 5 DEADBEEF (
D60TA5J5RS4JD5AQK25B1BCUAHGP4DHC
A SOA RRSIG DNSKEY NSEC3PARAM )
D60TA5J5RS4JD5AQK25B1BCUAHGP4DHC.dnssectest.mvolfik.tk. 3600 IN NSEC3 1 0 5 DEADBEEF (
F66KKS17FM851AVA4EARFHS55I3TOO85
A RRSIG )
既然我知道这个区域中唯一的域是 s3c.dnssectest.mvolfik.tk.
和 dnssectest.mvolfik.tk.
,我假设以下 Python 脚本会得到与 signe 中相同的哈希值上面的区域文件:(来自 RFC 5155 中的伪代码)
import hashlib
def ih(salt, x, k):
if k == 0:
return hashlib.sha1(x + salt).digest()
return hashlib.sha1(ih(salt, x, k-1) + salt).digest()
print(ih(bytes.fromhex("deadbeef"), b"s3c.dnssectest.mvolfik.tk.", 5).hex())
print(ih(bytes.fromhex("deadbeef"), b"dnssectest.mvolfik.tk.", 5).hex())
然而,我却得到了 b58374998347ba833ab33f15332829a589a80d82
和 545e01397a776ee73aa0372aea015408cc384574
。我做错了什么?
所以我查看了 dnspython
源代码,找到了 nsec3_hash 函数。事实证明,该名称必须采用有线格式(意味着删除点并在标签前添加一个长度字节 - \x03s3c\x10dnssectest\x07mvolfik\x02tk\x00
等,末尾为空字节)。并且结果使用 base32 (0-9A-V) 编码,而不是十六进制。使用 dnspython 库可能更容易,但这里是完整的(有点天真)代码:
import hashlib, base64
b32_trans = str.maketrans(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", "0123456789ABCDEFGHIJKLMNOPQRSTUV"
)
def ih(salt, x, k):
if k == 0:
return hashlib.sha1(x + salt).digest()
return hashlib.sha1(ih(salt, x, k - 1) + salt).digest()
def nsec3(salt, name, k):
if not name.endswith("."):
name += "."
labels = name.split(".")
name_wire = b"".join(len(l).to_bytes(1, "big") + l.lower().encode() for l in labels)
digest = ih(bytes.fromhex(salt), name_wire, k)
return base64.b32encode(digest).decode().translate(b32_trans)
print(nsec3("deadbeef", "dnssectest.mvolfik.tk.", 5))
print(nsec3("deadbeef", "s3c.dnssectest.mvolfik.tk.", 5))
这会得到在 NSEC3 记录中看到的正确哈希值
我正在摆弄 DNSSEC,我想尝试验证 dnssec-signzone
从 bind9-utils
生成的 NSEC3 记录(我认为是有效的)。这是我的区域文件:
$ORIGIN dnssectest.mvolfik.tk.
$TTL 120
@ SOA dnssectestns.mvolfik.tk. email.example.com. 15 259200 3600 300000 3600
A 192.168.0.101
s3c A 192.168.0.101
$INCLUDE zsk.key
$INCLUDE ksk.key
ZSK和KSK用dnssec-keygen -a ECDSAP256SHA256 dnssectest.mvolfik.tk.
生成(分别加-f KSK
)
然后我使用命令 dnssec-signzone -3 deadbeef -H 5 -o dnssectest.mvolfik.tk -k ksk.key zonefile zsk.key
对其进行签名(使用 NSEC3 和 deadbeef
十六进制盐,5 次迭代)
我在 zonefile.signed
中得到了以下 NSEC3 记录:(省略了 RRSIG 和 DNSKEY 无关;A 和 SOA 没有改变)
0 NSEC3PARAM 1 0 5 DEADBEEF
F66KKS17FM851AVA4EARFHS55I3TOO85.dnssectest.mvolfik.tk. 3600 IN NSEC3 1 0 5 DEADBEEF (
D60TA5J5RS4JD5AQK25B1BCUAHGP4DHC
A SOA RRSIG DNSKEY NSEC3PARAM )
D60TA5J5RS4JD5AQK25B1BCUAHGP4DHC.dnssectest.mvolfik.tk. 3600 IN NSEC3 1 0 5 DEADBEEF (
F66KKS17FM851AVA4EARFHS55I3TOO85
A RRSIG )
既然我知道这个区域中唯一的域是 s3c.dnssectest.mvolfik.tk.
和 dnssectest.mvolfik.tk.
,我假设以下 Python 脚本会得到与 signe 中相同的哈希值上面的区域文件:(来自 RFC 5155 中的伪代码)
import hashlib
def ih(salt, x, k):
if k == 0:
return hashlib.sha1(x + salt).digest()
return hashlib.sha1(ih(salt, x, k-1) + salt).digest()
print(ih(bytes.fromhex("deadbeef"), b"s3c.dnssectest.mvolfik.tk.", 5).hex())
print(ih(bytes.fromhex("deadbeef"), b"dnssectest.mvolfik.tk.", 5).hex())
然而,我却得到了 b58374998347ba833ab33f15332829a589a80d82
和 545e01397a776ee73aa0372aea015408cc384574
。我做错了什么?
所以我查看了 dnspython
源代码,找到了 nsec3_hash 函数。事实证明,该名称必须采用有线格式(意味着删除点并在标签前添加一个长度字节 - \x03s3c\x10dnssectest\x07mvolfik\x02tk\x00
等,末尾为空字节)。并且结果使用 base32 (0-9A-V) 编码,而不是十六进制。使用 dnspython 库可能更容易,但这里是完整的(有点天真)代码:
import hashlib, base64
b32_trans = str.maketrans(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", "0123456789ABCDEFGHIJKLMNOPQRSTUV"
)
def ih(salt, x, k):
if k == 0:
return hashlib.sha1(x + salt).digest()
return hashlib.sha1(ih(salt, x, k - 1) + salt).digest()
def nsec3(salt, name, k):
if not name.endswith("."):
name += "."
labels = name.split(".")
name_wire = b"".join(len(l).to_bytes(1, "big") + l.lower().encode() for l in labels)
digest = ih(bytes.fromhex(salt), name_wire, k)
return base64.b32encode(digest).decode().translate(b32_trans)
print(nsec3("deadbeef", "dnssectest.mvolfik.tk.", 5))
print(nsec3("deadbeef", "s3c.dnssectest.mvolfik.tk.", 5))
这会得到在 NSEC3 记录中看到的正确哈希值