Java 如何以编程方式验证使用jarsigner签名的jar

Java 如何以编程方式验证使用jarsigner签名的jar,java,code-signing,signature,jarsigner,Java,Code Signing,Signature,Jarsigner,我想使用jarsigner对一个jar进行签名,然后使用一个Java应用程序验证它,该应用程序的类路径中没有签名的jar(即,只使用jar的文件系统位置) 现在我的问题是从jar中取出签名文件,有没有简单的方法 我玩过充气机和罐子输入流,运气不好 或者这是可以用更好的方式完成的事情 谢谢本手册概述了验证JAR的过程。尽管这些说明是供JCA加密服务提供商进行自我验证的,但它们应该适用于您的问题 具体来说,请查看示例代码中的验证(X509Certificate targetCert)方法。您可以使用

我想使用jarsigner对一个jar进行签名,然后使用一个Java应用程序验证它,该应用程序的类路径中没有签名的jar(即,只使用jar的文件系统位置)

现在我的问题是从jar中取出签名文件,有没有简单的方法

我玩过充气机和罐子输入流,运气不好

或者这是可以用更好的方式完成的事情

谢谢

本手册概述了验证JAR的过程。尽管这些说明是供JCA加密服务提供商进行自我验证的,但它们应该适用于您的问题


具体来说,请查看示例代码中的
验证(X509Certificate targetCert)
方法。

您可以使用jarsigner应用程序来执行此操作。在processbuilder(或Runtime.exec)中,可以使用以下参数运行命令

 ProcessBulider pb = new ProcessBuilder("/usr/bin/jarsigner", "-verify", "-certs", f.getAbsolutePath());
如果输出已验证,则jar已签名

