You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Java读取JAR文件代码签名与证书信息的技术方案问询

嘿,我太懂你不想解析jarsigner输出的麻烦了——那玩意儿的输出格式偶尔会变,写解析逻辑很容易踩坑。其实Java本身就提供了一套原生API,能直接读取JAR的签名、证书和时间戳信息,完全不用依赖外部命令,比你之前的方案靠谱多了!

下面我就一步步教你怎么实现所有需求:

用Java原生API处理JAR签名信息

核心用到的是java.util.jar、java.security和javax.security.auth.x500包里的类,不用额外引入依赖,JDK自带就能用。

1. 检查JAR是否已签名 + 验证签名完整性

当你用JarFile打开JAR文件时,JVM会自动验证签名的完整性(只要你不关闭验证)。我们可以通过获取JarEntry的CodeSigner对象来判断是否已签名,同时验证签名是否未被篡改。

示例代码片段:

import java.io.File;
import java.util.jar.JarFile;
import java.util.jar.JarEntry;
import java.security.CodeSigner;

public boolean isJarSignedAndValid(File jarFile) throws Exception {
    try (JarFile jar = new JarFile(jarFile, true)) { // 第二个参数true表示开启签名验证
        // 遍历JAR里的非签名文件条目(跳过META-INF下的.SF/.RSA/.DSA等签名文件)
        for (JarEntry entry : jar.stream().toList()) {
            if (entry.isDirectory() || entry.getName().startsWith("META-INF/")) {
                continue;
            }
            // 尝试获取条目签名信息
            CodeSigner[] signers = entry.getCodeSigners();
            if (signers != null && signers.length > 0) {
                // 如果有签名,说明JAR已签名,且JVM已经验证过签名完整性(没抛异常就是有效)
                return true;
            }
        }
        // 遍历完所有条目都没找到签名信息,说明未签名
        return false;
    } catch (Exception e) {
        // 验证过程中抛出异常,说明签名无效(比如文件被篡改)
        System.err.println("签名验证失败:" + e.getMessage());
        return false;
    }
}

注意:如果JAR被篡改,JarFile在读取条目时会直接抛出SecurityException,这就是签名无效的信号。

2. 检查是否使用TSA时间戳

每个CodeSigner对象都包含时间戳信息,只要调用getTimestamp()方法,返回非null就说明签名时用了TSA:

import java.security.Timestamp;

// 接上面的代码,当找到CodeSigner时
for (CodeSigner signer : signers) {
    Timestamp timestamp = signer.getTimestamp();
    if (timestamp != null) {
        System.out.println("使用了TSA时间戳,时间:" + timestamp.getTimestamp());
        // 还能获取时间戳的证书信息
        System.out.println("TSA证书CN:" + timestamp.getSignerCertPath().getCertificates().get(0).getSubjectX500Principal().getName());
    } else {
        System.out.println("未使用TSA时间戳");
    }
}

3. 获取签名证书的CN、O等字段

从CodeSigner里拿到签名证书链,取第一个证书(就是直接签名的那个证书),然后解析其X.500主体名称:

import java.security.cert.X509Certificate;
import javax.security.auth.x500.X500Principal;

// 接上面的CodeSigner遍历
X509Certificate signerCert = (X509Certificate) signer.getSignerCertPath().getCertificates().get(0);
X500Principal subject = signerCert.getSubjectX500Principal();

// 解析CN(通用名称)、O(组织名称)等字段
String dn = subject.getName(X500Principal.RFC2253);
// 拆分DN字段,比如:CN=John Doe, O=Example Corp, L=New York
String[] dnParts = dn.split(", ");
for (String part : dnParts) {
    if (part.startsWith("CN=")) {
        System.out.println("证书CN:" + part.substring(3));
    } else if (part.startsWith("O=")) {
        System.out.println("证书组织O:" + part.substring(2));
    }
}

4. 获取证书过期时间 + 签名相关时间

  • 证书的过期时间直接从X509Certificate的getNotAfter()方法获取:
System.out.println("证书过期时间:" + signerCert.getNotAfter());
  • 如果有TSA时间戳,时间戳的时间就是timestamp.getTimestamp(),这个可以作为签名的可信时间(比证书生效时间更准确)。
为什么这个方案更优?
  • 完全原生:不用调用外部命令,避免了跨平台兼容性问题(比如Windows和Linux的jarsigner输出格式差异)
  • 更灵活:可以自定义验证逻辑,比如只验证特定证书的签名,或者批量处理多个JAR
  • 更可靠:JVM原生的签名验证逻辑比自己解析输出要严谨得多,不容易出错

内容的提问来源于stack exchange,提问作者PaulEdison

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:41:49