HardStacked
Partager :

openvpn · openssl

OpenVPN « CA signature digest algorithm too weak » alors que ton CA est en SHA-256

Publié le · HardStacked · 9 min

Tu as mis à jour ton serveur OpenVPN (Debian 11 vers 12 vers 13, ou un Ubuntu récent) et des clients ne se connectent plus. Le journal du serveur dit VERIFY ERROR: depth=0, error=CA signature digest algorithm too weak: CN=.... Ou bien un client à jour s'arrête dès le démarrage avec OpenSSL: error:0A00018E:SSL routines::ca md too weak. Tu ouvres ton CA : SHA-256. Une question sur Server Fault, en avril 2024 (question 1157597, 7 050 vues au 2026-11-06, une seule réponse, écrite par la personne qui pose la question, non acceptée), le dit mieux que nous : « It looks like we're already using SHA256 and our certificates are good. So I'm super confused by this error ».

Ton CA va bien. « CA signature digest » n'est pas l'empreinte du certificat du CA, c'est l'empreinte avec laquelle le CA a signé un autre certificat, et la ligne du journal nomme ce certificat : depth=0 et CN=. Depuis OpenSSL 3.0, un certificat signé en SHA-1 est refusé au niveau de sécurité 1, et le niveau 1 est exactement le défaut d'OpenVPN, que son propre manuel décrit comme « SHA1 and newer ». L'outil qu'on lance pour vérifier, openssl verify, dit OK au certificat fautif. Le correctif : re-signer ce certificat en SHA-256, même clé. Le contournement (tls-cert-profile insecure) est temporaire, et il laisse aussi entrer du MD5.

Dans cette question, le passage était Debian Buster vers Bookworm, OpenVPN 2.4.7 vers 2.6.3, OpenSSL 3.0.13. Chez elle, le coupable, c'étaient les certificats clients, signés en SHA-1 par un CA en SHA-256. Mesuré sur une machine virtuelle Debian 13.7 neuve (arm64, image cloud officielle), openvpn 2.6.14-1+deb13u3, easy-rsa 3.2.2-1, OpenSSL 3.5.7, le 2026-11-06.

01. Deux messages, un seul certificat

Tout vit dans ~/demo-ovpn et porte demo dans son nom. Rien ne touche à un vrai serveur OpenVPN : le serveur de démo écoute sur 127.0.0.1, port 11940, avec dev null, donc aucun tunnel et aucune interface réseau, et OpenVPN tourne sans root (sudo sert seulement à apt-get). Tout est retiré à la fin (section 07). On écrit /usr/sbin/openvpn en entier parce que /usr/sbin n'est pas dans le PATH d'un utilisateur normal sous Debian. On a coupé le bruit d'easy-rsa dans les sorties (points de progression, rapport de openssl ca), jamais une commande.

La PKI : un CA, un certificat de serveur, demo-new en SHA-256 (le défaut), et demo-old signé avec --digest=sha1, qui joue le certificat émis il y a des années.

la PKI de démo
$ sudo apt-get install -y -qq openvpn easy-rsa >/dev/null
$ dpkg-query -W openvpn easy-rsa openssl
easy-rsa 3.2.2-1 openssl 3.5.7-1~deb13u3 openvpn 2.6.14-1+deb13u3
$ make-cadir ~/demo-ovpn
$ cd ~/demo-ovpn
$ ./easyrsa --batch init-pki >/dev/null
$ ./easyrsa --sbatch --req-cn=demo-ca build-ca nopass >/dev/null
$ ./easyrsa --sbatch build-server-full demo-server nopass >/dev/null
$ ./easyrsa --sbatch build-client-full demo-new nopass >/dev/null
$ ./easyrsa --sbatch --digest=sha1 build-client-full demo-old nopass >/dev/null

Deux petits fichiers de configuration, puis le serveur en arrière-plan :

le serveur de démo
$ printf '%s\n' 'dev null' 'local 127.0.0.1' 'port 11940' 'proto udp' 'tls-server' 'dh none' 'ca pki/ca.crt' 'cert pki/issued/demo-server.crt' 'key pki/private/demo-server.key' 'verb 3' > demo-server.conf
$ printf '%s\n' 'dev null' 'remote 127.0.0.1 11940' 'proto udp' 'nobind' 'tls-client' 'remote-cert-tls server' 'ca pki/ca.crt' 'cert pki/issued/demo-old.crt' 'key pki/private/demo-old.key' 'verb 3' > demo-client.conf
$ /usr/sbin/openvpn --cd ~/demo-ovpn --config demo-server.conf --daemon --log demo-server.log --writepid demo-server.pid

D'abord le client avec le certificat SHA-1, sur un OpenVPN à jour. C'est la deuxième forme du message. OpenSSL refuse de charger le propre certificat du client, avant qu'un seul paquet parte.

un client à jour
$ timeout 10 /usr/sbin/openvpn --config demo-client.conf | grep -E 'too weak|Exiting'
2026-11-06 20:32:33 OpenSSL: error:0A00018E:SSL routines::ca md too weak: 2026-11-06 20:32:33 Exiting due to fatal error

