import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.security.*; import java.security.spec.*; import java.util.*; /** Local Ed25519 ZBS signing; private key material is never printed or packaged. */ public class SignZBS { public static void main(String[] args) throws Exception { if(args.length<3) throw new IllegalArgumentException("init | sign "); Path dir=Path.of(args[1]); Path privatePath=dir.resolve("ed25519-private.der"), publicPath=dir.resolve("ed25519-public.der"); Path authorPath=dir.resolve("author.properties"); if(args[0].equals("init")) { if(!args[2].matches("[0-9]{17}")) throw new IllegalArgumentException("Invalid SteamID64"); Files.createDirectories(dir); if(Files.exists(privatePath)||Files.exists(publicPath)||Files.exists(authorPath)) throw new IllegalStateException("Signing identity already exists; reuse it rather than replacing it."); KeyPair pair=KeyPairGenerator.getInstance("Ed25519").generateKeyPair(); Files.write(privatePath,pair.getPrivate().getEncoded(),StandardOpenOption.CREATE_NEW); Files.write(publicPath,pair.getPublic().getEncoded(),StandardOpenOption.CREATE_NEW); Files.writeString(authorPath,"steamID64="+args[2]+"\n",StandardOpenOption.CREATE_NEW); Files.writeString(dir.resolve("public-profile-key.txt"),profileKey(pair.getPublic())+"\n",StandardOpenOption.CREATE_NEW); System.out.println("Created dedicated Ed25519 ZombieBuddy signing identity for SteamID64 "+args[2]+"."); return; } if(!args[0].equals("sign")) throw new IllegalArgumentException("Unknown operation"); Properties author=new Properties(); try(var reader=Files.newBufferedReader(authorPath)) {author.load(reader);} String sid=author.getProperty("steamID64"); if(sid==null||!sid.matches("[0-9]{17}")) throw new IllegalStateException("Invalid signing identity"); var factory=KeyFactory.getInstance("Ed25519"); var privateKey=factory.generatePrivate(new PKCS8EncodedKeySpec(Files.readAllBytes(privatePath))); var publicKey=factory.generatePublic(new X509EncodedKeySpec(Files.readAllBytes(publicPath))); Path jar=Path.of(args[2]); String sha=HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(jar))); byte[] payload=("ZBS:"+sid+":"+sha).getBytes(StandardCharsets.UTF_8); var signer=Signature.getInstance("Ed25519");signer.initSign(privateKey);signer.update(payload); byte[] signature=signer.sign(); var verifier=Signature.getInstance("Ed25519");verifier.initVerify(publicKey);verifier.update(payload); if(!verifier.verify(signature)) throw new IllegalStateException("Signing key pair does not match"); Path sidecar=Path.of(jar.toString()+".zbs"); Files.writeString(sidecar,"ZBS\nSteamID64:"+sid+"\nSignature:"+HexFormat.of().formatHex(signature)+"\n"); System.out.println("Signed and cryptographically verified "+jar.getFileName()+" (SteamID64 "+sid+")."); System.out.println("SHA-256: "+sha); } private static String profileKey(PublicKey key) { byte[] encoded=key.getEncoded(); byte[] prefix=HexFormat.of().parseHex("302a300506032b6570032100"); if(encoded.length!=44||!Arrays.equals(prefix,Arrays.copyOf(encoded,12))) throw new IllegalStateException("Unexpected Ed25519 public key encoding"); return "JavaModZBS:"+HexFormat.of().formatHex(Arrays.copyOfRange(encoded,12,44)); } }