2009-10-30 86 views
18

簽名jar並使用-tsa選項後,如何驗證是否包含時間戳?我試過了:如何驗證簽名的jar包含時間戳?

jarsigner -verify -verbose -certs myApp.jar 

但是輸出沒有指定任何關於時間戳的信息。我問,因爲即使我在-tsa URL路徑中有錯字,jarsigner也會成功。這是GlobalSign的TSA網址:http://timestamp.globalsign.com/scripts/timstamp.dll,它後面的服務器顯然接受任何路徑(即timestamp.globalsign.com/foobar),所以最終我不確定我的jar是否有時間戳。

回答

9

剛剛花了最後2個小時尋找這個問題,並最終找到了一種方法來確定一個jar文件實際上是否包含簽名塊文件中的時間戳信息。我可以在/META-INF/FOO.DSA文件的hexeditor中看到GlobalSign的證書,但是我沒有找到任何工具來打印出您需要的信息。

您可以將FOO.DSA文件重命名爲foo.p7b以在Windows CertMgr中打開它,但它也不顯示任何時間戳記信息。我也沒有設法使用OpenSSL來驗證DSA文件(它是PKCS#7文件格式)。

所以我想出了下面的代碼,它將顯示時間戳SignerInfo和創建時間戳的日期。我希望這對你來說是一個好的開始。 您需要在classpath中使用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; 
     } 
    } 
} 
+0

謝謝,作品。 CMS的東西需要bcmail-jdk16-144.jar – user199092 2009-11-01 17:17:12

+0

非常感謝你的努力和時間。 – Edenshaw 2013-05-03 16:55:04

+1

我也使用它,但必須改變'endsWith(「。dsa」)'來檢查rsa。 – JimN 2013-11-09 01:48:18

15

讓他們從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 2015-10-29 10:11:59

+0

這對派對來說有點晚,但如果消化算法很重要,這可能很重要。如果你需要看看它是如何被打上時間戳的,你需要一個jdk8u111或更新的jarsigner。然後,使用-verify -verbose -certs,它會在最後顯示:「時間戳記摘要算法:SHA-1,時間戳簽名算法:SHA1withRSA,2048位密鑰」。如果您必須支持SHA-256與SHA256混合導致問題的混合java7安裝,那麼這非常重要。 – 2017-03-23 16:05:21

5

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 

... 
1

mhaller提供了極大的代碼(printDSAInfos)。在我的工作中給予我極大的幫助。但是需要進行一些更改。 DEREncodable類現在更改爲ASN1Encodable,getDERObject()方法更改爲ASN1Primitive。所以代碼看起來像這樣

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