Process p = pb.start();
p.waitFor();
InputStream is = p.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null)
{
if(line.contains("verified");
...

有了jarsigner代码的输出后,您可以做更复杂的事情。

您只需使用java.util.JAR.JAR文件打开JAR并告诉它验证JAR文件即可。如果JAR已签名,那么JarFile可以选择验证它(默认情况下是打开的)。但是,JarFile也会愉快地打开未签名的JAR,因此您还必须检查文件是否已签名。您可以通过检查JAR清单中的*-摘要属性来实现这一点:具有此类属性的元素被签名

例如:

JarFile jar = new JarFile(new File("path/to/your/jar-file"));

// This call will throw a java.lang.SecurityException if someone has tampered
// with the signature of _any_ element of the JAR file.
// Alas, it will proceed without a problem if the JAR file is not signed at all
InputStream is = jar.getInputStream(jar.getEntry("META-INF/MANIFEST.MF"));
Manifest man = new Manifest(is);
is.close();

Set<String> signed = new HashSet();
for(Map.Entry<String, Attributes> entry: man.getEntries().entrySet()) {
    for(Object attrkey: entry.getValue().keySet()) {
        if (attrkey instanceof Attributes.Name && 
           ((Attributes.Name)attrkey).toString().indexOf("-Digest") != -1)
            signed.add(entry.getKey());
    }
}

Set<String> entries = new HashSet<String>();
for(Enumeration<JarEntry> entry = jar.entries(); entry.hasMoreElements(); ) {
    JarEntry je = entry.nextElement();
    if (!je.isDirectory())
        entries.add(je.getName());
}

// contains all entries in the Manifest that are not signed.
// Ususally, this contains:
//  * MANIFEST.MF itself
//  * *.SF files containing the signature of MANIFEST.MF
//  * *.DSA files containing public keys of the signer

Set<String> unsigned = new HashSet<String>(entries);
unsigned.removeAll(signed);

// contains all the entries with a signature that are not present in the JAR
Set<String> missing = new HashSet<String>(signed);
missing.removeAll(entries);
JarFile jar=newjarfile(新文件(“path/to/your/jar文件”);
//如果有人篡改,此调用将抛出java.lang.SecurityException
//带有JAR文件中任意元素的签名。
//唉,如果JAR文件根本没有签名,它将毫无问题地继续进行
InputStream=jar.getInputStream(jar.getEntry(“META-INF/MANIFEST.MF”);
舱单人员=新舱单(is);
is.close();
Set signed=new HashSet();
对于(Map.Entry:man.getEntries().entrySet()){
对于(对象属性:entry.getValue().keySet()){
if(Attributes.Name&&
((Attributes.Name)attrkey.toString().indexOf(“-Digest”)!=-1)
signed.add(entry.getKey());
}
}
Set entries=newhashset();
for(枚举项=jar.entries();entry.hasMoreElements();){
JarEntry je=entry.nextElement();
如果(!je.isDirectory())
entries.add(je.getName());
}
//包含清单中所有未签名的条目。
//通常,这包括:
//*MANIFEST.MF本身
//***.SF文件,包含MANIFEST.MF的签名
//**.DSA文件包含签名者的公钥
Set unsigned=新哈希集(条目);
未签名。移除所有(已签名);
//包含所有带有JAR中不存在的签名的条目
Set missing=新哈希集(已签名);
丢失。删除所有(条目);

您可以使用entry.getCodeSigners()获取JAR中特定条目的签名者

确保在调用entry.getCodeSigners()之前打开verify=true的JAR文件并完全读取JAR条目

类似的内容可用于验证不是签名文件的每个条目:

boolean verify = true;
JarFile jar = new JarFile(signedFile, verify);

// Need each entry so that future calls to entry.getCodeSigners will return anything
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
   JarEntry entry = entries.nextElement();
   IOUtils.copy(jar.getInputStream(entry), new NullOutputStream());
}

// Now check each entry that is not a signature file
entries = jar.entries();
while (entries.hasMoreElements()) {
    JarEntry entry = entries.nextElement();
    String fileName = entry.getName().toUpperCase(Locale.ENGLISH);
    if (!fileName.endsWith(".SF")
       && !fileName.endsWith(".DSA")
       && !fileName.endsWith(".EC")
       && !fileName.endsWith(".RSA")) {

       // Now get code signers, inspect certificates etc here...
       // entry.getCodeSigners();
    }
 }
布尔验证=true;
JarFile jar=新的JarFile(signedFile,verify);
//需要每个条目,以便将来对entry.getCodeSigners的调用将返回任何内容
枚举条目=jar.entries();
while(entries.hasMoreElements()){
JarEntry=entries.nextElement();
copy(jar.getInputStream(entry),new NullOutputStream());
}
//现在检查每个不是签名文件的条目
entries=jar.entries();
while(entries.hasMoreElements()){
JarEntry=entries.nextElement();
字符串文件名=entry.getName().toUpperCase(Locale.ENGLISH);
如果(!fileName.endsWith(“.SF”)
&&!fileName.endsWith(“.DSA”)
&&!fileName.endsWith(“.EC”)
&&!fileName.endsWith(“.RSA”)){
//现在在这里获取代码签名者,检查证书等。。。
//entry.getCodeSigners();
}
}

与其建议代码,不如提供一个verifyAllContent()方法;-)该示例是特定于平台的,此外,jarsigner工具通常只随JDK提供。jarsigner不会验证签名者证书(因此任何不受信任的签名都可以),它不会检查受信任的时间戳(无法处理证书过期的签名者的有效签名),并且其输出是无用的(“verified”即使在出现错误时也会返回,通过解析错误和警告消息可以获得更多的信息,但这仍然不足以决定证书是否有效)不验证JAR中的类,它只启用访问时发生的验证。因此,要验证JAR的所有条目,必须调用JAR.getInputStream(..)对于每个条目-这将触发验证。有关实现Robert建议的一些代码,请参阅,没有参考用于验证签名的密钥库(用于签署JAR的密钥库)@madduci-我和你有同样的问题。你知道如何使用用于签名的密钥库的公钥进行验证吗?非常感谢任何帮助。@学习者我已经向Oracle报告了一个关于已签名JAR的问题。这里的代码是一个编写的示例