DNS: Générer un enregistrement TLSA

Article publié, le et modifié le
4 minutes de lecture

Cet article contient 747 mots.
Source brute de l'article :

Description

Du fait de gérer moi-même la zone DNS pour mon propre ndd, dans le but “d’implémenter” DNSSEC.

Je commence maintenant à m’intéresser aux enregistrements DANE. Pour l’instant, je ne génère qu’un enregistrement pour le port 443/tcp - service web. Mais il est possible d’en générer pour d’autres services, tel que 25/tcp qui correspond à smtp, d’autres protocoles que tcp, etc…

Créer un enregistrement TLSA n’est pas compliqué !

Pour le propos, un enregistrement TLSA est ainsi construit :

_numeroDePort._protocol.domain. IN TLSA usage selecteur correspondance certificatAssocie

Veuillez consulter la documentation de l’Afnic, ci-dessous, pour bien comprendre le propos.

Génération TLSA en shell

Pour générer un enregistrement TLSA en shell, c’est assez simple en somme :

openssl x509 -noout -pubkey -in /etc/ssl/acme/nomDeDomaine.cert.pem | openssl rsa -pubin -outform der 2>/dev/null | sha512 | tr "a-z" "A-Z"

Dans un premier temps :

  • on choisit l’algo, soit sha256, soit sha512
  • on choisit de n’utiliser tout le certificat ou juste la clé publique de celui-ci
algo="sha512"
domain="stephane-huc.net"
cert="/etc/ssl/acme/${domain}.cert.pem"    # chemin vers le fichier cert SSL généré par LetsEncrypt, avec le client natif OpenBSD - acme secure !

tls_port=443
tls_proto="tcp"

tlsa_usage=3    # Usage Field; possible: 0 => 3
tlsa_selector=0 # Selector Field; 0: use cert; 1: use public key
tlsa_method=0   # Matching Type Field


case "${algo}" in
    "sha256") tlsa_method=1 ;;
    "sha512") tlsa_method=2 ;;
esac


#tlsa_cert_associated="$(openssl x509 -in "${cert}" -outform der | openssl $algo | cut -d ' ' -f2)"
tlsa_cert_associated="$(openssl x509 -noout -pubkey -in "${cert}" | openssl rsa -pubin -outform der 2>/dev/null | ${algo} | tr "a-z" "A-Z" )"

tlsa_record="_${tls_port}._${tls_proto}.${domain}. IN TLSA ${tlsa_usage} ${tlsa_selector} ${tlsa_method} ${tlsa_cert_associated}"

# ajout de l'enregistrement vers votre zone dns ; dans ce cas, l'enregistrement de cette dernière est dû au fait de l'outil ldnscript de @"22decembre" !
echo "${tlsa_record}" >> /etc/ns/${domain}

Version multi-domaines

Voici le même code shell, pour gérer plusieurs domaines, pour exemple :

#!/bin/sh

algo=sha256
#cert="/etc/ssl/acme/${domain}.cert.pem"
domains="yeuxdelibad.net 3hg.fr ouaf.xyz"

tls_proto="tcp"
tls_ports="443"

tlsa_usage=3    # Usage Field; possible: 0 => 3
tlsa_selector=1 # Selector Field; 0: use cert; 1: use public key
tlsa_method=0   # Matching Type Field

case "${algo}" in
    "sha256") tlsa_method=1 ;;
    "sha512") tlsa_method=2 ;;
esac

for domain in ${domains}; do
    cert="etc/ssl/acme/${domain}.cert.pem"
    tlsa_cert_associated="$(openssl x509 -noout -pubkey -in "${cert}" | openssl rsa -pubin -outform der 2>/dev/null | ${algo} | tr "a-z" "A-Z" )"
    
    for port in ${tls_ports}; do
        tlsa_record="_${port}._${tls_proto}.${domain}. IN TLSA ${tlsa_usage} ${tlsa_selector} ${tlsa_method} ${tlsa_cert_associated}"
        echo "${tlsa_record}" >> "/etc/ns/${domain}"
    done
    
    echo "You should update your ${domain} zone in 48h" | mail -s "${domain} TLSA update" root
    
    sleep 1
    
    # reload zone and nsd ; à décommenter, si vous utiliser nsd ;)
    #/var/ldns/ldnscript signing "${domain}"
done

Merci Xavier ;)

Générer TLSA en PHP

Générer un enregistrement TLSA en PHP n’est guère plus compliqué, à partir du moment où on a les bonnes infos !

On veille à récupérer le certificat au format PEM, à l’injecter dans un formulaire HTML et à traiter les informations reçues par celui-ci. Je ne décris pas le code HTML.

<?php
function extractPublicKey($cert) {
    $certificate = openssl_x509_read($cert);
    $pub_key = openssl_get_publickey($certificate);
    $pub_key = openssl_pkey_get_details($pub_key);
    if (!isset($pub_key['key'])) {
      // @codeCoverageIgnoreStart
      throw new \InvalidArgumentException('Provided certificate is invalid.');
      // @codeCoverageIgnoreEnd
    }
    return $pub_key['key'];
}

function pem2der($pem) {
    return base64_decode(str_replace(["\n", "\r"], '', $pem));
}

function stripDatas($pem) {
    $datas = [
      '-----BEGIN PUBLIC KEY-----',
      '-----END PUBLIC KEY-----',
      '-----BEGIN CERTIFICATE-----',
      '-----END CERTIFICATE-----'
    ];
    $data = str_replace($datas, '', $pem);
    return trim($data);
}

# $datas = certificat au format PEM
$pkey = extractPublicKey($datas);
$pem = pem2der(stripDatas($pkey));
    
switch($tlsa_match) {
    case 0: $data = $pem; break;
    case 1: $data = hash('sha256', $pem, true); break;
    case 2: $data = hash('sha512', $pem, true); break;
        }
    
$tlsa_cert_associated = bin2hex($data);

# les variables $port, $protocol, $domain ainsi que les $tlsa_* sont des variables créées dans le contexte du formulaire HTML, et nommées très simplement selon leur contexte.
$tlsa_gen = "_".$port."._".$protocol.".".$domain.". IN TLSA ".$tlsa_usage." ".$tlsa_selector." ".$tlsa_match." ".$tlsa_cert_associated;

# pour finir, un echo là où vous le désirez dans votre code HTML de restitution...
echo $tlsa_gen
?>

Les fonctions dans le code ne sont pas de moi - je partage ni + ni -

Vérification de l’enregistrement TLSA

Il existe des modules à Firefox :

On peut le faire par l’usage de générateur TLSA en ligne - puis comparer les infos restituées avec celles que vous avez générées - malheureusement rien d’automati(que|sé) :

Documentation

L’afnic a écrit des articles intéressants à-propos de :

Stéphane Bortzmeyer aussi parle de la RFC 6698 et explique beaucoup de choses intéressantes !

Un autre article expliquant de manière claire, simple DANE-TLSA.


Voilà !