Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8157404

Unable to read certain PKCS12 keystores from SequenceInputStream

    XMLWordPrintable

Details

    • b15
    • generic
    • generic
    • Verified

    Backports

      Description

        FULL PRODUCT VERSION :


        A DESCRIPTION OF THE PROBLEM :
        When loading a PKCS12 file (that has a big undefined length DER value in it), i noticed that the loading succeeds when i load it from file directly, but fails when loaded though a servlet:

        Exception in thread "main" java.io.IOException: not all indef len BER resolved
        at sun.security.util.DerIndefLenConverter.convert(DerIndefLenConverter.java:340)
        at sun.security.util.DerValue.init(DerValue.java:376)
        at sun.security.util.DerValue.<init>(DerValue.java:320)
        at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:1914)
        at java.security.KeyStore.load(KeyStore.java:1445)

        When loading through a servlet, the container is using a SequenceInputStream (first x bytes are in memory, rest on disk).

        Root cause analysis:

        When loading a PKCS12 that has an 'indefinite length' value in it, the decoder assumes that stream.available represents the complete 'indefinite.length' section. However, this is not always the case. When using eg a SequenceInputStream, available will only return the available size of one element of the sequence, not the complete sequence.

        Relevant code in sun.security.util.DerValue:

        if(this.length == -1) {
                    int bytes = ((InputStream)in).available();
                    byte offset = 2;
                    byte[] indefData = new byte[bytes + offset];
                    indefData[0] = this.tag;
                    indefData[1] = lenByte;
                    DataInputStream dis = new DataInputStream((InputStream)in);
                    dis.readFully(indefData, offset, bytes);
                    dis.close();
                    DerIndefLenConverter derIn = new DerIndefLenConverter();
                    in = new ByteArrayInputStream(derIn.convert(indefData));
                    if(this.tag != ((InputStream)in).read()) {
                        throw new IOException("Indefinite length encoding not supported");
                    }

                    this.length = DerInputStream.getLength((InputStream)in);
                }


        REPRODUCIBILITY :
        This bug can be reproduced always.

        ---------- BEGIN SOURCE ----------
            public static void main(String[] args) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException
            {
                KeyStore p12 = KeyStore.getInstance("pkcs12");
                File file = new File("test.p12");
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                java.nio.file.Files.copy(file.toPath(), os);
                os.flush();
                byte[] bytes = os.toByteArray();
                p12.load(new ByteArrayInputStream(bytes), "motive".toCharArray());
                int split = 10;
                p12.load(createSplitStream(bytes, split), "motive".toCharArray());

            }

            private static SequenceInputStream createSplitStream(byte[] bytes, int split) {
                return new SequenceInputStream(new ByteArrayInputStream(bytes, 0, split), new ByteArrayInputStream(bytes, split, bytes.length - split));
            }

        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        Workaround is to store the stream i get from our servlet to a bytearray first and then pass a bytearraystream to keystore.load

        Attachments

          Issue Links

            Activity

              People

                weijun Weijun Wang
                webbuggrp Webbug Group
                Votes:
                0 Vote for this issue
                Watchers:
                7 Start watching this issue

                Dates

                  Created:
                  Updated:
                  Resolved: