I am signing PDF files with a digital signature. I am using SyncFusion as a PDF library and GlobalSign as a certificate provider/authority.
When signing documents with an external party, implementing the IPdfExternalSigner interface, in the "Sign" method, I am supposed to give out a byte array named "timeStampResponse". My guess is, the response from a trusted timestamp server.
I have called the external server of my signing authority and received a "Base64 encoded DER representation of timestamp token according to RFC3161. It includes the TSA signing certificate in SignedData.CertificatesSet."
I have parsed this token's bytes into an Rfc3161TimestampToken, so I am pretty sure the token itself is correct and the bytes contain what the authority says they do.
But no matter what I put into the requested array of the interface, no error or warning of any kind appears, but when I open the resulting signed PDF in Adobe, it always says
the signature includes an embedded timestamp, but it could not be verified
The rest of the signhing process works perfectly, the document is signed, has green checkmarks, is LTV, that's all fine. Just the timestamp is a problem.
I have tried passing the token's bytes as they come from the authority (the DER representation).
I have tried passing the bytes from rfc3161TimestampToken.AsSignedCms().Encode()
.
But nothing has changed.
Given the complete lack of any documentation for this field, I'm lost.
Does anyone know what I need to pass to this byte array so I can see a verified timestamp?
(And yes, I opened a support ticket, but I have never dealt with them before, I have no idea how fast that will be or how competent the answer will be. I hope someone here did that before or has a hint what I might do wrong.)
Since someone unfamiliar with the problem will just click close because there is no code, here is my code... although slightly irrelevant to the problem:
public byte[] Sign(byte[] message, out byte[] timeStampResponse)
{
var sha256MessageDigest = SHA256.HashData(message);
var hexEncodedSha256MessageDigest = Convert.ToHexString(sha256MessageDigest);
var globalSignTimeStampToken = // call their server with hexEncodedSha256MessageDigest and receive token
// token is supposed to be Base64 encoded DER representation
// of timestamp token according to RFC3161. It includes the
// TSA signing certificate in SignedData.CertificatesSet.
var timeStampTokenBytes = Convert.FromBase64String(globalSignTimeStampToken);
if (Rfc3161TimestampToken.TryDecode(timeStampTokenBytes, out var rfc3161TimestampToken, out var bytesConsumed))
{
timeStampResponse = rfc3161TimestampToken.AsSignedCms().Encode();
}
else
{
timeStampResponse = [];
}
var hexEncodedSignatureValue = // call their server with hexEncodedSha256MessageDigest and receive signature
return Convert.FromHexString(hexEncodedSignatureValue);
}
After looking online again (for the tenth time I guess) I read the signature includes en embedded timeStamp but it could not be verified and called VerifySignatureForData and VerifySignatureForHash on my token and both came back true
and with the expected certificate.
The specification for digital signatures requires to embed the timestamp token in the digital signature. I have seen timestamp servers that return only the token, others return full timestamp response (as in RFC 3161). Syncfusion probably expects the full timestamp response, as indicated by the parameter name.
The second problem is that the certificate used by GlobalSign to sign the timestamps is not trusted by default in Acrobat. When we tried this GlobalSign timestamp server http://timestamp.globalsign.com/tsa/r6advanced1 we got the result:
This affects the validation of the timestamp.
When we tried the DigiCert time stamp server http://timestamp.digicert.com we got the result:
My recommendation is to try the DigiCert timestamp server (you will have to prepare a full timestamp request for it), and in timeStampResponse
parameter return the full response returned by DigiCert timestamp server (it returns a full timestamp response) and see how it goes.