我必须传递CMSSignedData对象中的哪些数据才能生成有效的时间戳?

时间:2015-02-19 20:03:19

标签: java validation bouncycastle pkcs#7 trusted-timestamp

我有一个有效的PKCS7文件加载到CMSSignedData对象中。 此PKCS7文件包含纯文本消息和有效的附加数字签名(全部在同一文件中)。

现在我想给这个文件加盖时间戳。这是我使用的代码(source):

 private static CMSSignedData addTimestamp(CMSSignedData signedData)
throws Exception {
        Collection ss = signedData.getSignerInfos().getSigners();
        SignerInformation si = (SignerInformation) ss.iterator().next();

        TimeStampToken tok = getTimeStampToken();

        ASN1InputStream asn1InputStream = new ASN1InputStream
(tok.getEncoded());
        DERObject tstDER = asn1InputStream.readObject();
        DERSet ds = new DERSet(tstDER);

        Attribute a = new Attribute(new
DERObjectIdentifier("1.2.840.113549.1.9.16.2.14"), ds);
        DEREncodableVector dv = new DEREncodableVector();
        dv.add(a);
        AttributeTable at = new AttributeTable(dv);
        si = SignerInformation.replaceUnsignedAttributes(si, at);
        ss.clear();
        ss.add(si);
        SignerInformationStore sis = new SignerInformationStore(ss);

        signedData = CMSSignedData.replaceSigners(signedData, sis);
        return signedData;
    }


 private static TimeStampToken getTimeStampToken() throws
Exception {
        Security.addProvider (new
org.bouncycastle.jce.provider.BouncyCastleProvider());

        PostMethod post = new PostMethod("http://My-TrustedTimeStampProvier.com");

// I'm omitting the part where I pass the user and password

        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
        //request TSA to return certificate
        reqGen.setCertReq (true); // In my case this works

        //make a TSP request this is a dummy sha1 hash (20 zero bytes)
        TimeStampRequest request =
            reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));

        byte[] enc_req = request.getEncoded();
        ByteArrayInputStream bais = new ByteArrayInputStream(enc_req);

        post.setRequestBody(bais);
        post.setRequestContentLength (enc_req.length);
        post.setRequestHeader("Content-type","application/timestamp-query");

        HttpClient http_client = new HttpClient();
        http_client.executeMethod(post);
        InputStream in = post.getResponseBodyAsStream();

        //read TSP response
        TimeStampResponse resp = new TimeStampResponse (in);

        resp.validate(request);

        TimeStampToken  tsToken = resp.getTimeStampToken();       
        return tsToken;
    }  

我可以获得一个有效的TimeStamp,我可以将它放入我的CMSSignedData对象并将其保存到将signedData.getEncoded()中的字节写入硬盘的文件中。但是当我使用third party software验证我的新的带有时间戳的文件时,该软件会告诉原始签名没问题,但时间戳与签名不对应。该软件还可以显示原始纯文本消息。

我认为问题出在这一行:

TimeStampRequest request =
    reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));

我想我必须传递摘要而不是虚拟字节数组,但我不知道哪个摘要,或者我对timeStamp有什么正确的字节。 我成功地从SignerInformation获取并验证了signedData对象。然后我尝试将来自reqGen.generate()的字节传递给mySignerInformation.getSignature()函数。时间戳验证失败。然后我通过了mySignerInformation.getSignature()的Sha1摘要,但我的时间戳验证再次失败。

RFC3161 specification说:

  

2.4.1。请求格式

     

时间戳请求如下:

     

TimeStampReq :: = SEQUENCE {版本INTEGER   {v1(1)},messageImprint MessageImprint,         - 哈希算法OID和要

的数据的哈希值      

(...)

     

messageImprint字段应该包含要存在的数据的哈希值   时间戳。散列表示为OCTET STRING。它
  length必须匹配该算法的哈希值的长度
  (例如,SHA-1为20个字节,MD5为16个字节)。

     

MessageImprint :: = SEQUENCE {           hashAlgorithm AlgorithmIdentifier,           hashedMessage OCTET STRING}

但是,如果我想在TimeStamp中使用CMSSignedData对象中的字节,它并没有告诉我在哪里或如何获取MessageImprint数据。

我是这个数字签名的新手。

1 个答案:

答案 0 :(得分:3)

你是对的,问题是你要为不正确的数据添加时间戳。其余代码对我来说似乎是正确的。

所以问题是你要为签名的哈希加时间戳。要从CMSSignedData获取签名并将其哈希;您可以使用以下代码(假设您PKCS7中只有一位签名者并且您正在使用SHA1哈希算法):

CMSSignedData signedData = ...
// get the signers of your CMSSignedData signedData
Collection ss = signedData.getSignerInfos().getSigners();
SignerInformation si = (SignerInformation) ss.iterator().next();
// hash the signature
byte[] signDigest = MessageDigest
      .getInstance(TSPAlgorithms.SHA1, new BouncyCastleProvider())
      .digest(si.getSignature()); // since you're adding the bc provider with Security.addProvider you can use "BC" instead of passing the new BouncyCastleProvider() 
TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
// generate the TSRequest
TimeStampRequest request =
            reqGen.generate(TSPAlgorithms.SHA1, signDigest, BigInteger.valueOf(100));
...

希望这有帮助,