Every LDC of a String literal inside a method body is replaced
with a per-string, position-keyed obfuscated payload, plus a call to a runtime decrypt
routine at the point of use. Each UTF-8 byte is XORed with a key rotated by its position,
so a repeated plaintext byte doesn't produce a repeated ciphertext byte. The result is
Base64-encoded so it embeds as an ordinary class-file string constant.
Covers LDC of a string literal used inside a method body. Does
not cover a static final String field's own compile-time
constant value: other classes compiled against that field may already have inlined the
plaintext into their own constant pool at compile time, a copy this pass
cannot reach. Encrypting only the field's declaration would look complete without
being complete, so the field is left alone.
Runs before nativization's own eligibility scan. This means a method that later gets nativized never has its plaintext string embedded in the generated Rust code either. The compiled native code calls back into the same runtime decrypt routine through a JNI static-method call, and holds the string in Java-side memory only briefly, at the point it's used. This wasn't designed as a combined feature. It follows from the pipeline's fixed ordering, and it has been verified.
Raises the bar against a static scan for string literals inside a method body, such as
a plaintext search of the disassembly or a "strings" command run against the jar or the
compiled native library. It does not resist a debugger reading the decrypted value at
its actual point of use, and it doesn't cover a static final String's own
constant declaration (see Scope above).
strings output
for methods that were later nativized also found it absent, so the effect of string
encryption survives translation to native code as well as in the remaining bytecode.