如何验证签名的 jar 是否包含时间戳?
在对 jar 进行签名并使用 -tsa 选项后,如何验证是否包含时间戳?我尝试过:
jarsigner -verify -verbose -certs myApp.jar
但输出没有指定任何有关时间戳的信息。我这样问是因为即使我在 -tsa URL 路径中有拼写错误,jarsigner 也会成功。这是 GlobalSign TSA URL:http://timestamp.globalsign.com/scripts/timstamp.dll< /a> 并且它后面的服务器显然接受任何路径(即timestamp.globalsign.com/foobar),所以最后我不确定我的jar是否带有时间戳。
After a jar is signed and the -tsa option was used, how can I validate that the time stamp was included? I tried:
jarsigner -verify -verbose -certs myApp.jar
But the output does not specify anything about the time stamp. I'm asking because even if I have a typo in the -tsa URL path, the jarsigner succeeds. This is the GlobalSign TSA URL: http://timestamp.globalsign.com/scripts/timstamp.dll and the server behind it apparently accepts any path (ie. timestamp.globalsign.com/foobar), so in the end I'm not really sure my jar is time stamped or not.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

发布评论
评论(4)
mhaller 提供了很棒的代码(printDSAInfos)。对我的工作有很大帮助。然而,需要进行一些更改。
DEEncodable 类现已更改为 ASN1Encodable,getDERObject() 方法更改为 toASN1Primitive。所以代码看起来像这样
ASN1Encodable dob = attribute.getAttrValues().getObjectAt(0);
CMSSignedData signedData = new CMSSignedData(dob.toASN1Primitive().getEncoded());
刚刚花了 2 个小时寻找这个问题,终于找到了一种方法来识别 jar 文件中是否确实包含签名块文件中的时间戳信息。我可以在 /META-INF/FOO.DSA 文件的十六进制编辑器中看到 GlobalSign 证书,但我没有找到任何可以打印出您需要的信息的工具。
您可以将 FOO.DSA 文件重命名为 foo.p7b 以在 Windows CertMgr 中打开它,但它也不会显示任何时间戳信息。我也没有设法使用 OpenSSL 来验证 DSA 文件(它是 PKCS#7 文件格式)。
因此,我想出了以下代码,它将显示时间戳 SignerInfo 以及创建时间戳的日期。我希望这对您来说是一个良好的开始。
您需要在类路径中包含 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;
}
}
}
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
Java 的
keytool
可以确认签名的 JAR 是否带有时间戳,还可以显示 TSA 的证书:Java's
keytool
can confirm whether a signed JAR is timestamped, and can also display the TSA's certificate: