如何验证一个已签名的JAR文件是否包含时间戳?

20

在对一个jar文件进行签名并使用了-tsa选项之后,如何验证时间戳已经被包含?我尝试过:

jarsigner -verify -verbose -certs myApp.jar

但输出没有指定时间戳的任何信息。我之所以问这个问题,是因为即使我在-tsa URL路径中有错别字,jarsigner 也会成功。这是GlobalSign TSA URL:http://timestamp.globalsign.com/scripts/timstamp.dll,位于其后面的服务器显然接受任何路径(例如timestamp.globalsign.com/foobar),因此最终我不确定我的jar文件是否已经打上了时间戳。

4个回答

21

https://blogs.oracle.com/mullan/entry/how_to_determine_if_a

您可以使用jarsigner工具来确定已签名的JAR是否已被时间戳标记,方法如下:

jarsigner -verify -verbose -certs signed.jar

其中,signed.jar是您签署的JAR文件的名称。如果已经进行了时间戳标记,则输出将包括以下行,指示签名时间:

[entry was signed on 8/2/13 3:48 PM]

如果未对JAR文件进行时间戳标记,则输出将不包含这些行。


1
那其实是最好的答案! - thokuest
1
这可能有点晚了,但如果摘要算法很重要的话,它就很重要。如果您需要查看时间戳,请使用jdk8u111或更新版本的jarsigner。然后,使用-verify -verbose -certs,它将在最后显示:“时间戳摘要算法:SHA-1,时间戳签名算法:SHA1withRSA,2048位密钥”。然后,如果您必须支持混合java7安装,其中SHA-256与SHA256混淆会导致问题,则非常重要。 - Chris Holt

10

我刚刚花了最近2个小时来寻找一个问题的答案,最终找到了一种方式来确认一个jar文件是否有在签名块文件中包含时间戳信息。我可以在/META-INF/FOO.DSA文件的十六进制编辑器中看到GlobalSign证书,但我没有找到任何可以打印所需信息的工具。

你可以将FOO.DSA文件重命名为foo.p7b并在Windows CertMgr中打开它,但是它也不显示任何时间戳信息。我也无法使用OpenSSL验证DSA文件(它是PKCS#7文件格式)。

因此,我编写了以下代码,将显示时间戳签名者信息和时间戳创建日期。希望对你有所帮助。你需要在类路径中获取bcprov-jdk16-144.jar、bctsp-jdk16-144.jar和bcmail-jdk16-144.jar。从Bouncycastle获取它们。

package de.mhaller.bouncycastle;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.Security;
import java.util.Collection;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import org.bouncycastle.asn1.DEREncodable;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerId;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.tsp.TimeStampTokenInfo;

public class VerifyTimestampSignature {

    private static boolean found;

    public static void main(String[] args) throws Exception {
        if (args == null || args.length != 1) {
            System.out.println("usage: java " + VerifyTimestampSignature.class.getName()
                    + " [jar-file|dsa-file]");
            return;
        }

        BouncyCastleProvider provider = new BouncyCastleProvider();
        Security.addProvider(provider);

        String filename = args[0];

        if (filename.toLowerCase().endsWith(".dsa")) {
            InputStream dsa = new FileInputStream(filename);
            printDSAInfos(filename, dsa);
            return;
        }

        if (filename.toLowerCase().endsWith(".jar")) {
            InputStream jar = new FileInputStream(filename);
            JarInputStream jarInputStream = new JarInputStream(jar);
            JarEntry nextJarEntry;
            do {
                nextJarEntry = jarInputStream.getNextJarEntry();
                if (nextJarEntry == null) {
                    break;
                }
                if (nextJarEntry.getName().toLowerCase().endsWith(".dsa")) {
                    printDSAInfos(nextJarEntry.getName(), jarInputStream);
                }
            } while (nextJarEntry != null);
        }

        if (!found) {
            System.out.println("No certificate with time stamp information found in " + filename);
        } else {
            System.out.println("Found at least one time stamp info");
            System.out.println("Note: But it was NOT verified for validity!");
        }
    }

    private static void printDSAInfos(String file, InputStream dsa) throws CMSException,
            IOException, TSPException {
        System.out.println("Retrieving time stamp token from: " + file);
        CMSSignedData signature = new CMSSignedData(dsa);
        SignerInformationStore store = signature.getSignerInfos();
        Collection<?> signers = store.getSigners();
        for (Object object : signers) {
            SignerInformation signerInform = (SignerInformation) object;
            AttributeTable attrs = signerInform.getUnsignedAttributes();
            if (attrs == null) {
                System.err
                        .println("Signer Information does not contain any unsigned attributes. A signed jar file with Timestamp information should contain unsigned attributes.");
                continue;
            }
            Attribute attribute = attrs.get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
            DEREncodable dob = attribute.getAttrValues().getObjectAt(0);
            CMSSignedData signedData = new CMSSignedData(dob.getDERObject().getEncoded());
            TimeStampToken tst = new TimeStampToken(signedData);

            SignerId signerId = tst.getSID();
            System.out.println("Signer: " + signerId.toString());

            TimeStampTokenInfo tstInfo = tst.getTimeStampInfo();
            System.out.println("Timestamp generated: " + tstInfo.getGenTime());
            found = true;
        }
    }
}

谢谢,可以了。还需要bcmail-jdk16-144.jar来支持CMS功能。 - user199092
非常感谢您的努力和时间。 - Edenshaw
1
我也让它工作了,但是不得不将 'endsWith(“.dsa”)' 更改为检查 rsa。 - JimN
@mhaller,能否请您添加您代码的一个示例输出? - Anil Jadhav

7

Java的 keytool 可以确认签名的JAR是否有时间戳,并且可以显示TSA的证书:

$ keytool -printcert -jarfile myApp.jar

...

Timestamp:

Owner: CN=GeoTrust Timestamping Signer 1, O=GeoTrust Inc, C=US
Issuer: CN=Thawte Timestamping CA, OU=Thawte Certification, O=Thawte, L=Durbanville, ST=Western Cape, C=ZA
Serial number: 5e8d2daca44665546bb587978191a8bf
Valid from: Wed Oct 31 00:00:00 GMT 2007 until: Mon Oct 30 23:59:59 GMT 2017
Certificate fingerprints:
     MD5:  E5:30:07:8E:91:8D:A0:6C:18:6D:91:2A:B6:D2:3A:56
     SHA1: 22:3C:DA:27:07:96:73:81:6B:60:8A:1B:8C:B0:AB:02:30:10:7F:CC
     SHA256: D7:B8:44:BD:39:5A:17:36:02:39:51:C6:4D:6C:81:65:45:93:AD:29:1D:DC:E4:6C:8D:79:B6:65:DF:31:0C:F6
     Signature algorithm name: SHA1withRSA
     Version: 3

...

2

mhaller提供了很棒的代码(printDSAInfos)。在我的工作中帮助了我很多。然而需要进行一些更改。 DEREncodable类现在已经更改为ASN1Encodable,getDERObject()方法已更改为toASN1Primitive。因此,代码看起来像这样:

    ASN1Encodable dob = attribute.getAttrValues().getObjectAt(0);
    CMSSignedData signedData = new CMSSignedData(dob.toASN1Primitive().getEncoded());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接