You ran apt upgrade. Maybe rebuilt a Docker image. Maybe migrated distros. Whatever it was, your Asterisk box was humming along with PJSIP endpoints connecting over TLS on port 5061. Then this showed up:
WARNING[12345]: pjproject: SSL SSL_ERROR_SSL (Handshake):
err: <error:0A00017A:SSL routines::wrong curve>
Phones stop registering. ATAs go offline. SIP trunks drop. Disabling TLS brings everything back, but that's giving up, not fixing anything.
The culprit: OpenSSL 3.x changed how it handles elliptic curve negotiation during TLS handshakes, and your SIP devices didn't get the memo.
What Actually Changed
OpenSSL 1.1.x was forgiving about curve mismatches. If a client offered curves that didn't perfectly match the server certificate's key type, it would usually find a workaround and proceed.
OpenSSL 3.0+ got strict about three things:
- Certificate key curves must match negotiated ECDH curves. P-384 cert + client only offering P-256 = "wrong curve" rejection.
-
Default curve lists changed. OpenSSL 3.x defaults to
X25519:P-256:P-384:P-521. Older SIP hardware often only supports P-256. - ECDH auto-selection validates strictly. If the client doesn't advertise the server cert's curve, handshake fails immediately.
Why SIP Phones Get Hit Hardest
Grandstream, Yealink, Polycom, and Obihai devices ship firmware with limited curve support. A Grandstream HT801 might only advertise secp256r1 (P-256). It won't support X25519 or X448. If your Asterisk cert uses P-384, that device is locked out.
The cruel part: this breaks without any intentional change on your end. A base OS update or Docker image rebuild can silently swap OpenSSL 1.1.x for 3.x underneath Asterisk.
Diagnose It in 60 Seconds
# Confirm OpenSSL 3.x
openssl version
# Check your cert's curve
openssl x509 -in /etc/asterisk/keys/asterisk.pem -text -noout | grep -A 2 "Public Key"
# Simulate what your phone sees
openssl s_client -connect your-asterisk-ip:5061 -curves secp256r1 -tls1_2 2>&1 | head -30
If you see ECDSA with secp384r1 or secp521r1 in the cert, and the s_client test fails -- that's your problem confirmed.
The Fix: Regenerate with P-256
P-256 (secp256r1) works with every SIP device on the market. This is almost always the right answer:
# Generate P-256 key
openssl ecparam -genkey -name prime256v1 -out /etc/asterisk/keys/asterisk.key
# Self-signed cert (10 years)
openssl req -new -x509 -key /etc/asterisk/keys/asterisk.key \
-out /etc/asterisk/keys/asterisk.crt \
-days 3650 -subj "/CN=asterisk.local/O=Asterisk PBX"
# Combine into PEM
cat /etc/asterisk/keys/asterisk.key /etc/asterisk/keys/asterisk.crt \
> /etc/asterisk/keys/asterisk.pem
# Permissions
chown asterisk:asterisk /etc/asterisk/keys/*
chmod 600 /etc/asterisk/keys/asterisk.key
# Reload
asterisk -rx "module reload res_pjsip.so"
Alternatively, use RSA 2048-bit to sidestep curve issues entirely:
openssl req -new -x509 -nodes -newkey rsa:2048 \
-keyout /etc/asterisk/keys/asterisk.key \
-out /etc/asterisk/keys/asterisk.crt \
-days 3650 -subj "/CN=asterisk.local/O=Asterisk PBX"
Alternative Fixes
Force curves in pjsip.conf (if you can't regenerate certs):
[tls-transport]
type = transport
protocol = tls
bind = 0.0.0.0:5061
cert_file = /etc/asterisk/keys/asterisk.crt
priv_key_file = /etc/asterisk/keys/asterisk.key
method = tlsv1_2
cipher = ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256
System-wide OpenSSL config (/etc/ssl/openssl.cnf):
[system_default_sect]
Groups = P-256:P-384:X25519
Docker users: Pin your base image. OpenSSL can silently upgrade between rebuilds. Mount certs and openssl.cnf from the host to control what's inside the container.
Device Compatibility Quick Reference
| Device | P-256 | P-384 | X25519 |
|---|---|---|---|
| Grandstream HT80x | Yes | Sometimes | No |
| Yealink T4x/T5x | Yes | Yes | Firmware-dependent |
| Polycom VVX | Yes | Yes | No |
| Obihai/Obi | Yes | No | No |
| Linphone | Yes | Yes | Yes |
P-256 is the only curve guaranteed to work with everything.
Don't Chase the ca_list_file Red Herring
If you see ERROR: Failed to read CA list file, that's unrelated to the "wrong curve" error. For self-signed setups, you can safely omit ca_list_file and set verify_client = no. Don't waste time debugging CA chain issues when the real problem is curve negotiation.
Prevent It From Happening Again
- Pin your OpenSSL version in Docker images or package management
- Use P-256 for all new certificates
- Test TLS after every upgrade:
echo | openssl s_client -connect localhost:5061 -tls1_2 2>&1 | grep -q "Verify return code" && echo "OK" || echo "BROKEN" - Keep a backup of your last-known-good cert/key pair
The "wrong curve" error is OpenSSL 3.x being strict about something 1.1.x was lenient about. Don't panic, don't disable TLS, don't downgrade OpenSSL. Regenerate the cert with P-256 and move on. For more Asterisk configuration guides, check ViciStack's blog.
Originally published at https://vicistack.com/blog/asterisk-pjsip-tls-openssl3-guide/
Top comments (0)