Mutual TLS (mTLS) for SIP
Mutual TLS (mTLS) for SIP
Requires jambonz 11.1.2 or later (drachtio 10.1.3 or later).
Overview
Some carriers will not accept SIP over TLS unless the caller also proves who it is, by presenting a certificate of its own during the TLS handshake. This is known as mutual TLS, or mTLS. Ordinary SIP over TLS only proves the carrier’s identity to you; mTLS proves yours to them.
As of release 11.1.2 jambonz now supports mTLS. It can present a client certificate on outbound TLS connections. You configure one identity per server and it is used for every outbound TLS call.
There is nothing to enable per carrier. The requirement is signaled during the handshake: the carrier’s SBC asks for a certificate, and jambonz answers with the one you configured. Carriers that don’t ask are sent nothing, so configuring an identity is harmless for the rest of your trunks.
The consequence is that you cannot discover the requirement and retry. The handshake fails before any SIP is exchanged, so a missing certificate looks like a call that failed to connect, not a SIP rejection you can inspect. The identity has to be in place before the first call.
You cannot reuse your existing SIP TLS certificate
The certificate you already use for receiving SIP over TLS calls will not work as a client certificate, and the failure can be a bit confusing. Read this before you spend time on it.
Two independent reasons:
- A carrier requiring mTLS wants a certificate issued by a CA they trust — usually one you enrolled with them. If they accepted certificates from the public authorities, anyone holding a Let’s Encrypt certificate could authenticate as you.
- A certificate must carry the
clientAuthextended key usage to be usable as a client certificate at all. Public authorities have stopped issuing it — Let’s Encrypt issued its last on 2026-07-08, following a Chrome root program requirement that client and server authentication live in separate hierarchies. A certificate with onlyserverAuthis rejected asunsuitable certificate purposeno matter how well it is trusted.
You can check any certificate you already hold with this command:
A response of your-cert.pem: OK means it is valid for client authentication. If it prints
error 26 ... unsuitable certificate purpose, it is not. Any other error is about the certificate
chain rather than the purpose — error 20 ... unable to get local issuer certificate in particular
just means -CAfile is not the authority that issued the certificate.
Ask the carrier which authority they will accept
Settle this first, because it decides everything that follows. The question to put to them is: which certificate authority may sign our client certificate? There are three usual answers.
Option 1 — an authority you run. You create a small private CA, issue your own client certificates from it, and send the carrier only the CA certificate to load into their trust store. Prefer this when they will accept it: you can then issue certificates for additional servers, and replace expiring ones, without involving the carrier again.
Option 2 — an authority they already trust. If the carrier participates in an industry PKI, or already accepts a commercial client-authentication CA, you can buy a client certificate from it and nothing has to change on their side. Confirm the exact hierarchy with them before purchasing — “a well-known CA” is not specific enough, because a trust store used for client authentication is configured deliberately rather than populated with every public authority.
Option 3 — their own authority. You send a certificate signing request and they return a certificate. Necessary when their SBC will only trust its own CA. Every renewal is a new request to them.
Options 2 and 3 both work from a signing request, so the steps below are shared; only who signs it differs.
Do not plan on a certificate from one of the public authorities that already sit in the operating system trust store. Those hierarchies are being removed from client authentication entirely: under Chrome Root Program policy, subordinate CAs disclosed after 15 June 2026 may assert only server authentication, and from 15 March 2027 all newly issued public TLS certificates will be server-authentication only. Commercial CAs still sell client certificates, but from separate client-authentication hierarchies that a carrier must trust explicitly. Industry guidance is that client authentication belongs in a private or enterprise PKI.
The four files
Two of drachtio’s TLS settings do not mean what their names suggest, so it is worth being explicit about what each file is for.
Only the client/* settings are new. Your existing inbound configuration is unaffected.
Option 1: you issue the certificate
Create your certificate authority
Do this once. The CA does not have to live on the jambonz server, and generally should not — keep the key wherever you keep secrets, and use it to sign requests as they arrive. That also means you can create it before the server exists.
example-client-ca.key is the sensitive file — anyone holding it can issue certificates the carrier
will accept. example-client-ca.pem is public and is what you send the carrier.
Generate a key and request on the jambonz server
Do this on each server that will place calls to the carrier. Every server gets its own key, so that rebuilding or retiring one has no effect on the others.
Use this server’s SIP hostname as the CN unless the carrier asks for something specific.
Use absolute paths. /etc/drachtio/tls is mode 0750 and owned by root, so cd into it fails for a
non-root user and openssl then writes your key into your home directory instead.
The private key never leaves the server. The request (carrier-client.csr) is not secret.
Sign the request
On the machine holding the CA key:
-copy_extensions copy carries clientAuth across from the request, so there is only one place it
is declared. It requires OpenSSL 3.0 or later and is silently ignored on older versions and on
the LibreSSL that ships with macOS, so confirm the result:
If clientAuth is missing, declare the extensions explicitly instead:
Assemble and send
client/cert-file is a chain — the certificate first, then the CA:
Send the carrier example-client-ca.pem and ask them to add it to the trust store their SBC uses for
client authentication on the port your trunk uses. Never send a .key file.
Options 2 and 3: someone else issues the certificate
Generate the key and request exactly as above, then send the signing request to whoever will sign it — the carrier, or the commercial or industry CA they accept — and ask explicitly for three things:
- the certificate must include the
clientAuthextended key usage. Most current server certificate profiles omit it, and without it the certificate cannot work; - the intermediate certificates, not only the issued certificate. jambonz sends the whole chain, and their trust store may contain only the root;
- the validity period and how renewal is handled.
When it arrives, assemble it the same way — issued certificate first, then any intermediates:
Check the certificate before configuring drachtio
A failed call tells you very little. These checks tell you a lot.
The -purpose sslclient check is the one that catches a missing clientAuth.
Configure drachtio
Add a <client> element to the existing <tls> section of /etc/drachtio.conf.xml. Nothing else in
that section changes.
An outbound TLS connection requires a sips: contact, since the certificate belongs to the TLS
transport. Standard jambonz images already have one — see
Setting up TLS for WebRTC and SIP.
These three apply to every outbound TLS connection this server makes, not only the one to this
carrier. If you have other TLS carriers configured by IP address rather than hostname, set
verify-server-name to false — an IP address cannot match a hostname in a certificate. You still
validate the certificate chain.
Restart drachtio and confirm it read the identity:
If those lines are missing, drachtio did not pick up the <client> element — usually a path it
cannot read, or the element placed outside <tls>.
If the carrier uses a private authority
client/ca-file is usually just the system bundle, but some carriers present a server certificate
issued by a closed hierarchy whose root is deliberately not publicly trusted. Validation then fails
with self-signed certificate in certificate chain, even though the issuer is a well-known
commercial CA — it is a private hierarchy they operate.
Ask them for their root and append it to the system bundle, so one file covers that carrier and every publicly trusted one:
Point client/ca-file at the result. Check the fingerprint of the file they sent rather than trusting
whatever the connection offers you:
Test before placing a call
This proves the certificates work independently of jambonz, which separates a certificate problem from a SIP or routing problem:
Expect Verify return code: 0 (ok) and no fatal alert.
Configure the carrier in jambonz
Create the carrier in the portal with one outbound SIP gateway pointing at the carrier’s TLS host and port, and set the protocol to TLS or TLS/SRTP depending on whether they require encrypted media.
Set the port explicitly. Leaving it blank makes jambonz resolve the destination using DNS SRV
records, which many carriers do not publish for this purpose — and if they do, the SRV target
hostname may not be covered by their certificate, which breaks verify-server-name.
If the carrier only accepts INVITE — some SBCs reject OPTIONS outright — turn off the SIP
OPTIONS ping on the gateway. Otherwise jambonz will ping it, receive a rejection, mark the gateway
unavailable, and every call will fail for a reason unrelated to the trunk.
Troubleshooting
TLS failures are reported in /var/log/drachtio/drachtio.log with the reason attached.
Raise --sofia-loglevel to 9 for a full transport trace, including the SNI value sent and whether a
client identity was loaded.
Renewal and additional servers
Under option 1, renewing is the same three steps — new key and request on the server, signed by the same CA, then restart drachtio. The carrier is not involved, because their trust anchor has not changed. Adding another jambonz server is the same process with its own key.
Under options 2 and 3 the trust anchor is also unchanged, so the carrier does not need to act — but each certificate has to be reissued by whoever owns that authority, on their timetable rather than yours.
Track expiry: