8157404: Unable to read certain PKCS12 keystores from SequenceInputStream
Reviewed-by: xuelei
--- a/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java Tue Apr 02 00:23:31 2019 +0800
+++ b/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java Tue Apr 02 10:17:30 2019 +0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,9 @@
package sun.security.util;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* A package private utility class to convert indefinite length DER
@@ -143,6 +145,10 @@
/**
* Parse the length and if it is an indefinite length then add
* the current position to the <code>ndefsList</code> vector.
+ *
+ * @return the length of definite length data next, or -1 if there is
+ * not enough bytes to determine it
+ * @throws IOException if invalid data is read
*/
private int parseLength() throws IOException {
int curLen = 0;
@@ -160,7 +166,7 @@
throw new IOException("Too much data");
}
if ((dataSize - dataPos) < (lenByte + 1)) {
- throw new IOException("Too little data");
+ return -1;
}
for (int i = 0; i < lenByte; i++) {
curLen = (curLen << 8) + (data[dataPos++] & 0xff);
@@ -314,10 +320,10 @@
* @param indefData the byte array holding the indefinite
* length encoding.
* @return the byte array containing the definite length
- * DER encoding.
+ * DER encoding, or null if there is not enough data.
* @exception IOException on parsing or re-writing errors.
*/
- byte[] convert(byte[] indefData) throws IOException {
+ byte[] convertBytes(byte[] indefData) throws IOException {
data = indefData;
dataPos=0; index=0;
dataSize = data.length;
@@ -328,6 +334,9 @@
while (dataPos < dataSize) {
parseTag();
len = parseLength();
+ if (len < 0) {
+ return null;
+ }
parseValue(len);
if (unresolved == 0) {
unused = dataSize - dataPos;
@@ -337,7 +346,7 @@
}
if (unresolved != 0) {
- throw new IOException("not all indef len BER resolved");
+ return null;
}
newData = new byte[dataSize + numOfTotalLenBytes + unused];
@@ -354,4 +363,48 @@
return newData;
}
+
+ /**
+ * Read the input stream into a DER byte array. If an indef len BER is
+ * not resolved this method will try to read more data until EOF is reached.
+ * This may block.
+ *
+ * @param in the input stream with tag and lenByte already read
+ * @param lenByte the length of the length field to remember
+ * @param tag the tag to remember
+ * @return a DER byte array
+ * @throws IOException if not all indef len BER
+ * can be resolved or another I/O error happens
+ */
+ public static byte[] convertStream(InputStream in, byte lenByte, byte tag)
+ throws IOException {
+ int offset = 2; // for tag and length bytes
+ int readLen = in.available();
+ byte[] indefData = new byte[readLen + offset];
+ indefData[0] = tag;
+ indefData[1] = lenByte;
+ while (true) {
+ int bytesRead = in.readNBytes(indefData, offset, readLen);
+ if (bytesRead != readLen) {
+ readLen = bytesRead;
+ indefData = Arrays.copyOf(indefData, offset + bytesRead);
+ }
+ DerIndefLenConverter derIn = new DerIndefLenConverter();
+ byte[] result = derIn.convertBytes(indefData);
+ if (result == null) {
+ int next = in.read(); // This could block, but we need more
+ if (next == -1) {
+ throw new IOException("not all indef len BER resolved");
+ }
+ int more = in.available();
+ // expand array to include next and more
+ indefData = Arrays.copyOf(indefData, offset + readLen + 1 + more);
+ indefData[offset + readLen] = (byte)next;
+ offset = offset + readLen + 1;
+ readLen = more;
+ } else {
+ return result;
+ }
+ }
+ }
}
--- a/src/java.base/share/classes/sun/security/util/DerInputStream.java Tue Apr 02 00:23:31 2019 +0800
+++ b/src/java.base/share/classes/sun/security/util/DerInputStream.java Tue Apr 02 10:17:30 2019 +0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,11 +27,9 @@
import java.io.InputStream;
import java.io.IOException;
-import java.io.EOFException;
import java.util.Date;
import java.util.Vector;
import java.math.BigInteger;
-import java.io.DataInputStream;
/**
* A DER input stream, used for parsing ASN.1 DER-encoded data such as
@@ -130,7 +128,12 @@
System.arraycopy(data, offset, inData, 0, len);
DerIndefLenConverter derIn = new DerIndefLenConverter();
- buffer = new DerInputBuffer(derIn.convert(inData), allowBER);
+ byte[] result = derIn.convertBytes(inData);
+ if (result == null) {
+ throw new IOException("not all indef len BER resolved");
+ } else {
+ buffer = new DerInputBuffer(result, allowBER);
+ }
}
} else {
buffer = new DerInputBuffer(data, offset, len, allowBER);
@@ -389,16 +392,9 @@
if (len == -1) {
// indefinite length encoding found
- int readLen = buffer.available();
- int offset = 2; // for tag and length bytes
- byte[] indefData = new byte[readLen + offset];
- indefData[0] = tag;
- indefData[1] = lenByte;
- DataInputStream dis = new DataInputStream(buffer);
- dis.readFully(indefData, offset, readLen);
- dis.close();
- DerIndefLenConverter derIn = new DerIndefLenConverter();
- buffer = new DerInputBuffer(derIn.convert(indefData), buffer.allowBER);
+ buffer = new DerInputBuffer(
+ DerIndefLenConverter.convertStream(buffer, lenByte, tag),
+ buffer.allowBER);
if (tag != buffer.read())
throw new IOException("Indefinite length encoding" +
--- a/src/java.base/share/classes/sun/security/util/DerValue.java Tue Apr 02 00:23:31 2019 +0800
+++ b/src/java.base/share/classes/sun/security/util/DerValue.java Tue Apr 02 10:17:30 2019 +0800
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -257,16 +257,9 @@
length = DerInputStream.getLength(lenByte, in);
if (length == -1) { // indefinite length encoding found
DerInputBuffer inbuf = in.dup();
- int readLen = inbuf.available();
- int offset = 2; // for tag and length bytes
- byte[] indefData = new byte[readLen + offset];
- indefData[0] = tag;
- indefData[1] = lenByte;
- DataInputStream dis = new DataInputStream(inbuf);
- dis.readFully(indefData, offset, readLen);
- dis.close();
- DerIndefLenConverter derIn = new DerIndefLenConverter();
- inbuf = new DerInputBuffer(derIn.convert(indefData), in.allowBER);
+ inbuf = new DerInputBuffer(
+ DerIndefLenConverter.convertStream(inbuf, lenByte, tag),
+ in.allowBER);
if (tag != inbuf.read())
throw new IOException
("Indefinite length encoding not supported");
@@ -277,7 +270,7 @@
// indefinite form is encoded by sending a length field with a
// length of 0. - i.e. [1000|0000].
// the object is ended by sending two zero bytes.
- in.skip(length + offset);
+ in.skip(length + 2);
} else {
buffer = in.dup();
@@ -389,16 +382,8 @@
byte lenByte = (byte)in.read();
length = DerInputStream.getLength(lenByte, in);
if (length == -1) { // indefinite length encoding found
- int readLen = in.available();
- int offset = 2; // for tag and length bytes
- byte[] indefData = new byte[readLen + offset];
- indefData[0] = tag;
- indefData[1] = lenByte;
- DataInputStream dis = new DataInputStream(in);
- dis.readFully(indefData, offset, readLen);
- dis.close();
- DerIndefLenConverter derIn = new DerIndefLenConverter();
- in = new ByteArrayInputStream(derIn.convert(indefData));
+ in = new ByteArrayInputStream(
+ DerIndefLenConverter.convertStream(in, lenByte, tag));
if (tag != in.read())
throw new IOException
("Indefinite length encoding not supported");