0x0000 CAFEBABE major=70 minor=0 JDK 8 through 26

Your Java, compiled to native. Not just renamed.

RJNI translates real JVM bytecode into a Rust cdylib and relinks it back in through JNI, then obfuscates whatever is left. A decompiler does not see confusing code. It sees a method with no body at all.

The pipeline

8 independent passes

Each protection feature is its own pass, chained in order. Nothing below is planned. Every row runs in the current build.

0x00

Nativization

Eligible methods are translated to Rust, compiled to a real cdylib, and relinked through RegisterNatives. The method body is not in the jar anymore.

0x08

Name obfuscation

Classes, and non-private methods and fields wherever a hierarchy is provably self-contained, get short opaque names, not your original API surface.

0x10

String encryption

String constants are encrypted per build and decrypted at runtime. A raw scan of the jar or the compiled native library does not recover them.

0x18

Control-flow flattening

Eligible methods get their control flow rewritten into an encrypted dispatch table, defeating a plain reconstruction of the original graph.

0x20

Decompiler crashers

Bytecode shapes specifically chosen to make common decompilers fail or produce garbage, not just harder to follow.

0x28

Anti-tamper and anti-debug

Per-class checksums catch a patched class file at load time. A JDWP check catches an attached debugger. Both fail loudly, not silently.

0x30

Resource encryption

Bundled resources loaded through getResourceAsStream are transparently encrypted and decrypted at the call site.

0x38

Loader hardening

The injected runtime bootstrap classes are renamed and protected through the same pipeline as your own code, not left as an unobfuscated giveaway.

Threat model

stated plainly

This is a tier-one protection posture. It raises the bar for a casual to moderate attacker. It is not a claim of unbreakable, and nothing below pretends otherwise.

ThreatEffect
Casual decompilation (CFR, Fernflower, JD-GUI)DEFEATED no method body to decompile, several decompilers crash outright on the surrounding shell
Raw scan for string literalsDEFEATED string constants are encrypted, verified against the actual compiled output
Class, method, and field name readingDEFEATED wherever a hierarchy is self-contained; JDK-facing names are conservatively left alone rather than risk breaking your program
Casual bytecode patchingDEFEATED anti-tamper checksums fail loudly at load time
An attached debugger (JDWP)DEFEATED detected, fails loudly
A determined reverse engineer with the compiled library and unlimited timeOUT OF SCOPE see the FAQ
An attacker who reimplements this exact loader logicPARTIAL raises the bar, does not remove the seam. Same posture as every native-code obfuscator on the market

FAQ

6 entries

Versus GraalVM native-image

native-image compiles your whole application ahead of time into one standalone executable. It is a deployment and startup-time tool with real constraints on reflection, dynamic class loading, and JVM feature coverage. RJNI does the opposite trade: your jar stays a normal jar on a normal JVM, and only the methods safe to translate get nativized. Reflection, dynamic loading, your existing deployment: all untouched. native-image aims for faster startup and a smaller footprint. RJNI aims to make compiled logic hard to read while the jar keeps behaving exactly like a jar.

Does this make code faster

No, and it is not trying to. Crossing the JNI boundary has a real, mostly fixed cost per call. A trivial method that is mostly overhead can see a real slowdown; a method doing meaningful work sees a much smaller relative cost. Treat any speed change as a side effect, not the point.

JDK version support

Class file major versions 52 through 70: JDK 8 through JDK 26.

Reflection, proxies, and frameworks

RJNI ships pluggable exclusion profiles for the common cases: reflection by name, ServiceLoader, JPMS module descriptors, and common proxy-framework internals. Matched code is excluded from nativization and renaming rather than silently broken. Custom keep rules cover anything the built-in profiles do not.

Platform coverage

Windows, macOS (Intel and Apple Silicon), and Linux x86-64/ARM64. Coverage is tiered honestly in the docs: stable versus experimental, not one blanket claim.

Code signing

The output is not signed. RJNI ships a jar with a bundled native library, loaded in-process through System.load() from already-running JVM code. The shell-launched-executable warning that Windows SmartScreen and macOS Gatekeeper apply does not target this shape. The one real exception is macOS quarantine on a browser-downloaded jar, which has a documented one-line workaround.