Java桌面应用更新Jar:如何确保JVM仅运行本人签名的未篡改Jar?
确保JVM仅运行经验证签名Jar包的方案
核心逻辑
由于更新后的Jar只能存储在用户可写的不安全目录,必须依赖原始安装包内的可信组件(受MSIX/macOS系统保护,无法被第三方篡改)完成校验,所有校验逻辑必须由应用目录内的程序触发执行,绝对不能信任用户目录内的任何文件。
具体实现步骤
固化可信根到应用目录
将你的数字证书公钥(或签名后的公钥证书文件)打包进原始安装包的固定资源目录(比如/resources/trusted_public.key)。这个目录受系统权限保护,是整个校验流程的唯一可信源。更新包与签名文件同步分发
每次发布更新Jar时,同步生成对应签名文件:- 计算Jar包的SHA-256哈希值;
- 用你的私钥对该哈希值签名,生成独立的
.sig文件(也可以直接给Jar添加官方签名块,利用Jar自带的签名机制); - 将Jar和签名文件同时部署到更新服务端。
加载前的双重校验(必须由应用内可信启动器执行)
禁止直接运行用户目录中的Jar,必须通过原始安装包自带的启动器(Java类或原生程序)加载:- 完整性校验:计算用户目录内Jar包的SHA-256哈希值,与签名文件中的哈希值比对,不一致则判定Jar被篡改;
- 签名合法性校验:用应用目录内的可信公钥验证签名文件的有效性——只有你的私钥签名的哈希值才能通过公钥验证,确保Jar由你官方发布;
- 任一校验失败时,立即删除用户目录内的可疑Jar,回退到应用目录的原始Jar运行,并提示用户更新异常。
JVM层面的强制拦截
若用Java编写启动器,可利用JarFile类API完成Jar签名校验:JarFile jarFile = new JarFile("path/to/user/directory/update.jar"); // 验证Jar签名的完整性 jarFile.verify(); // 加载应用目录内的可信公钥 PublicKey trustedPubKey = loadTrustedPublicKeyFromAppResources(); // 校验Jar签名证书与可信公钥匹配 Certificate[] certs = jarFile.getManifest().getCertificates(); for (Certificate cert : certs) { if (cert instanceof X509Certificate) { ((X509Certificate) cert).checkValidity(); cert.verify(trustedPubKey); } } // 校验通过后,通过URLClassLoader加载Jar并启动 URLClassLoader classLoader = new URLClassLoader(new URL[]{new URL("jar:file:" + jarFile.getName() + "!/")}); classLoader.loadClass("com.your.package.MainClass").getMethod("main", String[].class).invoke(null, new Object[]{new String[]{}});额外安全加固
- 存储Jar时用应用目录内的密钥加密,即使用户目录被篡改,恶意程序也无法直接获取可运行的Jar;
- 每次启动应用都重新校验用户目录内的Jar,避免合法Jar被事后篡改。
内容的提问来源于stack exchange,提问作者Scott Pierce
相关产品推荐
相关产品推荐

