changeset 2370:26477628f2d5

6813340: X509Factory should not depend on is.available()==0 Reviewed-by: xuelei
author weijun
date Thu, 25 Mar 2010 12:07:42 +0800
parents f8c9a5e3f5db
children 6109b166bf68
files src/share/classes/sun/security/provider/X509Factory.java src/share/classes/sun/security/tools/KeyTool.java test/java/security/cert/CertificateFactory/ReturnStream.java test/java/security/cert/CertificateFactory/SlowStream.java test/java/security/cert/CertificateFactory/slowstream.sh
diffstat 5 files changed, 413 insertions(+), 357 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/security/provider/X509Factory.java	Tue Mar 23 19:03:26 2010 -0700
+++ b/src/share/classes/sun/security/provider/X509Factory.java	Thu Mar 25 12:07:42 2010 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright 1998-2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1998-2010 Sun Microsystems, Inc.  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,6 @@
 package sun.security.provider;
 
 import java.io.*;
-import java.util.Collection;
 import java.util.*;
 import java.security.cert.*;
 import sun.security.x509.X509CertImpl;
@@ -37,6 +36,7 @@
 import sun.security.util.DerValue;
 import sun.security.util.Cache;
 import sun.misc.BASE64Decoder;
+import sun.security.pkcs.ParsingException;
 
 /**
  * This class defines a certificate factory for X.509 v3 certificates &
@@ -62,10 +62,6 @@
     public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
     public static final String END_CERT = "-----END CERTIFICATE-----";
 
-    private static final int defaultExpectedLineLength = 80;
-
-    private static final char[] endBoundary = "-----END".toCharArray();
-
     private static final int ENC_MAX_LENGTH = 4096 * 1024; // 4 MB MAX
 
     private static final Cache certCache = Cache.newSoftMemoryCache(750);
@@ -92,13 +88,7 @@
             throw new CertificateException("Missing input stream");
         }
         try {
-            if (is.markSupported() == false) {
-                // consume the entire input stream
-                byte[] totalBytes;
-                totalBytes = getTotalBytes(new BufferedInputStream(is));
-                is = new ByteArrayInputStream(totalBytes);
-            }
-            byte[] encoding = readSequence(is);
+            byte[] encoding = readOneBlock(is);
             if (encoding != null) {
                 X509CertImpl cert = (X509CertImpl)getFromCache(certCache, encoding);
                 if (cert != null) {
@@ -108,19 +98,7 @@
                 addToCache(certCache, cert.getEncodedInternal(), cert);
                 return cert;
             } else {
-                X509CertImpl cert;
-                // determine if binary or Base64 encoding. If Base64 encoding,
-                // the certificate must be bounded at the beginning by
-                // "-----BEGIN".
-                if (isBase64(is)) {
-                    // Base64
-                    byte[] data = base64_to_binary(is);
-                    cert = new X509CertImpl(data);
-                } else {
-                    // binary
-                    cert = new X509CertImpl(new DerValue(is));
-                }
-                return intern(cert);
+                throw new IOException("Empty input");
             }
         } catch (IOException ioe) {
             throw (CertificateException)new CertificateException
@@ -129,73 +107,21 @@
     }
 
     /**
-     * Read a DER SEQUENCE from an InputStream and return the encoding.
-     * If data does not represent a SEQUENCE, it uses indefinite length
-     * encoding, or is longer than ENC_MAX_LENGTH, the stream is reset
-     * and this method returns null.
-     */
-    private static byte[] readSequence(InputStream in) throws IOException {
-        in.mark(ENC_MAX_LENGTH);
-        byte[] b = new byte[4];
-        int i = readFully(in, b, 0, b.length);
-        if ((i != b.length) || (b[0] != 0x30)) { // first byte must be SEQUENCE
-            in.reset();
-            return null;
-        }
-        i = b[1] & 0xff;
-        int totalLength;
-        if (i < 0x80) {
-            int valueLength = i;
-            totalLength = valueLength + 2;
-        } else if (i == 0x81) {
-            int valueLength = b[2] & 0xff;
-            totalLength = valueLength + 3;
-        } else if (i == 0x82) {
-            int valueLength = ((b[2] & 0xff) << 8) | (b[3] & 0xff);
-            totalLength = valueLength + 4;
-        } else { // ignore longer length forms
-            in.reset();
-            return null;
-        }
-        if (totalLength > ENC_MAX_LENGTH) {
-            in.reset();
-            return null;
-        }
-        byte[] encoding = new byte[totalLength];
-        if( totalLength < b.length ) {
-            in.reset();
-            i = readFully(in, encoding, 0, totalLength);
-            if( i != totalLength ) {
-                in.reset();
-                return null;
-            }
-        } else {
-            System.arraycopy(b, 0, encoding, 0, b.length);
-            int n = totalLength - b.length;
-            i = readFully(in, encoding, b.length, n);
-            if (i != n) {
-                in.reset();
-                return null;
-            }
-        }
-        return encoding;
-    }
-
-    /**
      * Read from the stream until length bytes have been read or EOF has
      * been reached. Return the number of bytes actually read.
      */
