Une ligne suffit pour lire la date d’expiration d’un certificat distant

La date d’expiration d’un certificat TLS se lit avec openssl en une seule ligne, sans navigateur. Le tube enchaîne deux programmes : openssl s_client ouvre la connexion et récupère le certificat du serveur, puis openssl x509 en extrait les dates et les écrit sur la sortie standard. La documentation OpenSSL décrit l’option -dates comme l’affichage des deux dates du certificat, notBefore et notAfter, et l’option -enddate comme le seul notAfter. Sur un certificat valide, la réponse arrive en moins d’une seconde sous la forme notAfter=Oct 27 21:29:51 2026 GMT. Le code de sortie reste 0 tant que le certificat est lisible, ce qui rend la ligne directement utilisable dans un script de surveillance. Deux pièges attendent pourtant celui qui l’écrit de mémoire : un hôte injoignable produit un message qui parle de certificat, et l’absence de redirection de l’entrée standard fait patienter la commande indéfiniment.
Sommaire
- Récupérer le certificat, puis lire son notAfter, en trois gestes
- Quatre options à coller après le tube, et la valeur que chacune rend
- Un hôte injoignable produit une erreur de certificat, pas une erreur réseau
- Sans redirection de l’entrée standard, s_client attend une frappe au clavier
- L’option -dateopt n’existe pas sous OpenSSL 1.1.1, donc pas sous Debian 11
- Un certificat expiré s’affiche quand même, avec le code 10
- Six situations que le code de sortie tranche seul
Récupérer le certificat, puis lire son notAfter, en trois gestes
Le tube transmet le certificat tel que le serveur l’envoie. La documentation OpenSSL présente s_client comme un client TLS générique et x509 comme l’utilitaire qui imprime la date d’expiration du certificat lu sur l’entrée standard. Aucun fichier intermédiaire n’est nécessaire, et rien n’est écrit sur le disque.
-
Vérifier la version d’OpenSSL présente sur le poste.
openssl versionCe que vous devez voir : une ligne qui commence par le mot OpenSSL et se poursuit par trois nombres séparés par des points.
-
Enchaîner la connexion et la lecture des dates sur une seule ligne.
openssl s_client -connect example.org:443 -servername example.org </dev/null 2>/dev/null | openssl x509 -noout -datesCe que vous devez voir : deux lignes, notBefore= puis notAfter=, chacune suivie d’une date terminée par GMT.
-
Contrôler le code de sortie de la ligne précédente, sans rien lancer entre-temps.
echo $?Ce que vous devez voir : 0.
L’option -servername est facultative depuis OpenSSL 1.1.1, publié le 11 septembre 2018 : le manuel du projet indique que l’extension SNI est remplie automatiquement avec le nom passé à -connect lorsqu’il a la forme d’un nom DNS. La garder rend la ligne lisible et couvre le cas d’une connexion par adresse IP, où aucun nom DNS n’est fourni au serveur.
Quatre options à coller après le tube, et la valeur que chacune rend
La seconde moitié du tube change selon la question posée. Quatre options d’openssl x509 couvrent la lecture des dates, l’extraction du seul notAfter, un verdict préparé pour un script et le contrôle du nom d’hôte. Les valeurs de la colonne de droite proviennent du manuel openssl-x509 et de la page de manuel Debian du paquet openssl.
| Commande après le tube | Valeur rendue | Code de sortie | Version OpenSSL minimale |
|---|---|---|---|
openssl x509 -noout -dates |
notBefore= et notAfter=, en heures GMT |
0 | 1.1.1 |
openssl x509 -noout -enddate |
notAfter= seul |
0 | 1.1.1 |
openssl x509 -noout -checkend 2592000 |
Certificate will not expire |
0 au-delà de 30 jours, 1 en deçà | 1.1.1 |
openssl x509 -noout -enddate -dateopt iso_8601 |
notAfter=2026-10-27 21:29:51Z |
0 | 3.0 |
La valeur 2592000 correspond à 30 jours exprimés en secondes, l’unité qu’attend -checkend. La documentation OpenSSL rappelle que le format de date par défaut reste rfc_822, et que -dateopt accepte la valeur iso_8601 à côté de rfc_822.
Un hôte injoignable produit une erreur de certificat, pas une erreur réseau
Quand la connexion TCP échoue, openssl x509 reçoit une entrée standard vide et répond sur le sujet du certificat, pas sur celui du réseau. OpenSSL 3.1.4 écrit alors le message suivant, relevé sur une adresse de test non routable.
Could not read certificate from <stdin>
La cause réelle se trouve de l’autre côté du tube. openssl s_client nomme la fonction fautive sur sa sortie d’erreur, avec les mentions BIO_connect et calling connect(). Le code de sortie vaut 1, comme pour un certificat illisible.
openssl s_client -connect example.org:443 -servername example.org </dev/null
Ce que vous devez voir : les lignes Certificate chain et Verify return code, ou l’erreur réseau d’openssl s_client. Retirer la redirection 2>/dev/null rend ce diagnostic visible. Un refus de connexion franc relève d’un diagnostic réseau, pas de la cryptographie.
Sans redirection de l’entrée standard, s_client attend une frappe au clavier
Le manuel d’openssl-s_client consacre une note entière à l’usage non interactif. Il précise que s_client lancé depuis une tâche planifiée ou un script sans entrée standard valide peut fermer la connexion prématurément, en particulier avec TLS 1.3, et recommande l’option -ign_eof pour y remédier. Sans aucune redirection, la commande reste ouverte sur la session et rien ne s’affiche.
openssl s_client -connect example.org:443 -servername example.org </dev/null 2>/dev/null | openssl x509 -noout -enddate
La redirection </dev/null ferme l’entrée et déclenche la fin de session. Le même manuel avertit que -ign_eof seul peut bloquer indéfiniment si le serveur attend que le client ferme la connexion le premier, ce qui arrive avec SMTP ou IMAP. La combinaison </dev/null sans -ign_eof reste la plus sûre pour une lecture de dates.
Ce que vous devez voir : l’invite de commandes revient en moins de 5 secondes, avec une seule ligne notAfter= en sortie.
L’option -dateopt n’existe pas sous OpenSSL 1.1.1, donc pas sous Debian 11
Le format de date par défaut reste rfc_822, et l’option -dateopt accepte la valeur iso_8601 depuis OpenSSL 3.0. La page de manuel Debian du paquet openssl 1.1.1n-0+deb11u4 ne la mentionne pas, alors que les pages officielles des branches 3.0 et 3.5 la listent. Le code source de la branche 1.1.1 confirme l’absence de cette entrée dans la table des options du programme x509. Un script écrit pour Debian 12 ne se rabat donc pas sur le format par défaut sous Debian 11 : la commande échoue et renvoie vers son message d’aide.
openssl s_client -connect example.org:443 -servername example.org </dev/null 2>/dev/null | openssl x509 -noout -enddate -dateopt iso_8601
Ce que vous devez voir : notAfter=2026-10-27 21:29:51Z sous OpenSSL 3.0 et suivants, et une date du type notAfter=Oct 27 21:29:51 2026 GMT sous OpenSSL 1.1.1.
Selon le paquet tracker Debian, la branche openssl 3.0.20-1~deb12u2 équipe Debian 12, la branche openssl 3.5.7-1~deb13u2 équipe Debian 13 et la branche openssl 1.1.1w-0+deb11u8 équipe Debian 11. Selon Launchpad, Ubuntu 24.04 reçoit openssl 3.0.13-0ubuntu3.15. Trois de ces quatre distributions acceptent donc -dateopt, la plus ancienne non. Convertir la date à la main reste possible avec date -d “$(openssl x509 -noout -enddate | cut -d= -f2)”.
Un certificat expiré s’affiche quand même, avec le code 10
openssl s_client poursuit la poignée de main après une erreur de vérification. Le manuel du projet l’assume et le présente comme un outil de test qui accepte toute chaîne, fiable ou non. Les dates restent donc lisibles sur un certificat expiré, ce qui est précisément ce qu’on cherche, mais le code de vérification ne doit pas être confondu avec le code de sortie de la ligne.
verify error:num=10:certificate has expired
Verify return code: 10 (certificate has expired)
Verify return code: 21 (unable to verify the first certificate)
Le code 10 signale une date dépassée, le code 21 une chaîne incomplète faute d’autorité intermédiaire. Dans les deux cas, openssl x509 imprime le notAfter, car la lecture des dates ne dépend pas de la confiance accordée à la chaîne.
Ce que vous devez voir : la date réelle du certificat, même avec un code de vérification non nul. L’option -verify_return_error inverse ce comportement et interrompt la poignée de main au premier échec.
Six situations que le code de sortie tranche seul
Faut-il payer un service pour être prévenu avant l’expiration ?
Non : une tâche planifiée et la ligne de cet article suffisent. Placez la commande avec -checkend 604800, soit 7 jours en secondes, dans un cron quotidien et testez le code de sortie : 1 déclenche l’alerte, 0 ne fait rien. Le même enchaînement se range dans un timer systemd si la machine n’a pas de cron.
-checkend ou -enddate, lequel mettre dans un script ?
-checkend, sans hésiter. Il rend un verdict binaire et un code de sortie, alors que -enddate oblige à comparer deux chaînes de caractères dans le format rfc_822. Pour un seuil de 15 jours, la valeur est 1296000. Gardez -enddate pour l’affichage humain, -checkend pour la décision.
Comment surveiller quarante domaines avec une seule commande ?
Une boucle lit le fichier et relance le tube pour chaque nom. Le code de sortie de chaque tour est accumulé, puis affiché en fin de parcours.
while read h; do
openssl s_client -connect "$h:443" -servername "$h" </dev/null 2>/dev/null | openssl x509 -noout -checkend 604800
done < domaines.txt
La sortie affiche un certificat qui n’est pas celui du site, que faire ?
Un antivirus ou un proxy d’entreprise qui inspecte le TLS se place au milieu et présente son propre certificat. Le nom dans le bloc Certificate chain trahit l’interception. Comparez le champ issuer avec celui affiché par le navigateur, et ajoutez -showcerts pour voir la chaîne complète envoyée par le serveur.
Un certificat valide 90 jours, est-ce anormal ?
Non, c’est la durée standard actuelle. Selon Let’s Encrypt, le profil classic passe à 64 jours le 10 février 2027, puis à 45 jours le 16 février 2028, sous l’effet des exigences du CA/Browser Forum. Le profil tlsserver, lui, émet déjà des certificats de 45 jours depuis le 13 mai 2026. Une surveillance hebdomadaire reste donc utile.
La commande donne-t-elle le bon certificat sur un hébergement mutualisé ?
Oui, à condition de passer un nom et pas une adresse. Depuis OpenSSL 1.1.1, l’extension SNI est remplie automatiquement avec le nom donné à -connect s’il a la forme d’un nom DNS. Une connexion écrite avec l’adresse IP seule n’envoie aucun SNI et peut recevoir le certificat par défaut du serveur. Dans ce cas, ajoutez -servername avec le nom du site.
Sources : manuel openssl-s_client, documentation officielle OpenSSL, manuel openssl-x509, documentation officielle OpenSSL, page de manuel Debian du paquet openssl 1.1.1n-0+deb11u4, paquet tracker Debian, versions par suite, Let’s Encrypt, réduction des durées de validité, 2 décembre 2025, Launchpad, paquet openssl de la suite noble.