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

标签 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;
    }  

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

我认为问题出在这一行:

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

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

RFC3161 specification说:

2.4.1. Request Format

A time-stamping request is as follows:

TimeStampReq ::= SEQUENCE { version INTEGER { v1(1) }, messageImprint MessageImprint, --a hash algorithm OID and the hash value of the data to be

(...)

The messageImprint field SHOULD contain the hash of the datum to be time-stamped. The hash is represented as an OCTET STRING. Its
length MUST match the length of the hash value for that algorithm
(e.g., 20 bytes for SHA-1 or 16 bytes for MD5).

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

但如果我想对 CMSSignedData 对象中的字节进行时间戳记,它不会告诉我从何处或如何获取 MessageImprint 数据。

我是数字签名方面的新手。

最佳答案

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

所以问题是您必须为签名的散列加上时间戳。从您的 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));
...

希望对您有所帮助,

关于java - 我必须传递来自 CMSSignedData 对象的哪些数据才能生成有效的时间戳?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28615837/

相关文章:

java - 我可以重定向工作线程以在处理过程中执行不同的工作吗?

validation - 手动使用数据注释验证和对象图

c# - CMS 签名 - 时间戳和计数器签名有什么区别

java - GSON直接访问私有(private)成员?

java - 尝试添加 Firebase UI 2.3.0 时,我不断收到 gradle 同步错误

java - Google App Engine - 始终运行的实例

c# - 如何使绑定(bind)到 Nullable<int> 属性的 DropDownListFor 接受空值?

ios - 由于 -[__NSArrayM objectForKey :]: unrecognized selector sent to instance,存档验证失败

java - 如何从 PKCS1 转换为 PKCS8

java - EST 与充气城堡