Maintenant la forme de la question. On donne au client --tls-cert-profile insecure, pour qu'il joue un client plus ancien, par exemple compilé avec OpenSSL 1.1.1, qui acceptait encore le SHA-1 au niveau 1 d'après le changelog d'OpenSSL (voir section 03). C'est très probablement la situation de la question Server Fault, où l'erreur n'apparaît que côté serveur. Le client n'affiche rien, aucune ligne Initialization : il n'entre pas. Le journal du serveur a la ligne :

un client plus ancien
$ timeout 10 /usr/sbin/openvpn --config demo-client.conf --tls-cert-profile insecure | grep -E 'Initialization|Exiting'
$ grep -E 'VERIFY|too weak' demo-server.log
2026-11-06 20:32:34 VERIFY ERROR: depth=0, error=CA signature digest algorithm too weak: CN=demo-old, serial=19432028680526676012959639061018401116

02. Lis la ligne : le CA n'est pas le fautif

depth=0 est le certificat présenté par le pair. depth=1 serait le CA. CN=demo-old est son nom. Le message dit « CA » parce que c'est la signature du CA sur ce certificat qui est trop faible. La personne qui pose la question l'a trouvé elle aussi : « The error message specifically indicates the CA, not the server or client certificates, so that's what was throwing me. »

L'algorithme de signature de chaque certificat :

