Realm.Builder.ha2 writes A2 into the recycled StringBuilder that
newResponse took from StringBuilderPool, but on the auth-int branch with
no precomputed entity-body hash it called StringUtils.toHexString, which
takes that same thread-local builder and resets it. The "POST:/secret:"
already written was discarded and A2 came out as the empty-body hash
twice, so the Digest response no longer bound the request method or the
target URI. Appending with appendBase16 keeps the hash in the buffer
already being built, which is what ha1 and newResponse already do for
HA1 and HA2. The two encoders emit identical lowercase, zero-padded hex,
so the digest is unchanged everywhere the branch was already correct.
Latent since #2148 replaced the EMPTY_ENTITY_MD5 constant with a
computed hash. The null-entityBodyHash branch is no longer reachable
from the request pipeline: #2276 wired setEntityBodyHash into
perRequestAuthorizationHeader and computeBodyHash never returns null, so
no wrong header reaches the wire today. It is still reached by the
nextnonce rotation in Interceptors and by Realms built through the
public API. The added RealmTest case checks the response against the
RFC 7616 A2 and fails on the current code.
Realm.Builder.ha2 writes A2 into the recycled StringBuilder that newResponse took from StringBuilderPool, but on the auth-int branch with no precomputed entity-body hash it calls toHexString, which takes that same thread-local builder and resets it, so the "POST:/secret:" already written is discarded. A2 comes out as the empty-body hash twice and the Digest response no longer binds the request method or the target URI. A server reaches this by answering with qop="auth-int" in WWW-Authenticate or Proxy-Authenticate, since parseRawQop picks auth-int when that is the only value offered.
Appending with appendBase16 keeps the hash in the buffer already being built, which is what newResponse does for HA1 and HA2 a few lines below. The added RealmTest case checks the response against the RFC 7616 A2 and fails on the current code.