JDK-8136534 : Loading JKS keystore using non-null InputStream results in closed stream
  • Type: Bug
  • Component: security-libs
  • Sub-Component: java.security
  • Affected Version: 8u60,9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2008
  • CPU: x86
  • Submitted: 2015-08-28
  • Updated: 2017-12-21
  • Resolved: 2015-09-30
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 Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8 JDK 9 Other
7u171Fixed 8u72Fixed 9Fixed openjdk7uFixed
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) Client VM (build 25.60-b23, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.3.9600]

EXTRA RELEVANT SYSTEM CONFIGURATION :
This issue relates to the following bug fix that was added in JRE 8 u60:
http://bugs.java.com/view_bug.do?bug_id=8062552

A DESCRIPTION OF THE PROBLEM :
Following a call to KeyStore.load() with non-null InputStream and password, the input stream appears to be closed, when using JRE 8 u60 with a default value for security property "keystore.type.compat" (true).

Using previous versions of JRE (e.g. u51) or with "keystore.type.compat" set to "false", the input stream remains open.

I haven't seen anything in documentation for https://docs.oracle.com/javase/8/docs/api/java/security/KeyStore.html#load-java.io.InputStream-char:A- that talks about JRE closing the stream, I think that should be left to the caller to manage.

REGRESSION.  Last worked in version 8u51

ADDITIONAL REGRESSION INFORMATION: 
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) Client VM (build 25.60-b23, mixed mode)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create an instance of JKS keystore (ks1)
2. Persist it on file system or in memory
3. Create a new instance of JKS keystore (ks2)
4. Open the input stream pointing at persisted keystore from step 2
5. Load ks2, using the input stream from 4
6. Attempt to access the input stream after the loading has completed (by performing a call to either .reset, .available, etc)

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expectation is that the stream can continue being used by the caller (e.g. in my application the stream was pointing to a JarInputStream, and the code was attempting to iterate through the Jar entries, processing some of them as keystores)
ACTUAL -
The actual result was IOException: Stream is closed being thrown when trying to use the stream that was passed to KeyStore.load.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.io.IOException: Stream Closed
	at java.io.FileInputStream.available(Native Method)

or

java.io.IOException: Stream closed
	at java.util.zip.ZipInputStream.ensureOpen(Unknown Source) ~[na:1.8.0_60]
	at java.util.zip.ZipInputStream.getNextEntry(Unknown Source) ~[na:1.8.0_60]
	at java.util.jar.JarInputStream.getNextEntry(Unknown Source) ~[na:1.8.0_60]
	at java.util.jar.JarInputStream.getNextJarEntry(Unknown Source) ~[na:1.8.0_60]



REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class Foo {

	public static void main(String[] args) {
		try 
		{
			KeyStore keystore1 = KeyStore.getInstance("JKS");
			keystore1.load(null,  null);
			File temp = File.createTempFile("temp-file-name", ".tmp");
			FileOutputStream fos = new FileOutputStream(temp);
			keystore1.store(fos, "password".toCharArray());
			fos.close();
			
			FileInputStream fis = new FileInputStream(temp);
	    	        KeyStore keystore2 = KeyStore.getInstance("JKS");
	    	        keystore2.load(fis, "password".toCharArray());
	    	        fis.available(); // this throws IOException: stream closed since jre8u60
	    	        fis.close();
		}
		catch (Exception e)
		{
			System.out.println(e);
		}
	}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Temporary workaround seems to be invoking:

Security.setProperty("keystore.type.compat", "false");

on application/applet initialization.


Comments
Attached TestCase gave the following results on Windows 7 OS : JDK 8u51 - Pass JDK 8u60 - Fail JDK 8u66 - Fail JDK 9eab78 -Fail
15-09-2015