algorithmes de signature
$ for f in pki/ca.crt pki/issued/*.crt; do echo "$f: $(openssl x509 -in "$f" -noout -text | grep -m1 -o 'Signature Algorithm: .*')"; done
pki/ca.crt: Signature Algorithm: sha256WithRSAEncryption pki/issued/demo-new.crt: Signature Algorithm: sha256WithRSAEncryption pki/issued/demo-old.crt: Signature Algorithm: sha1WithRSAEncryption pki/issued/demo-server.crt: Signature Algorithm: sha256WithRSAEncryption

Le CA, le serveur et demo-new sont en SHA-256. Seul demo-old est en sha1WithRSAEncryption.

03. Pourquoi ça marchait avant la mise à jour

versions et manuel
$ openssl version
OpenSSL 3.5.7 9 Jun 2026 (Library: OpenSSL 3.5.7 9 Jun 2026)
$ /usr/sbin/openvpn --version | head -1
OpenVPN 2.6.14 aarch64-unknown-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] [DCO]
$ man openvpn 2>/dev/null | grep -A1 'legacy (default)'
legacy (default) SHA1 and newer, RSA 2048-bit+, any elliptic curve. -- legacy (default) sets "security level 1"

Le changelog d'OpenSSL, changements entre 1.1.1 et 3.0.0 : « X509 certificates signed using SHA1 are no longer allowed at security level 1 and above. » Buster avait OpenSSL 1.1.1, Bookworm 3.0, Debian 13 a la 3.5. Et le manuel d'OpenVPN se contredit : legacy (default) est « SHA1 and newer », puis quelques lignes plus bas, pour les builds OpenSSL, legacy (default) « sets security level 1 ». Avec OpenSSL 3, la première phrase est fausse. Écrire tls-cert-profile legacy toi-même ne change rien, c'est déjà le défaut (mesuré sur le poste).

04. Trouve tous les certificats qui vont casser

Voilà le piège. openssl verify, la commande que tout le monde lance, dit OK à demo-old. Avec -auth_level 1, il applique la même règle qu'OpenVPN, sur toute la PKI d'un coup :

openssl verify
$ openssl verify -CAfile pki/ca.crt pki/issued/demo-old.crt
pki/issued/demo-old.crt: OK
$ openssl verify -auth_level 1 -CAfile pki/ca.crt pki/issued/*.crt
CN=demo-old error 68 at 0 depth lookup: CA signature digest algorithm too weak error pki/issued/demo-old.crt: verification failed pki/issued/demo-new.crt: OK pki/issued/demo-server.crt: OK

La page de manuel d'OpenSSL 3.5.7 (openssl-verification-options) explique les deux. « The default security level is -1, or "not set". At security level 0 or lower all algorithms are acceptable. » C'est pourquoi la première commande blanchit le certificat. Et « The signature algorithm security level is enforced for all the certificates in the chain except for the chain's trust anchor » : un CA racine auto-signé en SHA-1 ne déclenche rien. Mesuré sur le poste, racine SHA-1 avec des certificats SHA-256, connexion complète. Bonne nouvelle si ton vieux CA est une racine.

05. Le correctif : re-signe le certificat, garde la clé

Regarde d'abord vars (et pki/vars si tu en as un, on a mesuré les deux). Commenté, c'est le défaut, SHA-256. S'il contient set_var EASYRSA_DIGEST "sha1" non commenté, retire cette ligne avant tout : on a mesuré, avec un témoin, que easyrsa renew dit alors « Renew was successful. » et signe de nouveau en SHA-1.

easyrsa renew
$ grep -n 'EASYRSA_DIGEST' vars
229:#set_var EASYRSA_DIGEST "sha256"
$ ./easyrsa --batch renew demo-old 2>&1 | grep -E 'successful|revoke-renewed'
Renew was successful. use command 'revoke-renewed demo-old'
$ openssl verify -auth_level 1 -CAfile pki/ca.crt pki/issued/*.crt
pki/issued/demo-new.crt: OK pki/issued/demo-old.crt: OK pki/issued/demo-server.crt: OK
$ timeout 10 /usr/sbin/openvpn --config demo-client.conf | grep -E 'Initialization|Exiting'
2026-11-06 20:32:57 Initialization Sequence Completed
$ grep 'depth=0' demo-server.log
2026-11-06 20:32:34 VERIFY ERROR: depth=0, error=CA signature digest algorithm too weak: CN=demo-old, serial=19432028680526676012959639061018401116 2026-11-06 20:32:56 VERIFY OK: depth=0, CN=demo-old

Le client entre maintenant avec la même clé et le nouveau certificat, et le journal du serveur montre l'erreur d'avant, puis VERIFY OK. La clé privée ne change pas (on a comparé les clés publiques, identiques), seul le .crt change. Chaque client reçoit un nouveau certificat, pas une nouvelle clé. Si tu distribues pki/inline/private/<nom>.inline, renew le régénère.

easy-rsa suggère ensuite revoke-renewed, une fois le nouveau certificat déployé, et celui-ci dit à son tour « You must run 'gen-crl' and upload a new CRL ». Côté OpenVPN, la liste est lue par crl-verify (« Check peer certificate against a Certificate Revocation List »), une option que le manuel dit « not mandatory » : vérifie que ton serveur l'a. Un deuxième renew du même nom est refusé (« a conflicting file exists ») tant que l'ancien n'est pas révoqué.

06. Si tu ne peux pas ré-émettre aujourd'hui

Le contournement laisse entrer plus que tu penses. On émet un certificat signé en MD5. Sur le serveur par défaut, il est refusé avec le même message que le SHA-1. On ajoute tls-cert-profile insecure au serveur et on le relance. Le client reçoit aussi cette option, pour la raison de la section 01.

tls-cert-profile insecure
$ ./easyrsa --sbatch --digest=md5 build-client-full demo-md5 nopass >/dev/null
$ timeout 10 /usr/sbin/openvpn --config demo-client.conf --cert pki/issued/demo-md5.crt --key pki/private/demo-md5.key --tls-cert-profile insecure | grep -E 'Initialization|Exiting'
$ grep 'CN=demo-md5' demo-server.log
2026-11-06 20:33:10 VERIFY ERROR: depth=0, error=CA signature digest algorithm too weak: CN=demo-md5, serial=158015191702880275000999649420847075776
$ kill $(cat demo-server.pid)
$ echo 'tls-cert-profile insecure' >> demo-server.conf
$ /usr/sbin/openvpn --cd ~/demo-ovpn --config demo-server.conf --daemon --log demo-server.log --writepid demo-server.pid
$ timeout 10 /usr/sbin/openvpn --config demo-client.conf --cert pki/issued/demo-md5.crt --key pki/private/demo-md5.key --tls-cert-profile insecure | grep -E 'Initialization|Exiting'
2026-11-06 20:33:26 Initialization Sequence Completed
$ grep 'CN=demo-md5' demo-server.log
2026-11-06 20:33:25 VERIFY OK: depth=0, CN=demo-md5

Le contournement ne dit pas « accepte le SHA-1 », il dit « accepte tout », MD5 compris, et le message d'erreur était le même pour les deux : tu ne sais pas ce que tu laisses entrer. Si tu dois l'utiliser, mets-le sur le serveur, qui vérifie et journalise, et seulement jusqu'à ce que chaque client ait son nouveau certificat. On a mesuré tls-cipher "DEFAULT:@SECLEVEL=0" sur le poste : même effet sur le certificat SHA-1. On ne l'a pas testé avec le MD5, mais le niveau 0 est celui où « all algorithms are acceptable » (section 04). Et un client dont le propre certificat est en SHA-1 s'arrête au démarrage (section 01) : il lui faut le même réglage, ce n'est donc pas « une ligne sur le serveur ».

07. Nettoyage

démontage
$ kill $(cat demo-server.pid)
$ cd ~ && rm -rf ~/demo-ovpn
$ ls -d ~/demo-ovpn
ls: cannot access '/home/user/demo-ovpn': No such file or directory

Si tu as installé les paquets seulement pour ce test, tu peux les retirer. Vérifie d'abord que tu n'as pas un vrai serveur OpenVPN sur la machine.

Portée : machine virtuelle Debian 13.7 arm64 et poste Debian 13, mêmes versions, 2026-11-06. Le refus vient d'OpenSSL 3.0 et plus, donc toute distribution qui livre OpenSSL 3 avec OpenVPN devrait se comporter pareil (inféré du changelog, pas mesuré). Non mesuré : les clients Windows, macOS et mobiles (OpenVPN Connect, Viscosity), les builds mbed TLS (le manuel dit que les profils y sont gérés autrement), OpenVPN 2.5 et 2.7. Une autre mise à jour qui casse OpenVPN, cette fois le noyau : dkms « failed for ovpn-dco » après la mise à jour du noyau 7.0.

Partager :