-    private static int readFully(InputStream in, byte[] buffer, int offset,
+    private static int readFully(InputStream in, ByteArrayOutputStream bout,
             int length) throws IOException {
         int read = 0;
+        byte[] buffer = new byte[2048];
         while (length > 0) {
-            int n = in.read(buffer, offset, length);
+            int n = in.read(buffer, 0, length<2048?length:2048);
             if (n <= 0) {
                 break;
             }
+            bout.write(buffer, 0, n);
             read += n;
             length -= n;
-            offset += n;
         }
         return read;
     }
@@ -309,21 +235,11 @@
             throw new CertificateException("Missing input stream");
         }
         try {
-            if (inStream.markSupported() == false) {
-                // consume the entire input stream
-                byte[] totalBytes;
-                totalBytes = getTotalBytes(new BufferedInputStream(inStream));
-                inStream = new ByteArrayInputStream(totalBytes);
-            }
-            // determine if binary or Base64 encoding. If Base64 encoding,
-            // each certificate must be bounded at the beginning by
-            // "-----BEGIN".
-            if (isBase64(inStream)) {
-                // Base64
-                byte[] data = base64_to_binary(inStream);
-                return new X509CertPath(new ByteArrayInputStream(data));
+            byte[] encoding = readOneBlock(inStream);
+            if (encoding != null) {
+                return new X509CertPath(new ByteArrayInputStream(encoding));
             } else {
-                return new X509CertPath(inStream);
+                throw new IOException("Empty input");
             }
         } catch (IOException ioe) {
             throw new CertificateException(ioe.getMessage());
@@ -350,21 +266,11 @@
             throw new CertificateException("Missing input stream");
         }
         try {
-            if (inStream.markSupported() == false) {
-                // consume the entire input stream
-                byte[] totalBytes;
-                totalBytes = getTotalBytes(new BufferedInputStream(inStream));
-                inStream = new ByteArrayInputStream(totalBytes);
-            }
-            // determine if binary or Base64 encoding. If Base64 encoding,
-            // each certificate must be bounded at the beginning by
-            // "-----BEGIN".
-            if (isBase64(inStream)) {
-                // Base64
-                byte[] data = base64_to_binary(inStream);
+            byte[] data = readOneBlock(inStream);
+            if (data != null) {
                 return new X509CertPath(new ByteArrayInputStream(data), encoding);
             } else {
-                return(new X509CertPath(inStream, encoding));
+                throw new IOException("Empty input");
             }
         } catch (IOException ioe) {
             throw new CertificateException(ioe.getMessage());
@@ -426,11 +332,6 @@
             throw new CertificateException("Missing input stream");
         }
         try {
-            if (is.markSupported() == false) {
-                // consume the entire input stream
-                is = new ByteArrayInputStream
-                     (getTotalBytes(new BufferedInputStream(is)));
-            }
             return parseX509orPKCS7Cert(is);
         } catch (IOException ioe) {
             throw new CertificateException(ioe);
@@ -458,13 +359,7 @@
             throw new CRLException("Missing input stream");
         }
         try {
-            if (is.markSupported() == false) {
-                // consume the entire input stream
-                byte[] totalBytes;
-                totalBytes = getTotalBytes(new BufferedInputStream(is));
-                is = new ByteArrayInputStream(totalBytes);
-            }
-            byte[] encoding = readSequence(is);
+            byte[] encoding = readOneBlock(is);
             if (encoding != null) {
                 X509CRLImpl crl = (X509CRLImpl)getFromCache(crlCache, encoding);
                 if (crl != null) {
@@ -474,19 +369,7 @@
                 addToCache(crlCache, crl.getEncodedInternal(), crl);
                 return crl;
             } else {
-                X509CRLImpl crl;
-                // determine if binary or Base64 encoding. If Base64 encoding,
-                // the CRL must be bounded at the beginning by
-                // "-----BEGIN".
-                if (isBase64(is)) {
-                    // Base64
-                    byte[] data = base64_to_binary(is);
-                    crl = new X509CRLImpl(data);
-                } else {
-                    // binary
-                    crl = new X509CRLImpl(new DerValue(is));
-                }
-                return intern(crl);
+                throw new IOException("Empty input");
             }
         } catch (IOException ioe) {
             throw new CRLException(ioe.getMessage());
@@ -504,19 +387,13 @@
      *
      * @exception CRLException on parsing errors.
      */
-    public Collection<? extends java.security.cert.CRL> engineGenerateCRLs(InputStream
-is)
-        throws CRLException
+    public Collection<? extends java.security.cert.CRL> engineGenerateCRLs(
+            InputStream is) throws CRLException
     {
         if (is == null) {
             throw new CRLException("Missing input stream");
         }
         try {
-            if (is.markSupported() == false) {
-                // consume the entire input stream
-                is = new ByteArrayInputStream
-                    (getTotalBytes(new BufferedInputStream(is)));
-            }
             return parseX509orPKCS7CRL(is);
         } catch (IOException ioe) {
             throw new CRLException(ioe.getMessage());
@@ -533,42 +410,25 @@
         throws CertificateException, IOException
     {
         Collection<X509CertImpl> coll = new ArrayList<X509CertImpl>();
-        boolean first = true;
-        while (is.available() != 0) {
-            // determine if binary or Base64 encoding. If Base64 encoding,
-            // each certificate must be bounded at the beginning by
-            // "-----BEGIN".
-            InputStream is2 = is;
-            if (isBase64(is2)) {
-                // Base64
-                is2 = new ByteArrayInputStream(base64_to_binary(is2));
+        byte[] data = readOneBlock(is);
+        if (data == null) {
+            return new ArrayList<X509CertImpl>(0);
+        }
+        try {
+            PKCS7 pkcs7 = new PKCS7(data);
+            X509Certificate[] certs = pkcs7.getCertificates();
+            // certs are optional in PKCS #7
+            if (certs != null) {
+                return Arrays.asList(certs);
+            } else {
+                // no crls provided
+                return new ArrayList<X509Certificate>(0);
             }
-            if (first)
-                is2.mark(is2.available());
-            try {
-                // treat as X.509 cert
-                coll.add(intern(new X509CertImpl(new DerValue(is2))));
-            } catch (CertificateException e) {
-                Throwable cause = e.getCause();
-                // only treat as PKCS#7 if this is the first cert parsed
-                // and the root cause of the decoding failure is an IOException
-                if (first && cause != null && (cause instanceof IOException)) {
-                    // treat as PKCS#7
-                    is2.reset();
-                    PKCS7 pkcs7 = new PKCS7(is2);
-                    X509Certificate[] certs = pkcs7.getCertificates();
-                    // certs are optional in PKCS #7
-                    if (certs != null) {
-                        return Arrays.asList(certs);
-                    } else {
-                        // no certs provided
-                        return new ArrayList<X509Certificate>(0);
-                    }
-                } else {
-                    throw e;
-                }
+        } catch (ParsingException e) {
+            while (data != null) {
+                coll.add(new X509CertImpl(data));
+                data = readOneBlock(is);
             }
-            first = false;
         }
         return coll;
     }
@@ -583,162 +443,215 @@
         throws CRLException, IOException
     {
         Collection<X509CRLImpl> coll = new ArrayList<X509CRLImpl>();
-        boolean first = true;
-        while (is.available() != 0) {
-            // determine if binary or Base64 encoding. If Base64 encoding,
-            // the CRL must be bounded at the beginning by
-            // "-----BEGIN".
-            InputStream is2 = is;
-            if (isBase64(is)) {
-                // Base64
-                is2 = new ByteArrayInputStream(base64_to_binary(is2));
+        byte[] data = readOneBlock(is);
+        if (data == null) {
+            return new ArrayList<X509CRL>(0);
+        }
+        try {
+            PKCS7 pkcs7 = new PKCS7(data);
+            X509CRL[] crls = pkcs7.getCRLs();
+            // CRLs are optional in PKCS #7
+            if (crls != null) {
+                return Arrays.asList(crls);
+            } else {
+                // no crls provided
+                return new ArrayList<X509CRL>(0);
             }
-            if (first)
-                is2.mark(is2.available());
-            try {
-                // treat as X.509 CRL
-                coll.add(new X509CRLImpl(is2));
-            } catch (CRLException e) {
-                // only treat as PKCS#7 if this is the first CRL parsed
-                if (first) {
-                    is2.reset();
-                    PKCS7 pkcs7 = new PKCS7(is2);
-                    X509CRL[] crls = pkcs7.getCRLs();
-                    // CRLs are optional in PKCS #7
-                    if (crls != null) {
-                        return Arrays.asList(crls);
-                    } else {
-                        // no crls provided
-                        return new ArrayList<X509CRL>(0);
-                    }
-                }
+        } catch (ParsingException e) {
+            while (data != null) {
+                coll.add(new X509CRLImpl(data));
+                data = readOneBlock(is);
             }
-            first = false;
         }
         return coll;
     }
 
-    /*
-     * Converts a Base64-encoded X.509 certificate or X.509 CRL or PKCS#7 data
-     * to binary encoding.
-     * In all cases, the data must be bounded at the beginning by
-     * "-----BEGIN", and must be bounded at the end by "-----END".
+    /**
+     * Returns an ASN.1 SEQUENCE from a stream, which might be a BER-encoded
+     * binary block or a PEM-style BASE64-encoded ASCII data. In the latter
+     * case, it's de-BASE64'ed before return.
+     *
+     * After the reading, the input stream pointer is after the BER block, or
+     * after the newline character after the -----END SOMETHING----- line.
+     *
+     * @param is the InputStream
+     * @returns byte block or null if end of stream
+     * @throws IOException If any parsing error
      */
-    private byte[] base64_to_binary(InputStream is)
-        throws IOException
-    {
-        long len = 0; // total length of base64 encoding, including boundaries
-
-        is.mark(is.available());
-
-        BufferedInputStream bufin = new BufferedInputStream(is);
-        BufferedReader br =
-            new BufferedReader(new InputStreamReader(bufin, "ASCII"));
+    private static byte[] readOneBlock(InputStream is) throws IOException {
 
-        // First read all of the data that is found between
-        // the "-----BEGIN" and "-----END" boundaries into a buffer.
-        String temp;
-        while (true) {
-            temp=readLine(br);
-            if (temp == null) {
-                throw new IOException("Unsupported encoding");
+        // The first character of a BLOCK.
+        int c = is.read();
+        if (c == -1) {
+            return null;
+        }
+        if (c == DerValue.tag_Sequence) {
+            ByteArrayOutputStream bout = new ByteArrayOutputStream(2048);
+            bout.write(c);
+            readBERInternal(is, bout, c);
+            return bout.toByteArray();
+        } else {
+            // Read BASE64 encoded data, might skip info at the beginning
+            char[] data = new char[2048];
+            int pos = 0;
+
+            // Step 1: Read until header is found
+            int hyphen = (c=='-') ? 1: 0;   // count of consequent hyphens
+            int last = (c=='-') ? -1: c;    // the char before hyphen
+            while (true) {
+                int next = is.read();
+                if (next == -1) {
+                    // We accept useless data after the last block,
+                    // say, empty lines.
+                    return null;
+                }
+                if (next == '-') {
+                    hyphen++;
+                } else {
+                    hyphen = 0;
+                    last = next;
+                }
+                if (hyphen == 5 && (last==-1 || last=='\r' || last=='\n')) {
+                    break;
+                }
             }
-            len += temp.length();
-            if (temp.startsWith("-----BEGIN")) {
-                break;
-            }
-        }
-        StringBuffer strBuf = new StringBuffer();
-        while ((temp=readLine(br))!=null && !temp.startsWith("-----END")) {
-            strBuf.append(temp);
-        }
-        if (temp == null) {
-            throw new IOException("Unsupported encoding");
-        } else {
-            len += temp.length();
-        }
 
-        // consume only as much as was needed
-        len += strBuf.length();
-        is.reset();
-        is.skip(len);
-
-        // Now, that data is supposed to be a single X.509 certificate or
-        // X.509 CRL or PKCS#7 formatted data... Base64 encoded.
-        // Decode into binary and return the result.
-        BASE64Decoder decoder = new BASE64Decoder();
-        return decoder.decodeBuffer(strBuf.toString());
-    }
+            // Step 2: Read the rest of header, determine the line end
+            int end;
+            while (true) {
+                int next = is.read();
+                if (next == -1) {
+                    throw new IOException("Incomplete data");
+                }
+                if (next == '\n') {
+                    end = '\n';
+                    break;
+                }
+                if (next == '\r') {
+                    next = is.read();
+                    if (next == -1) {
+                        throw new IOException("Incomplete data");
+                    }
+                    if (next == '\n') {
+                        end = '\n';
+                    } else {
+                        end = '\r';
+                        data[pos++] = (char)next;
+                    }
+                    break;
+                }
+            }
 
-    /*
-     * Reads the entire input stream into a byte array.
-     */
-    private byte[] getTotalBytes(InputStream is) throws IOException {
-        byte[] buffer = new byte[8192];
-        ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
-        int n;
-        baos.reset();
-        while ((n = is.read(buffer, 0, buffer.length)) != -1) {
-            baos.write(buffer, 0, n);
-        }
-        return baos.toByteArray();
-    }
+            // Step 3: Read the data
+            while (true) {
+                int next = is.read();
+                if (next == -1) {
+                    throw new IOException("Incomplete data");
+                }
+                if (next != '-') {
+                    data[pos++] = (char)next;
+                    if (pos >= data.length) {
+                        data = Arrays.copyOf(data, data.length+1024);
+                    }
+                } else {
+                    break;
+                }
+            }
 
-    /*
-     * Determines if input is binary or Base64 encoded.
-     */
-    private boolean isBase64(InputStream is) throws IOException {
-        if (is.available() >= 1) {
-            is.mark(1);
-            int c1 = is.read();
-            is.reset();
-            if (c1 != DerValue.tag_Sequence) {
-                return true;
-            } else {
-                return false;
+            // Step 4: Consume the footer
+            while (true) {
+                int next = is.read();
+                // Add next == '\n' for maximum safety, in case endline
+                // is not consistent.
+                if (next == -1 || next == end || next == '\n') {
+                    break;
+                }
             }
-        } else {
-            return false;
+
+            BASE64Decoder decoder = new BASE64Decoder();
+            return decoder.decodeBuffer(new String(data, 0, pos));
         }
     }
 
-    /*
-     * Read a line of text.  A line is considered to be terminated by any one
-     * of a line feed ('\n'), a carriage return ('\r'), a carriage return
-     * followed immediately by a linefeed, or an end-of-certificate marker.
+    /**
+     * Read one BER data block. This method is aware of indefinite-length BER
+     * encoding and will read all of the sub-sections in a recursive way
      *
-     * @return     A String containing the contents of the line, including
-     *             any line-termination characters, or null if the end of the
-     *             stream has been reached.
+     * @param is    Read from this InputStream
+     * @param bout  Write into this OutputStream
+     * @param tag   Tag already read (-1 mean not read)
+     * @returns     The current tag, used to check EOC in indefinite-length BER
+     * @throws IOException Any parsing error
      */
-    private String readLine(BufferedReader br) throws IOException {
-        int c;
-        int i = 0;
-        boolean isMatch = true;
-        boolean matched = false;
-        StringBuffer sb = new StringBuffer(defaultExpectedLineLength);
-        do {
-            c = br.read();
-            if (isMatch && (i < endBoundary.length)) {
-                isMatch = ((char)c != endBoundary[i++]) ? false : true;
+    private static int readBERInternal(InputStream is,
+            ByteArrayOutputStream bout, int tag) throws IOException {
+
+        if (tag == -1) {        // Not read before the call, read now
+            tag = is.read();
+            if (tag == -1) {
+                throw new IOException("BER/DER tag info absent");
+            }
+            if ((tag & 0x1f) == 0x1f) {
+                throw new IOException("Multi octets tag not supported");
+            }
+            bout.write(tag);
+        }
+
+        int n = is.read();
+        if (n == -1) {
+            throw new IOException("BER/DER length info ansent");
+        }
+        bout.write(n);
+
+        int length;
+
+        if (n == 0x80) {        // Indefinite-length encoding
+            if ((tag & 0x20) != 0x20) {
+                throw new IOException(
+                        "Non constructed encoding must have definite length");
+            }
+            while (true) {
+                int subTag = readBERInternal(is, bout, -1);
+                if (subTag == 0) {   // EOC, end of indefinite-length section
+                    break;
+                }
             }
-            if (!matched)
-                matched = (isMatch && (i == endBoundary.length));
-            sb.append((char)c);
-        } while ((c != -1) && (c != '\n') && (c != '\r'));
-
-        if (!matched && c == -1) {
-            return null;
-        }
-        if (c == '\r') {
-            br.mark(1);
-            int c2 = br.read();
-            if (c2 == '\n') {
-                sb.append((char)c);
-            } else {
-                br.reset();
+        } else {
+            if (n < 0x80) {
+                length = n;
+            } else if (n == 0x81) {
+                length = is.read();
+                if (length == -1) {
+                    throw new IOException("Incomplete BER/DER length info");
+                }
+                bout.write(length);
+            } else if (n == 0x82) {
+                int highByte = is.read();
+                int lowByte = is.read();
+                if (lowByte == -1) {
+                    throw new IOException("Incomplete BER/DER length info");
+                }
+                bout.write(highByte);
+                bout.write(lowByte);
+                length = (highByte << 8) | lowByte;
+            } else if (n == 0x83) {
+                int highByte = is.read();
+                int midByte = is.read();
+                int lowByte = is.read();
+                if (lowByte == -1) {
+                    throw new IOException("Incomplete BER/DER length info");
+                }
+                bout.write(highByte);
+                bout.write(midByte);
+                bout.write(lowByte);
+                length = (highByte << 16) | (midByte << 8) | lowByte;
+            } else { // ignore longer length forms
+                throw new IOException("Invalid BER/DER data (too huge?)");
+            }
+            if (readFully(is, bout, length) != length) {
+                throw new IOException("Incomplete BER/DER data");
             }
         }
-        return sb.toString();
+        return tag;
     }
 }
--- a/src/share/classes/sun/security/tools/KeyTool.java	Tue Mar 23 19:03:26 2010 -0700
+++ b/src/share/classes/sun/security/tools/KeyTool.java	Thu Mar 25 12:07:42 2010 +0800
@@ -977,46 +977,35 @@
             if (filename != null) {
                 inStream = new FileInputStream(filename);
             }
-            // Read the full stream before feeding to X509Factory,
-            // otherwise, keytool -gencert | keytool -importcert
-            // might not work properly, since -gencert is slow
-            // and there's no data in the pipe at the beginning.
-            ByteArrayOutputStream bout = new ByteArrayOutputStream();
+            String importAlias = (alias!=null)?alias:keyAlias;
             try {
-                byte[] b = new byte[4096];
-                while (true) {
-                    int len = inStream.read(b);
-                    if (len < 0) break;
-                    bout.write(b, 0, len);
+                if (keyStore.entryInstanceOf(
+                        importAlias, KeyStore.PrivateKeyEntry.class)) {
+                    kssave = installReply(importAlias, inStream);
+                    if (kssave) {
+                        System.err.println(rb.getString
+                            ("Certificate reply was installed in keystore"));
+                    } else {
+                        System.err.println(rb.getString
+                            ("Certificate reply was not installed in keystore"));
+                    }
+                } else if (!keyStore.containsAlias(importAlias) ||
+                        keyStore.entryInstanceOf(importAlias,
+                            KeyStore.TrustedCertificateEntry.class)) {
+                    kssave = addTrustedCert(importAlias, inStream);
+                    if (kssave) {
+                        System.err.println(rb.getString
+                            ("Certificate was added to keystore"));
+                    } else {
+                        System.err.println(rb.getString
+                            ("Certificate was not added to keystore"));
+                    }
                 }
             } finally {
                 if (inStream != System.in) {
                     inStream.close();
                 }
             }
-            inStream = new ByteArrayInputStream(bout.toByteArray());
-            String importAlias = (alias!=null)?alias:keyAlias;
-            if (keyStore.entryInstanceOf(importAlias, KeyStore.PrivateKeyEntry.class)) {
-                kssave = installReply(importAlias, inStream);
-                if (kssave) {
-                    System.err.println(rb.getString
-                        ("Certificate reply was installed in keystore"));
-                } else {
-                    System.err.println(rb.getString
-                        ("Certificate reply was not installed in keystore"));
-                }
-            } else if (!keyStore.containsAlias(importAlias) ||
-                    keyStore.entryInstanceOf(importAlias,
-                        KeyStore.TrustedCertificateEntry.class)) {
-                kssave = addTrustedCert(importAlias, inStream);
-                if (kssave) {
-                    System.err.println(rb.getString
-                        ("Certificate was added to keystore"));
-                } else {
-                    System.err.println(rb.getString
-                        ("Certificate was not added to keystore"));
-                }
-            }
         } else if (command == IMPORTKEYSTORE) {
             doImportKeyStore();
             kssave = true;
@@ -2149,18 +2138,7 @@
                 inStream = new FileInputStream(filename);
             }
             try {
-                // Read the full stream before feeding to X509Factory,
-                // otherwise, keytool -gencert | keytool -printcert
-                // might not work properly, since -gencert is slow
-                // and there's no data in the pipe at the beginning.
-                ByteArrayOutputStream bout = new ByteArrayOutputStream();
-                byte[] b = new byte[4096];
-                while (true) {
-                    int len = inStream.read(b);
-                    if (len < 0) break;
-                    bout.write(b, 0, len);
-                }
-                printCertFromStream(new ByteArrayInputStream(bout.toByteArray()), out);
+                printCertFromStream(inStream, out);
             } finally {
                 if (inStream != System.in) {
                     inStream.close();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/security/cert/CertificateFactory/ReturnStream.java	Thu Mar 25 12:07:42 2010 +0800
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6813340
+ * @summary X509Factory should not depend on is.available()==0
+ */
+import java.io.*;
+import java.security.cert.*;
+
+/**
+ * Tests ol'Mac style file, end witha  single '\r'
+ */
+public class ReturnStream {
+
+    public static void main(String[] args) throws Exception {
+        FileInputStream fin = new FileInputStream(new File(new File(
+                System.getProperty("test.src", "."), "openssl"), "pem"));
+        byte[] buffer = new byte[4096];
+        int size = 0;
+        while (true) {
+            int len = fin.read(buffer, size, 4096-size);
+            if (len < 0) break;
+            size += len;
+        }
+        fin.close();
+
+        // Make a copy
+        System.arraycopy(buffer, 0, buffer, size, size);
+        size += size;
+
+        // Create a ol'Mac style file.
+        for (int i=0; i<size; i++) {
+            if (buffer[i] == '\n') buffer[i] = '\r';
+        }
+
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        if (factory.generateCertificates(
+                new ByteArrayInputStream(buffer, 0, size)).size() != 2) {
+            throw new Exception("Cert not OK");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/security/cert/CertificateFactory/SlowStream.java	Thu Mar 25 12:07:42 2010 +0800
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.io.*;
+import java.security.cert.*;
+
+class SlowStreamReader {
+
+    public static void main(String[] args) throws Exception {
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        if (factory.generateCertificates(System.in).size() != 5) {
+            throw new Exception("Not all certs read");
+        }
+    }
+}
+
+class SlowStreamWriter {
+    public static void main(String[] args) throws Exception {
+        for (int i=0; i<5; i++) {
+            FileInputStream fin = new FileInputStream(new File(new File(
+                    System.getProperty("test.src", "."), "openssl"), "pem"));
+            byte[] buffer = new byte[4096];
+            while (true) {
+                int len = fin.read(buffer);
+                if (len < 0) break;
+                System.out.write(buffer, 0, len);
+            }
+            Thread.sleep(2000);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/security/cert/CertificateFactory/slowstream.sh	Thu Mar 25 12:07:42 2010 +0800
@@ -0,0 +1,50 @@
+#
+# Copyright 2010 Sun Microsystems, Inc.  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
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+# @test
+# @bug 6813340
+# @summary X509Factory should not depend on is.available()==0
+
+if [ "${TESTSRC}" = "" ] ; then
+  TESTSRC="."
+fi
+if [ "${TESTJAVA}" = "" ] ; then
+  echo "TESTJAVA not set.  Test cannot execute."
+  echo "FAILED!!!"
+  exit 1
+fi
+
+# set platform-dependent variables
+OS=`uname -s`
+case "$OS" in
+  Windows_* )
+    FS="\\"
+    ;;
+  * )
+    FS="/"
+    ;;
+esac
+
+${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}SlowStream.java
+${TESTJAVA}${FS}bin${FS}java -Dtest.src=${TESTSRC} SlowStreamWriter | \
+        ${TESTJAVA}${FS}bin${FS}java SlowStreamReader