3

I am using the iTextSharp library to sign PDF documents.

When I use the standard TSAClientBouncyCastle class to get time stamp from GoDaddy's server (http://tsa.starfieldtech.com), the response is an empty stream.

At the same time, it perfectly works with other RFC 3161 compliant timestamp servers. Also, when I use Microsoft's signtool.exe to sign exe file with /tr option, it works with Godaddy's server.

So I wonder what is wrong with my attempt to get a timestamp programmatically using the iTextSharp library.

Update

I have used this sample:

http://sourceforge.net/p/itextsharp/code/HEAD/tree/tutorial/signatures/chapter2/C2_01_SignHelloWorld/C2_01_SignHelloWorld.cs

With only modification that I've specified a TSAClient with constructor that takes an URL.

That means that instead of calling:

MakeSignature.SignDetached(appearance, pks, chain, null, null, null, 0, subfilter);

I use:

ITSAClient tsaClient = new TSAClientBouncyCastle("http://tsa.starfieldtech.com/");

MakeSignature.SignDetached(appearance, pks, chain, null, null, tsaClient, 0, subfilter);

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • I just tested iText-signing with timestamping using that TSA and it worked fine. Thus, your issues are beyond what you have yet told us. – mkl Feb 25 '15 at 11:42
  • Which iTextSharp version do you use? I'll have a look tomorrow in office. – mkl Feb 25 '15 at 13:11
  • @mkl, I am using iTextSharp 5.5.5.0 – Vladimir Dubrov Feb 25 '15 at 20:14
  • Concerning your edit: I used the full URL as argument of the `TSAClientBouncyCastle` constructor but you seem to have dropped the protocol part. – mkl Feb 25 '15 at 20:30
  • @mkl, that was an error while typing from memory. Actually, I specified fully the URL including leading "http://". – Vladimir Dubrov Feb 26 '15 at 11:36
  • I'll continue looking into this tomorrow. I had to fight with a HTTP proxy first which kept me from seeing anything interesting. Currently I, too, get an empty response from the C# version while I get a proper time stamp from the Java version. The request objects look similar enough. I assume it may be a matter of HTTP headers. – mkl Feb 26 '15 at 16:31

1 Answers1

3

When I use the standard TSAClientBouncyCastle class to get time stamp from GoDaddy's server (http://tsa.starfieldtech.com), the response is an empty stream.

I could reproduce this behavior using iTextSharp & C# but not iText & Java.

By inspecting the actual network traffic the time stamp request objects turned out to be identically built but there were minor differences in the HTTP headers used in the enveloping HTTP request.

Adjusting the request headers in TSAClientBouncyCastle.GetTSAResponse one by one, the User-Agent header proved to be the culprit:

  • The .Net HttpWebRequest by default does not seem to add such a header but
  • the Java HttpURLConnection by default adds such a header containing the Java version as value, e.g. "Java/1.8.0_20".

After adding such a header explicitly in TSAClientBouncyCastle.GetTSAResponse, e.g. like this:

/**
 * Get timestamp token - communications layer
 * @return - byte[] - TSA response, raw bytes (RFC 3161 encoded)
 */
protected internal virtual byte[] GetTSAResponse(byte[] requestBytes) {
    HttpWebRequest con = (HttpWebRequest)WebRequest.Create(tsaURL);
    // Additional User-Agent header to make http://tsa.starfieldtech.com happy
    con.UserAgent = "iTextSharp";
    con.ContentLength = requestBytes.Length;
    con.ContentType = "application/timestamp-query";
    con.Method = "POST";

the time stamp server returns a proper time stamp response.

As the User-Agent header is specified as recommended but not required, this behavior of the time stamp server in focus is quite questionable.


Actually I had to fight with a different issue first: I have to use a HTTP proxy here, and the proxy always interfered with the iTextSharp/C# time stamp requests (but again not with the iText/Java time stamp requests) returning a

System.Net.WebException : The remote server returned an error: (417) Expectation Failed.
at System.Net.HttpWebRequest.GetResponse()

Restricting the HTTP protocol version to 1.0

    con.ProtocolVersion = Version.Parse("1.0");

solved this problem.


(@BrunoLowagie, @PauloSoares: It shouldn't hurt to add a User-Agent header in iTextSharp but I doubt generally restricting HTTP to 1.0 is a good idea.)

mkl
  • 90,588
  • 15
  • 125
  • 265
  • 1
    Also, it would be informative to explicitly throw an exception with appropriate message in the TSAClientBouncyCastle class when server returns a zero-length stream as a response. Currently, it crashes with an obscure NullReferenceException downwards the code. – Vladimir Dubrov Feb 27 '15 at 10:44
  • 1
    ;) well, I assume the original programmer upon receiving an OK from the time stamp server (the answer here is a 200 all-is-well) did not imagine an empty content was possible. But you are right, a certain amount of checks of data received from external services is appropriate. – mkl Feb 27 '15 at 10:52