Serguey Asael Shinder: What NFKC does to a password, and where to look in the JDK
One of the CVEs closed by Quarkus 3.39.5 is described as password keyspace reduction via NFKC fullwidth folding in WildFly Elytron (CVE-2026-19611, listed at https://quarkus.io/blog/quarkus-3-39-5-released/). The phrase is worth unpacking, because the mechanism lives in a JDK class most code uses without thinking about which constant it passes.
java.text.Normalizer.normalize(CharSequence, Normalizer.Form) takes one of four forms - NFC, NFD, NFKC, NFKD. The javadoc is explicit about the difference that matters here: the K forms apply compatibility decomposition, which the specification describes as replacing characters with compatibility equivalents rather than canonical ones. Reference: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/text/Normalizer.html and the underlying report at https://www.unicode.org/reports/tr15/
The practical consequence for a secret is that NFKC is lossy by design. Fullwidth Latin characters - the ones in the U+FF01..U+FF5E block used in CJK input contexts - are compatibility equivalents of ordinary ASCII, so under NFKC a fullwidth A and an ASCII A become the same character. Ligatures collapse, superscripts flatten to digits, and certain spaces become U+0020. Each of those collapses maps several distinct inputs onto one output.
For display and for searching, that is exactly what you want: a user typing either form finds the same record. For a password it means the set of distinct secrets is smaller than the set of distinct strings a user could type, and "keyspace reduction" is the accurate name for it. An attacker enumerating candidates does not need to try both forms.
⚠ No JDK is installed on the machine these notes are written on, so nothing above is a measurement - it is what the javadoc and UAX #15 specify, with the links to check.

What to do with it
The rule that falls out is narrow and easy to apply: normalise for comparison, not for storage of secrets - and if you normalise a password at all, use a canonical form, not a compatibility one. NFC preserves the distinction between fullwidth and ASCII while still fixing the genuine problem normalisation exists for, which is that the same visible character can arrive as one code point or as a base plus a combining mark.
The trap is that NFKC is often the reasonable default elsewhere in the same codebase - for usernames, search keys, identifiers - so the wrong constant arrives by consistency rather than by carelessness. That is the part worth grepping for: not whether you call normalize, but whether the form you pass changes between the field that must be forgiving and the field that must be precise.