SIP Digest Authentication
Overview
PJSIP implements HTTP digest authentication for SIP per RFC 3261, RFC 7616, and RFC 8760, with the following digest algorithms:
Algorithm enum |
IANA name |
Reference |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RFC 4169, 3GPP TS 33.203 |
The full enum is pjsip_auth_algorithm_type in
pjsip/sip_auth.h. The IANA name is the value that appears in the
algorithm parameter of SIP WWW-Authenticate /
Proxy-Authenticate / Authorization / Proxy-Authorization
headers.
MD5 has been the historical default and remains widely supported, but it is cryptographically weak and many modern SIP servers now mandate SHA-256 (or stronger) for compliance reasons. SHA-256 and SHA-512/256 support landed in PJSIP 2.15 (#4118).
This page covers how to select the digest algorithm on both the client (UA) and server (UAS / proxy) side, build prerequisites, and backward-compat / migration notes. For asynchronous handling of incoming 401/407 challenges (e.g. when credentials must be fetched from an external service before responding), see Asynchronous SIP Authentication.
Build prerequisites
Algorithm availability depends on what the build links against:
MD5 is always available. With OpenSSL it goes through
EVP_get_digestbyname("MD5"); in strict-FIPS OpenSSL builds where MD5 is unavailable, PJSIP detects this at runtime and transparently falls back to its internal MD5 implementation.SHA-256 and SHA-512/256 require OpenSSL as the SSL socket implementation, i.e. the library must be built with
PJ_HAS_SSL_SOCK = 1andPJ_SSL_SOCK_IMP = PJ_SSL_SOCK_IMP_OPENSSL. Without OpenSSL, the SHA digests are not computed; only MD5 works.AKA-MD5 (v1 and v2) is gated separately by the compile-time flag
PJSIP_HAS_DIGEST_AKA_AUTH, which defaults to0. Set it to1in config_site.h when building for IMS / VoLTE deployments. AKA additionally needs the application to compute the AKA response in a callback (see below) and is independent of the OpenSSL requirement above.
To check at runtime whether a given algorithm is supported in the current build, use:
if (pjsip_auth_is_digest_algorithm_supported(PJSIP_AUTH_ALGORITHM_SHA256)) {
/* SHA-256 is available */
}
There is no separate PJSIP_HAS_DIGEST_SHA256_AUTH flag — SHA-256 support follows OpenSSL availability automatically.
Selecting the digest algorithm (client side)
When acting as a UA, the application advertises its credentials via
pjsip_cred_info. Two fields together determine which
algorithm is used in the Authorization header:
pjsip_cred_info::data_type—PJSIP_CRED_DATA_PLAIN_PASSWD(plaintext password; the framework computes the H(A1) hash when responding to a challenge) orPJSIP_CRED_DATA_DIGEST(thedatafield already contains the pre-hashed H(A1) for the selected algorithm).pjsip_cred_info::algorithm_type— thepjsip_auth_algorithm_typeto use. If left atPJSIP_AUTH_ALGORITHM_NOT_SET(the default afterpj_bzero()/PJ_POOL_ZALLOC_T()), the framework treats the credential as MD5 — this preserves backward compatibility for existing apps.
When the credential’s algorithm matches what the server’s challenge
asked for, the framework uses that credential to compute the
response. If the server’s challenge is for a different algorithm,
the framework looks for another credential on the account with a
matching algorithm_type.
PJSUA-LIB
Each pjsua_acc_config::cred_info array entry is a
pjsip_cred_info. Set algorithm_type per credential.
The most common pattern: the application advertises both an MD5 and
a SHA-256 credential for the same realm/username, and the framework
picks whichever the server challenges with.
pjsua_acc_config cfg;
pjsua_acc_config_default(&cfg);
/* ...id, reg URI, etc... */
/* Credential #0: SHA-256 */
cfg.cred_info[0].realm = pj_str("*"); /* match any realm */
cfg.cred_info[0].scheme = pj_str("digest");
cfg.cred_info[0].username = pj_str("alice");
cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
cfg.cred_info[0].data = pj_str("s3cret");
cfg.cred_info[0].algorithm_type = PJSIP_AUTH_ALGORITHM_SHA256;
/* Credential #1: MD5 fallback for older servers */
cfg.cred_info[1].realm = pj_str("*");
cfg.cred_info[1].scheme = pj_str("digest");
cfg.cred_info[1].username = pj_str("alice");
cfg.cred_info[1].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
cfg.cred_info[1].data = pj_str("s3cret");
cfg.cred_info[1].algorithm_type = PJSIP_AUTH_ALGORITHM_MD5;
cfg.cred_count = 2;
PJSUA2
The PJSUA2 wrapper pj::AuthCredInfo exposes the same field
in camelCase as pj::AuthCredInfo::algoType:
AccountConfig cfg;
/* ...idUri, regConfig.registrarUri, etc... */
AuthCredInfo sha;
sha.scheme = "digest";
sha.realm = "*";
sha.username = "alice";
sha.dataType = PJSIP_CRED_DATA_PLAIN_PASSWD;
sha.data = "s3cret";
sha.algoType = PJSIP_AUTH_ALGORITHM_SHA256;
cfg.sipConfig.authCreds.push_back(sha);
AuthCredInfo md5;
md5.scheme = "digest";
md5.realm = "*";
md5.username = "alice";
md5.dataType = PJSIP_CRED_DATA_PLAIN_PASSWD;
md5.data = "s3cret";
md5.algoType = PJSIP_AUTH_ALGORITHM_MD5;
cfg.sipConfig.authCreds.push_back(md5);
Pre-hashed credentials
If you don’t want the plaintext password in your binary, set
data_type to PJSIP_CRED_DATA_DIGEST and put the H(A1)
hash directly in data. The hash must be computed with the
algorithm declared in algorithm_type:
For MD5:
H(A1) = MD5(username:realm:password), hex string.For SHA-256:
H(A1) = SHA256(username:realm:password), hex string.For SHA-512/256:
H(A1) = SHA-512/256(username:realm:password), hex string.
In this mode, algorithm_type MUST match the algorithm used to
compute the hash; the framework will not re-hash a digest credential.
Issuing challenges (server side)
When acting as a UAS or proxy that authenticates incoming requests,
use pjsip_auth_srv_challenge2() to attach a
WWW-Authenticate (or Proxy-Authenticate) header to a 401/407
response with a specific algorithm:
/* qop, nonce, opaque can be NULL — the framework fills them in */
pjsip_auth_srv_challenge2(&auth_srv,
NULL, NULL, NULL,
PJ_FALSE, /* not stale */
tdata,
PJSIP_AUTH_ALGORITHM_SHA256);
The legacy pjsip_auth_srv_challenge() exists for backward
compatibility but always issues an MD5 challenge — new server-side
code should call _challenge2().
To advertise multiple algorithms in one response (so a client can
pick the strongest it supports), call pjsip_auth_srv_challenge2()
multiple times on the same tdata with different
algorithm_type values. Per RFC 7616 the strongest algorithm
should appear first.
Helper APIs
pjsip_auth_get_algorithm_by_type()— returns thepjsip_auth_algorithmdescribing an enum value (IANA name, OpenSSL name, digest length, hex length).pjsip_auth_get_algorithm_by_iana_name()— same lookup by header value (e.g."SHA-256").pjsip_auth_is_digest_algorithm_supported— runtime check; returnsPJ_FALSEfor SHA-256 / SHA-512-256 if the build has no OpenSSL.
These are useful when the application needs to negotiate per-realm algorithm preference dynamically, or when bridging credentials between PJSIP and an external auth backend.
AKA authentication
Digest AKA (Authentication and Key Agreement, used in IMS / VoLTE) is structurally similar to MD5 digest but the H(A1) hash is computed by the application using the SIM key material rather than from a plaintext password. To use it:
Build with
PJSIP_HAS_DIGEST_AKA_AUTHset to1in config_site.h.Set
algorithm_typeon the credential to eitherPJSIP_AUTH_ALGORITHM_AKAV1_MD5orPJSIP_AUTH_ALGORITHM_AKAV2_MD5.Set
data_typetoPJSIP_CRED_DATA_PLAIN_PASSWD | PJSIP_CRED_DATA_EXT_AKA.Provide the AKA inputs in the
ext.akasub-struct:k(permanent subscriber key),op(operator variant),amf(authentication management field), andcb— a callback that computes the AKA response from the challenge.The PJSUA2 equivalent fields are
pj::AuthCredInfo::akaK,pj::AuthCredInfo::akaOp, andpj::AuthCredInfo::akaAmf.
The full AKA programming model is its own topic and not covered
further here; see pjsip/sip_auth_aka.h and
PJSIP_AUTH_AKA_API for the callback contract and helper
functions.
Backward compatibility and deprecations
Code written before SHA-256 support landed continues to work unchanged. Credentials with
algorithm_type == 0(NOT_SET) are treated as MD5.pjsip_auth_create_digestSHA256()is deprecated; new code should usepjsip_auth_create_digest2()withalgorithm_type = PJSIP_AUTH_ALGORITHM_SHA256. The deprecated helper still works but the explicit form composes more cleanly with the rest of the algorithm-aware API.pjsip_auth_create_digest()(MD5-only) is similarly deprecated in favour ofpjsip_auth_create_digest2().The legacy server-side
pjsip_auth_srv_challenge()keeps working for MD5-only deployments; usepjsip_auth_srv_challenge2()to issue challenges for any other algorithm.
See also
Asynchronous SIP Authentication — asynchronous handling of 401/407 challenges, plus
pjsua_acc_config::use_shared_authfor reusing the auth session across modules (REGISTER / SUBSCRIBE / PUBLISH / etc.).