openvpn · openssl
OpenVPN "CA signature digest algorithm too weak" when your CA is SHA-256
Published · HardStacked · 9 min
You upgraded your OpenVPN server (Debian 11 to 12 to 13, or a recent Ubuntu) and some clients no longer connect. The server log says VERIFY ERROR: depth=0, error=CA signature digest algorithm too weak: CN=.... Or a client that is up to date dies at startup with OpenSSL: error:0A00018E:SSL routines::ca md too weak. You open your CA: SHA-256. A Server Fault question from April 2024 (question 1157597, 7,050 views on 2026-11-06, one answer, written by the asker, not accepted) says it better than we can: "It looks like we're already using SHA256 and our certificates are good. So I'm super confused by this error".
Your CA is fine. "CA signature digest" is not the digest of the CA's certificate, it is the digest the CA used to sign another certificate, and the log line names that certificate: depth=0 and CN=. Since OpenSSL 3.0, a certificate signed with SHA-1 is refused at security level 1, and level 1 is exactly OpenVPN's default, which its own manual describes as "SHA1 and newer". The tool you run to check, openssl verify, says OK to the guilty certificate. The fix is to re-sign that one certificate in SHA-256, same key. The workaround (tls-cert-profile insecure) is temporary, and it lets MD5 in too.
In that question the move was Debian Buster to Bookworm, OpenVPN 2.4.7 to 2.6.3, OpenSSL 3.0.13. The asker's culprit was the client certificates, signed in SHA-1 by a SHA-256 CA. Measured on a fresh Debian 13.7 virtual machine (arm64, official cloud image), openvpn 2.6.14-1+deb13u3, easy-rsa 3.2.2-1, OpenSSL 3.5.7, on 2026-11-06.
01. Two messages, one certificate
Everything here lives in ~/demo-ovpn and carries demo in its name. Nothing touches a real OpenVPN server: the demo server listens on 127.0.0.1 port 11940 with dev null, so no tunnel and no network interface, and OpenVPN itself runs without root (sudo is only for apt-get). Everything is removed at the end (section 07). We write /usr/sbin/openvpn in full because /usr/sbin is not in the PATH of a normal user on Debian. We cut the easy-rsa noise from the outputs (progress dots, the openssl ca report), never a command.
The PKI: a CA, a server certificate, demo-new in SHA-256 (the default), and demo-old signed with --digest=sha1, playing the certificate issued years ago.
$ 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
Two small config files, then the server in the background:
the demo server
$ 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
First, the client with the SHA-1 certificate, on an up to date OpenVPN. This is the second form of the message. OpenSSL refuses to load the client's own certificate, before a single packet leaves.
an up to date client
$ 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
Now the form of the question. We give the client --tls-cert-profile insecure, so it plays an older client, for example one built with OpenSSL 1.1.1, which still accepted SHA-1 at level 1 according to the OpenSSL changelog (see section 03). That is very probably the situation of the Server Fault question, where the error shows only on the server. The client prints nothing, no Initialization line: it does not get in. The server log has the line:
an older client
$ 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. Read the line: the CA is not the one being blamed
depth=0 is the certificate presented by the peer. depth=1 would be the CA. CN=demo-old is its name. The message says "CA" because it is the CA's signature on that certificate that is too weak. The asker found this too: "The error message specifically indicates the CA, not the server or client certificates, so that's what was throwing me."
The signature algorithm of each certificate:
signature algorithms
$ 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
The CA, the server and demo-new are SHA-256. Only demo-old is sha1WithRSAEncryption.
03. Why it worked before the upgrade
versions and manual
$ 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"
The OpenSSL changelog, changes between 1.1.1 and 3.0.0: "X509 certificates signed using SHA1 are no longer allowed at security level 1 and above." Buster had OpenSSL 1.1.1, Bookworm 3.0, Debian 13 has 3.5. And the OpenVPN manual contradicts itself: legacy (default) is "SHA1 and newer", and a few lines further, for OpenSSL builds, legacy (default) "sets security level 1". With OpenSSL 3, the first sentence is false. Writing tls-cert-profile legacy yourself changes nothing, it is already the default (we measured it on the workstation).
04. Find every certificate that will break
Here is the trap. openssl verify, the command everybody runs, says OK to demo-old. With -auth_level 1, it applies the same rule as OpenVPN, on the whole PKI at once:
$ 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
The man page of OpenSSL 3.5.7 (openssl-verification-options) explains both. "The default security level is -1, or "not set". At security level 0 or lower all algorithms are acceptable." That is why the first command blesses the certificate. And "The signature algorithm security level is enforced for all the certificates in the chain except for the chain's trust anchor": a self-signed SHA-1 root CA triggers nothing. We measured it on the workstation, SHA-1 root with SHA-256 certificates, full connection. Good news if your old CA is a root.
05. The fix: re-sign the certificate, keep the key
First look at vars (and pki/vars if you have one, we measured both). Commented out means the default, SHA-256. If it holds set_var EASYRSA_DIGEST "sha1" uncommented, remove that line before anything else: we measured, with a control, that easyrsa renew then says "Renew was successful." and signs in SHA-1 again.
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
The client now gets in with the same key and the new certificate, and the server log shows the error from before, then VERIFY OK. The private key does not change (we compared the public keys, identical), only the .crt. Each client gets a new certificate, not a new key. If you distribute pki/inline/private/<name>.inline, renew regenerates it.
easy-rsa then suggests revoke-renewed, once the new certificate is deployed, and that one in turn says "You must run 'gen-crl' and upload a new CRL". On the OpenVPN side, the list is read by crl-verify ("Check peer certificate against a Certificate Revocation List"), an option the manual calls "not mandatory": check that your server has it. A second renew of the same name is refused ("a conflicting file exists") until the old one is revoked.
06. If you can't re-issue today
The workaround admits more than you think. We issue a certificate signed with MD5. On the default server it is refused with the same message as the SHA-1 one. We add tls-cert-profile insecure to the server and restart it. The client also gets that option, for the reason of 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
The workaround does not say "accept SHA-1", it says "accept anything", MD5 included, and the error message was the same for both: you do not know what you let in. If you must use it, put it on the server, which verifies and logs, and only until every client has its new certificate. We measured tls-cipher "DEFAULT:@SECLEVEL=0" on the workstation: same effect on the SHA-1 certificate. We did not test MD5 with it, but level 0 is the one where "all algorithms are acceptable" (section 04). And a client whose own certificate is SHA-1 stops at startup (section 01), so it needs the same setting too: it is not "one line on the server".
07. Clean up
teardown
$ 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
If you installed the packages only for this test, you can remove them. Check first that you do not have a real OpenVPN server on the machine.
Scope: Debian 13.7 arm64 virtual machine and a Debian 13 workstation, same versions, 2026-11-06. The refusal comes from OpenSSL 3.0 and later, so any distribution that ships OpenSSL 3 with OpenVPN should behave the same (inferred from the changelog, not measured). Not measured: Windows, macOS and mobile clients (OpenVPN Connect, Viscosity), builds with mbed TLS (the manual says profiles are handled differently there), OpenVPN 2.5 and 2.7. Another upgrade that breaks OpenVPN, this time the kernel: dkms "failed for ovpn-dco" after the kernel 7.0 update.