JDK-5039967 : Repeated authentication for each jar with multi-jar applet/client authentication
  • Type: Bug
  • Component: deploy
  • Sub-Component: plugin
  • Affected Version: 5.0
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000,windows_xp
  • CPU: x86
  • Submitted: 2004-04-29
  • Updated: 2017-05-16
  • Resolved: 2004-06-22
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availabitlity Release.

To download the current JDK release, click here.
Other
5.0 b57Fixed
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Description
Name: gm110360			Date: 04/29/2004


FULL PRODUCT VERSION :
java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b32c)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b32c, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195] for client and server machines.

EXTRA RELEVANT SYSTEM CONFIGURATION :
Web server must have "Client certificate required" option set.
Using MS Internet Explorer 6.0.2800.1106 (SP1). Other IE 6.0 also used.

A DESCRIPTION OF THE PROBLEM :
An applet that uses an HttpURLConnection to send a request to a server causes a popup dialog to appear at the browser for every request sent. The dialog asks the user to select a user certificate to present to the server. This would be OK for the first request, but EVERY request triggers this dialog. Since there may be hundreds of HTTP requests during a session, this behaviour makes the applet virtually unuseable.

The applet code gets a HttpURLConnection object for each request using URL.openConnection() and its "Keep-Alive" header property is set, however the objects do not appear to be cached and reused. This may be related to bug #4814794 which describes a similar problem for JRE 1.4.x.

Other pages not using an applet (html, jsp, etc.) work properly in that the browser does not repeatedly popup the dialog for every request. The same SSL session is shared between the applet and JSP pages.

The effect of this problem is that the JRE 1.5 plugin cannot be used in a secure web environment where the applet makes HTTP requests to a server that requires client certificates.

The older 1.3.1x JRE plugin did not have this problem but it is not a desireable option to regress to that version for other reasons. The 1.4.x JRE plugin does not handle browser certificates at all and cannot be used.

This is a business critical issue for us.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Set your web server to "Require client certificates". This requires that a client present a user certificate to the web server otherwise connections will be denied.

Create a test applet that uses an HttpURLConnection object to send a request to the web server. Call URL.openConnection() to get the connection for each request.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
HTTP requests made by the applet should be sent to the web server without invoking the browsers Client Authentication (browser certificate list) dialog except perhaps for the first request.
ACTUAL -
Every HTTP request made by the applet causes the Client Authentication dialog to appear, requiring user input. All non-applet web page requests within the same session do not cause the dialog to popup.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
No error messages, just annoying redundant user dialog interaction.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
Create an applet with a button that invokes an HTTP request similar to the following:

      protected String cookie_;   // instance variable


      // THE REQUEST

      URLConnection con = null;
      ObjectOutputStream out = null;
      try
      {
         // Get connection to server
         URL url = new URL("server-that-requires-client-certificates/");
         con = url.openConnection();
         con.setDoInput(true);
         con.setDoOutput(true);
         con.setUseCaches(false);
         con.setRequestProperty(Headers.CONNECTION_HDR, "Keep-Alive");

         // If we saved a cookie previously obtained from the
         // server, add it to this request to maintain the session.
         if (cookie_ != null)
         {
            // Send session cookie back to server
            con.setRequestProperty(Headers.COOKIE_REQUEST_HDR, cookie_);
         }

         // Send a dummy test object as the request
         HashMap map = new HashMap();
         map.put("test_object", map);

         con.setRequestProperty(Headers.CONTENT_TYPE_HDR, ContentTypes.CONTENT_JAVA);
         out = new ObjectOutputStream(con.getOutputStream());

         // Send test object as HTTP POST to servlet
         out.writeObject(map);
         out.flush();

      catch (Exception e)
      {
         System.err.println("Error in HTTP request: " + e);
      }
      finally
      {
         try { if (out != null) out.close(); } catch (IOException e) {}
      }


      // THE REPLY -- probably will not get here, not important for test

      Object result = null;
      ObjectInputStream in = null;
      try
      {
         // Create an object input stream
         in = new ObjectInputStream(con.getInputStream());

         // Retrieve object reply
         result = in.readObject();

         // If a cookie is sent from the server, save it
         String s = con.getHeaderField(Headers.COOKIE_RESPONSE_HDR);
         if (s != null)
         {
            cookie_ = s;
         }
      }
      catch (Exception e)
      {
         System.err.println("Error in HTTP reply: " + e);
      }
      finally
      {
         try { if (in != null) in.close(); } catch (IOException e) {}
      }


---------- END SOURCE ----------
(Incident Review ID: 260099) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger-rc FIXED IN: tiger-rc INTEGRATED IN: tiger-b57 tiger-rc
2004-06-25

EVALUATION The description is a little bit sketchy. The obvious questions are 1. what does the comments in the sample code imply: "/ THE REPLY -- probably will not get here, not important for test" does it mean that the response will never arrive and in that case inputstream will block at reading. How are you supposed to reuse this blocked connection then? 2. // Retrieve object reply result = in.readObject(); We have no idea what kind of response the server will send. will the above read in all the response? if it doesn't and then it closes the inputstream, we can only do our best to clean the stream up in order to reuse it (keep it alive). but if not all data is read and some data is not available yet. then we can't really clean up the socket channel without blocking, so we would close the connection instead of reusing it. ###@###.### 2004-04-30 I tested with b49. This is potential a showstoper. Run an applet with multiple jars, when caching is enabled and cache is clean, downloading each jar will prompt a client authentication dialog box. Wehn caching is disabled, still prompt two client authentication dialog boxes. ###@###.### 2004-05-04 Ok. there should 2 two separate issues, and the showstopper should apply to the second issue. 1. Current implementation of keep-alive require user code to clean up the pending data in the connection channel before the connection can return to reuse pool. java.net should at least provide an explicit way to make a connection resuable, instead of asking user code to do so. 2. By reading through jar input stream till JarInputStream.nextEntry() == null does not clean up the channel. In other word, plugin has to do further clean up during download/cache jar file. Combine with newly filed #5045269 to cause this showstopper. ###@###.### 2004-05-11 Filed #5045306 to track #1. Change Synopsis to reflect plugin issue. ###@###.### 2004-05-11 There are two more issues that comes up in the test case: 1. When the application encounters a 404 response, it will ignore the IOException and then does a retry. In this case, the underlying tcp connection won't be reused (kept-alive) because the response body is still there to be consumed, so the socket channel is not clean and ready to be reused. What the application needs to do is to call httpurlconnection.getErrorStream() after catching the IOException and read the response body then close the stream. However, this won't help existing applications. So to address this problem, we (Java Networking) will introduce a workaround this problem by buffering the response body (up to a certain amount and within a time limit) if the response is >=400, thus freeing up the underlying socket connection for reuse. See details in the ccc request with the same bug number. 2. Another problem found in debugging the test case is related to zero length response body. When no response body is present, there is logic in HttpURLConnection to create an EmptyInputStream and put the underlying socket connection into the keep-alive cache to be reused. However, under some circumstances (when content-length was not explicitly set to 0 and ProgressMonitor is turned on) we could wrap a MeteredStream around a BufferedInputStream, which wrapped around a socketinputstream. When the inputstream is to be garbage collected, MeteredStream.finalize method will actually close the underlying socket. This could happen while the underlying socket connection is being reused. The fix is not to wrap a MeteredStream if there is no response body. In summary, we (Java Networking) will fix these two problems in Tiger-rc. ###@###.### 2004-05-18
2004-05-18