Compare commits
4 commits
6d7f439642
...
05875b3bee
Author | SHA1 | Date | |
---|---|---|---|
05875b3bee | |||
589a8f7c4f | |||
2d7f449784 | |||
38db311931 |
4 changed files with 140 additions and 7 deletions
110
bin/dnssec
Executable file
110
bin/dnssec
Executable file
|
@ -0,0 +1,110 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from sys import argv
|
||||||
|
from os.path import realpath, dirname
|
||||||
|
from bundlewrap.repo import Repository
|
||||||
|
from base64 import b64decode, urlsafe_b64encode
|
||||||
|
from cryptography.utils import int_to_bytes
|
||||||
|
from cryptography.hazmat.primitives import serialization as crypto_serialization
|
||||||
|
from struct import pack, unpack
|
||||||
|
from hashlib import sha1, sha256
|
||||||
|
|
||||||
|
|
||||||
|
def long_to_base64(n):
|
||||||
|
return urlsafe_b64encode(int_to_bytes(n, None)).decode()
|
||||||
|
|
||||||
|
domain = argv[1]
|
||||||
|
repo = Repository(dirname(dirname(realpath(__file__))))
|
||||||
|
#repo = Repository('.')
|
||||||
|
|
||||||
|
flags = 256
|
||||||
|
protocol = 3
|
||||||
|
algorithm = 8
|
||||||
|
algorithm_name = 'RSASHA256'
|
||||||
|
|
||||||
|
# Private Key
|
||||||
|
|
||||||
|
priv = repo.libs.rsa.generate_deterministic_rsa_private_key(
|
||||||
|
b64decode(str(repo.vault.random_bytes_as_base64_for('dnssec' + domain)))
|
||||||
|
)
|
||||||
|
|
||||||
|
# https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateNumbers
|
||||||
|
# https://crypto.stackexchange.com/a/21104
|
||||||
|
public_exponent = priv.private_numbers().public_numbers.e
|
||||||
|
modulo = priv.private_numbers().public_numbers.n
|
||||||
|
private_exponent = priv.private_numbers().d
|
||||||
|
prime1 = priv.private_numbers().p
|
||||||
|
prime2 = priv.private_numbers().q
|
||||||
|
exponent1 = priv.private_numbers().dmp1
|
||||||
|
exponent2 = priv.private_numbers().dmq1
|
||||||
|
coefficient = priv.private_numbers().iqmp
|
||||||
|
|
||||||
|
print(f"""
|
||||||
|
Private-key-format: v1.3
|
||||||
|
Algorithm: {algorithm} ({algorithm_name})
|
||||||
|
Modulus: {long_to_base64(modulo)}
|
||||||
|
PublicExponent: {long_to_base64(public_exponent)}
|
||||||
|
PrivateExponent: {long_to_base64(private_exponent)}
|
||||||
|
Prime1: {long_to_base64(prime1)}
|
||||||
|
Prime2: {long_to_base64(prime2)}
|
||||||
|
Exponent1: {long_to_base64(exponent1)}
|
||||||
|
Exponent2: {long_to_base64(exponent2)}
|
||||||
|
Coefficient: {long_to_base64(coefficient)}
|
||||||
|
Created: 20230428110109
|
||||||
|
Publish: 20230428110109
|
||||||
|
Activate: 20230428110109
|
||||||
|
""")
|
||||||
|
|
||||||
|
# DNSKEY
|
||||||
|
|
||||||
|
pub = priv.public_key()
|
||||||
|
|
||||||
|
dnskey = ''.join(pub.public_bytes(
|
||||||
|
crypto_serialization.Encoding.PEM,
|
||||||
|
crypto_serialization.PublicFormat.SubjectPublicKeyInfo
|
||||||
|
).decode().split('\n')[1:-2])
|
||||||
|
|
||||||
|
value = f"{flags} {protocol} {algorithm} {dnskey}"
|
||||||
|
print(f"""
|
||||||
|
{domain}. IN DNSKEY {value}
|
||||||
|
""")
|
||||||
|
|
||||||
|
# DS
|
||||||
|
# https://gist.github.com/wido/4c6288b2f5ba6d16fce37dca3fc2cb4a#file-dnskey_to_dsrecord-py-L40
|
||||||
|
|
||||||
|
|
||||||
|
def _calc_ds(domain, flags, protocol, algorithm, dnskey):
|
||||||
|
if domain.endswith('.') is False:
|
||||||
|
domain += '.'
|
||||||
|
|
||||||
|
signature = bytes()
|
||||||
|
for i in domain.split('.'):
|
||||||
|
signature += pack('B', len(i)) + i.encode()
|
||||||
|
|
||||||
|
signature += pack('!HBB', int(flags), int(protocol), int(algorithm))
|
||||||
|
signature += b64decode(dnskey)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'sha1': sha1(signature).hexdigest().upper(),
|
||||||
|
'sha256': sha256(signature).hexdigest().upper(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _calc_keyid(flags, protocol, algorithm, dnskey):
|
||||||
|
st = pack('!HBB', int(flags), int(protocol), int(algorithm))
|
||||||
|
st += b64decode(dnskey)
|
||||||
|
|
||||||
|
cnt = 0
|
||||||
|
for idx in range(len(st)):
|
||||||
|
s = unpack('B', st[idx:idx+1])[0]
|
||||||
|
if (idx % 2) == 0:
|
||||||
|
cnt += s << 8
|
||||||
|
else:
|
||||||
|
cnt += s
|
||||||
|
|
||||||
|
return ((cnt & 0xFFFF) + (cnt >> 16)) & 0xFFFF
|
||||||
|
|
||||||
|
keyid = _calc_keyid(flags, protocol, algorithm, dnskey)
|
||||||
|
ds = _calc_ds(domain, flags, protocol, algorithm, dnskey)
|
||||||
|
|
||||||
|
print(f"{domain}. IN DS {str(keyid)} {str(algorithm)} 1 {ds['sha1'].lower()}")
|
||||||
|
print(f"{domain}. IN DS {str(keyid)} {str(algorithm)} 2 {ds['sha256'].lower()}")
|
26
bundles/bind/README.md
Normal file
26
bundles/bind/README.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
## DNSSEC
|
||||||
|
|
||||||
|
https://wiki.debian.org/DNSSEC%20Howto%20for%20BIND%209.9+#The_signing_part
|
||||||
|
https://blog.apnic.net/2021/11/02/dnssec-provisioning-automation-with-cds-cdnskey-in-the-real-world/
|
||||||
|
https://gist.github.com/wido/4c6288b2f5ba6d16fce37dca3fc2cb4a
|
||||||
|
|
||||||
|
```python
|
||||||
|
import dns.dnssec
|
||||||
|
algorithm = dns.dnssec.RSASHA256
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
import cryptography
|
||||||
|
pk = cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key(key_size=2048, public_exponent=65537)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nomenclature
|
||||||
|
|
||||||
|
### parent
|
||||||
|
|
||||||
|
DNSKEY:
|
||||||
|
the public key
|
||||||
|
|
||||||
|
DS
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
from os.path import join, exists
|
|
||||||
from re import sub
|
from re import sub
|
||||||
from cryptography.hazmat.primitives import serialization as crypto_serialization
|
from cryptography.hazmat.primitives import serialization as crypto_serialization
|
||||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# https://stackoverflow.com/a/18266970
|
# https://stackoverflow.com/a/18266970
|
||||||
|
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
from Crypto.Hash import HMAC
|
|
||||||
from struct import pack
|
from struct import pack
|
||||||
from hashlib import sha3_512
|
from hashlib import sha3_512
|
||||||
from cryptography.hazmat.primitives.serialization import load_der_private_key
|
from cryptography.hazmat.primitives.serialization import load_der_private_key
|
||||||
|
@ -23,12 +22,12 @@ class PRNG(object):
|
||||||
|
|
||||||
|
|
||||||
@cache_to_disk(30)
|
@cache_to_disk(30)
|
||||||
def _generate_deterministic_rsa_private_key(secret_bytes):
|
def _generate_deterministic_rsa_private_key(secret_bytes, key_size):
|
||||||
return RSA.generate(2048, randfunc=PRNG(secret_bytes)).export_key('DER')
|
return RSA.generate(key_size, randfunc=PRNG(secret_bytes)).export_key('DER')
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def generate_deterministic_rsa_private_key(secret_bytes):
|
def generate_deterministic_rsa_private_key(secret_bytes, key_size=2048):
|
||||||
return load_der_private_key(
|
return load_der_private_key(
|
||||||
_generate_deterministic_rsa_private_key(secret_bytes),
|
_generate_deterministic_rsa_private_key(secret_bytes, key_size),
|
||||||
password=None,
|
password=None,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue