Class encryption
Everything else makes your bytecode hard to read. ClassGuard removes it from disk entirely: your classes ship as encrypted blobs with no loadable bytecode to decompile, unsealed only in memory, only at load time. It's the strongest single option in Lock Master.
What it does
With classGuard on, each of your classes is encrypted with strong authenticated
encryption and the original .class file is stripped from the jar. In its place ships
an opaque encrypted blob. A tiny generated bootstrap loader — the only real bytecode left — reconstructs the
key at runtime and unseals each class on demand, in memory. Nothing decrypted ever touches the disk.
{ "classGuard": { "enabled": true, "mode": "STANDALONE" } }
The result: a decompiler opening your jar finds almost nothing to work with. No class names, no methods, no strings — just ciphertext and a loader.
Before & after
Real output. A six-class jar, before and after ClassGuard. Every business class is gone from the file
listing — replaced by encrypted .bin blobs under
META-INF/guardian/ — and only the two-class bootstrap loader remains as openable
bytecode:
demo/Heavy.class
demo/Report.class
demo/Sample.class
shop/App.class
shop/model/Cart.class
shop/util/Money.class
a/a.class
b/b.class
META-INF/guardian/-4512027295375453326.bin
META-INF/guardian/5166365122521541383.bin
META-INF/guardian/-5206015750035504180.bin
META-INF/guardian/-5405373286271541278.bin
META-INF/guardian/6344447860271584778.bin
META-INF/guardian/7602776200188678415.bin
The .bin files are high-entropy ciphertext — no readable strings, no class
structure, nothing a decompiler can parse. Your Sample,
Report, Cart and the rest simply aren't there as bytecode
any more. To read them, an attacker has to defeat the encryption and capture the classes as the JVM
unseals them at runtime — which is the path antiDebug closes.
Modes & fields
ClassGuard needs an entry point where it can unseal your classes before anything uses them. That's the only real decision: where your program starts.
| Field | What it does |
|---|---|
| enabled | Turn on whole-class encryption. |
| mode | STANDALONE — for normal apps: a generated launcher class becomes the main entry point and unseals everything on startup. PLUGIN — for Minecraft plugins (Bukkit / Spigot / Paper): Lock Master renames and encrypts your real main and ships a small cleartext stub under your plugin.yml main name; the server loads the stub, which boots the loader and hands off to your real main. Rename must be on. |
| pluginMainClass | Required for PLUGIN mode. The internal name of your plugin's main class (matching plugin.yml). |
| excludeClasses | Classes or prefixes to leave unencrypted — anything loaded very early, or by name, before the loader is ready. In PLUGIN mode you don't list your main here: it's handled for you. |
PLUGIN mode you
set pluginMainClass and that's it: don't add your main to
keep or excludeClasses. Lock Master renames and encrypts your
real main and ships a cleartext stub under your plugin.yml name for the server to
load, so the main must be renamed (keeping it is rejected). This differs from plain renaming without
ClassGuard, where you do keep the main
because plugin.yml isn't rewritten.antiDebug
Sealing the classes closes static analysis, so the remaining attack is dynamic: run the program under a
debugger and dump the classes as they're unsealed. antiDebug closes that door. At a
ClassGuard entry point it inspects the JVM's launch configuration and, if a debugger is attached, refuses —
so the encrypted classes are never unsealed under observation.
| Field | What it does |
|---|---|
| enabled | Turn it on. Needs ClassGuard to have an entry point. |
| action | "throw" — refuse to unseal (fail to enable/load). "exit" — kill the JVM outright. |
| checkAgents | Also flag -javaagent / -agentpath. Off by default — many legitimate servers run monitoring or profiling agents, so only enable it if yours doesn't. |
watermark
An invisible, recoverable identifier stamped redundantly across a jar, so a leaked copy traces back to whoever it was handed to. It's split and checksummed across classes, so it survives re-jarring or partial deletion, and reading it back needs a secret that never ships in the build.
rymga-cli watermark stamp, and recover it from a leak
with watermark trace — both run server-side. See
Per-buyer watermark.Before you ship
- Test on a real run. ClassGuard changes how your program loads — always run the sealed build on a real server or launch before shipping.
- Works alongside other plugins. The guard now loads its own encrypted classes
child-first, so two plugins whose obfuscated classes end up with the same short name no longer
collide on a shared server — the failure that used to surface as a
ClassNotFoundException/NoSuchMethodErrorwhen another plugin was installed. Async scheduler tasks and threads your own code starts resolve encrypted classes correctly too. - One edge remains: name-based loading from a foreign thread. A bundled library that loads
a class by name off a thread it started itself, using that thread's context class loader rather than
its own (some builds of bStats do this), can still miss an encrypted class. If a sealed build throws
ClassNotFoundExceptionfrom inside such a library, list it inexcludeClasses, or skip ClassGuard for that jar — rename + string encryption + flow still protect strongly. See the real-plugin example. - It's non-reproducible by design. The encryption key is drawn from a secure random source, so a ClassGuard build differs every time even with a fixed seed. That's deliberate — a reproducible key would defeat the purpose.
- Mind the entry point. Keep whatever starts your program (or is loaded before the loader
runs) in
excludeClasses.